Does systemd still know about runlevels?

SystemD Run-Level Low-Down

Within the SystemD(aemon), runlevels are exposed as "Targets." The concept is still there, but the workflow to produce the desired result for your requirement is different.

The attached should clarify this issue.

How do I change the current runlevel?

$ systemctl isolate runlevelX.target

How do I change the default runlevel for next-boot?

# Create a symlink
$ ln -sf /usr/lib/systemd/system/multi-user.target /etc/systemd/system/default.target
  • ln -sf TARGET DESTINATION
  • -s creates symbolic link
  • -f removes the existing destination file

OR (as @centimane suggested) simply use the "blessed" systemd command:

systemctl set-default [target name].target

How do I identify the current runlevel?

$ systemctl list-units --type=target

No. As the systemd people themselves wrote twice over, once in their telinit manual and once in their runlevel manual, runlevels are "obsolete". You can forget about runlevels.

These things do not actually exist in systemd at all, outwith a few compatibility shims.

  • There are some symbolic links for target names, but these targets are never actually used by systemd proper.
    • Rather, the bootstrap process employs a default.target (and thence one or both of a graphical.target and a multi-user.target), a rescue.target, or an emergency.target. And the shutdown process involves a shutdown.target, a reboot.target, a halt.target, or a poweroff.target. No run-level targets are involved in either bootstrap or shutdown.
    • The telinit command, which one might think uses the compatibility symbolic links to map its command-line arguments, does not do that either. There's a hardwired table in the source code of the telinit program, and the numbers 2, 3, 4, and 5 as arguments to the command are hardwired to map to multi-user.target and graphical.target.
    • systemd-update-utmp also has an internal hardwired table.
  • There is no "init table" of runlevel stuff. systemd is only compatible with van Smoorenburg rc, not with van Smoorenburg init.
  • There is no "current run level" value maintained by systemd itself. Rather, the almost wholly undocumented systemd-update-utmp command operates internally in terms of the activation states of rescue.target, multi-user.target, and graphical.target.
  • systemd-sysv-generator, systemd's backwards compatibility service unit generator, merges the /etc/rc[234].d directories into just the one Wanted-By relationship to multi-user.target in generated service units. There is no actual reference to run levels in the generated service units. (There used to be, years ago, but the systemd people found that this went wrong, because they weren't being referenced anywhere else.)

If one is a user of a system that builds systemd as did Arch Linux for the questioner at "Why does `init 0` result in "Excess Arguments" on Arch install?", one does not even get the compatibility shims, and commands such as init 0 result in the "native" systemd behaviour, which is to complain that the command has been incorrectly invoked.

Further reading

  • Lennart Poettering et al.. runlevel. systemd manual pages. Freedesktop.org.
  • Lennart Poettering et al.. telinit. systemd manual pages. Freedesktop.org.
  • Lennart Poettering et al.. bootup. systemd manual pages. Freedesktop.org.
  • Lennart Poettering (2015-02-08). everywhere: remove configurability of sysv runlevel to target mapping. systemd. github.
  • https://unix.stackexchange.com/a/196014/5132
  • https://unix.stackexchange.com/a/196197/5132
  • https://unix.stackexchange.com/a/233581/5132
  • https://unix.stackexchange.com/a/211927/5132
  • https://unix.stackexchange.com/a/392612/5132
  • Jonathan de Boyne Pollard (2015). /etc/inittab is a thing of the past.. Frequently Given Answers.

Thanks very much. So, if I understood correctly:

For example:

ls -ll /usr/lib/systemd/system/runlevel*.target

Output:

/usr/lib/systemd/system/runlevel0.target -> poweroff.target
/usr/lib/systemd/system/runlevel1.target -> rescue.target
/usr/lib/systemd/system/runlevel2.target -> multi-user.target
/usr/lib/systemd/system/runlevel3.target -> multi-user.target
/usr/lib/systemd/system/runlevel4.target -> multi-user.target
/usr/lib/systemd/system/runlevel5.target -> graphical.target
/usr/lib/systemd/system/runlevel6.target -> reboot.target

So as you can see, the concept of runlevels do exists, but it is quite obsolete due to the fact that the runlevel.target files are not actually “real” files but soft-links to the new, modern, better named files scheme which systemd likes to call them “targets”.

So, if you would like to do sth like telinit 5 it would be like this: systemctl isolate runlevel5.target which is identical with: systemctl isolate graphical.target (recommended in my opinion).

Just in case you are interested to know all the possible targets:

ls /usr/lib/systemd/system/*.target