In this post, we’ll look at how runlevels work in two major init systems, systemd and OpenRC. If you’re interested in trying out
systemd, I’d suggest using an Arch Linux Live ISO. For those interested in trying OpenRC, check out Funtoo. Both of these will work great in your favorite virtualization solution.
OpenRC uses runlevels in very much the same way as sysvinit (or BSD init). At any given time the system is in one of the defined runlevels, there are three internal runlevels and four user defined runlevels.
sysinit: Initialize the system.
shutdown: Power off the system.
reboot: Rebooting the system.
boot: Starts all system-necessary services for other runlevels.
default: Used for day-to-day-operations
nonetwork: Used when no network connectivity is required.
single: Single-user mode.
A system transitions between runlevels like so:
sysinit -> boot -> default -> shutdown
The services controlled by a particular runlevel are controlled by
/etc/inittab and via the
On a modern Funtoo LXC Container, your runlevels might look like this:
± # rc-update -v bootmisc | boot busybox-ntpd | busybox-watchdog | consolefont | dbus | devfs | sysinit dhcpcd | dmesg | sysinit fsck | boot git-daemon | gpm | hostname | boot hwclock | boot iptables | default keymaps | boot killprocs | shutdown kmod-static-nodes | sysinit local | default localmount | boot modules | boot mount-ro | shutdown mtab | boot murmur | netif.eth0 | default netif.lo | sysinit netif.tmpl | netmount | default nginx | default numlock | openvpn | default pciparm | postfix | procfs | boot pydoc-2.7 | pydoc-3.3 | pydoc-3.4 | redis | root | boot rsyncd | savecache | shutdown sshd | default swap | boot swapfiles | boot swclock | sysctl | boot sysfs | sysinit termencoding | boot tmpfiles.dev | sysinit tmpfiles.setup | boot udev | sysinit udev-mount | sysinit udev-postmount | boot urandom | boot
Services can be added to runlevels with:
rc-update add <service> <runlevel>
Or removed with:
rc-update del <service> <runlevel>
Changing between runlevels is done via the
rc <runlevel> command, but most users will never need to do this as runlevels are (mostly) managed by the system.
In the case where a user does want to change runlevels in this way, it is often such that they are utilizing Stacked Runlevels.
For example, if a user wanted to differentiate which services were running on their laptop when it was powered or on battery, they could create two separate runlevels stacked atop
default. While plugged in, the laptop could be running test databases or any number of services, but after changing runlevels these services could be disabled to save on battery.
- Simple: There are (normally) only 7 runlevels, and for most of it’s time the system remains on the
sysvinithave been using runlevels for ages.
- Sufficient: Runlevels have been shown to provide sufficient functionality for most applications.
- Inflexible: While there are ways to create ‘Stacked Runlevels’, the system is not very capable of more exotic requirements.
- Aging: Modern systems, especially desktops and laptops, frequently change their hardware (inserting a USB stick), or network settings (Changing between WiFi networks), steps have been taken by system designers to handle these stumbling blocks, but it’s not longer always sufficient to scan once for hardware, for example.
systemd uses targets instead of runlevels. Each target has a unique name, and multiple targets can be active at one time. Since most distributions using systemd have migrated from a runlevel based init system, there are targets which roughly correspond to the runlevels used by OpenRC,
poweroff: Halt the system.
rescue: Single user mode.
multi-user: Multi-user, non-graphical. User-defined/Site-specific runlevels.
graphical: Multi-user, graphical. Usually multi-user + graphical login.
emergency: Emergency Shell
It’s important to understand that these are not necessarily the only active targets. In the case of the
graphical target, a display manager service like
kdm might be started, this target would also activate the
multi-user target might also activate a service like
dbus, and another target named
basic. This is interesting because it allows us to cleanly compose and extend the system to suit our needs.
.target file resides in
/usr/lib/systemd/system/ and looks like this:
[Unit] Description=Graphical Interface Documentation=man:systemd.special(7) Requires=multi-user.target After=multi-user.target Conflicts=rescue.target Wants=display-manager.service AllowIsolate=yes
This file would be accompanied by a
<target>.wants directory containing the services it should enable.
Targets can be controlled very similar to systemd’s
.service unit files, you just need to add
.target at the end.
systemctl status graphical.target ± % systemctl status graphical.target graphical.target - Graphical Interface Loaded: loaded (/lib/systemd/system/graphical.target; enabled) Active: active since Thu 2014-09-25 21:21:26 PDT; 4 days ago Docs: man:systemd.special(7) Sep 25 21:21:26 aluminum systemd: Starting Graphical Interface. Sep 25 21:21:26 aluminum systemd: Reached target Graphical Interface.
.target based approach allows for various services to be grouped in a logical manner. For example, is is the state of targets on a modern Fedora Desktop:
± % systemctl list-units --type=target --all UNIT LOAD ACTIVE SUB DESCRIPTION basic.target loaded active active Basic System bluetooth.target loaded active active Bluetooth cryptsetup-pre.target loaded inactive dead Encrypted Volumes (Pre) cryptsetup.target loaded active active Encrypted Volumes emergency.target loaded inactive dead Emergency Mode final.target loaded inactive dead Final Step getty.target loaded active active Login Prompts graphical.target loaded active active Graphical Interface local-fs-pre.target loaded active active Local File Systems (Pre) local-fs.target loaded active active Local File Systems multi-user.target loaded active active Multi-User System network-online.target loaded inactive dead Network is Online network.target loaded active active Network nfs.target loaded active active Network File System Server nss-user-lookup.target loaded inactive dead User and Group Name Lookups paths.target loaded active active Paths remote-fs-pre.target loaded inactive dead Remote File Systems (Pre) remote-fs.target loaded active active Remote File Systems rescue.target loaded inactive dead Rescue Mode shutdown.target loaded inactive dead Shutdown sleep.target loaded inactive dead Sleep slices.target loaded active active Slices sockets.target loaded active active Sockets sound.target loaded active active Sound Card suspend.target loaded active active Suspend swap.target loaded active active Swap sysinit.target loaded active active System Initialization syslog.target not-found inactive dead syslog.target time-sync.target loaded inactive dead System Time Synchronized timers.target loaded active active Timers umount.target loaded inactive dead Unmount All Filesystems LOAD = Reflects whether the unit definition was properly loaded. ACTIVE = The high-level unit activation state, i.e. generalization of SUB. SUB = The low-level unit activation state, values depend on unit type. 31 loaded units listed. To show all installed unit files use 'systemctl list-unit-files'.
- Composition: Allows for services to be composed together in logical, user-defined groups easily.
- Configuration: It’s simple to add custom targets.
- Less Restrictive: Since multiple targets can be active at a given time, the system’s behavior can be more finely defined.
- Complexity: A sufficiently sophisticated installation could be composed of dozens of targets, and can require a deeper understanding of the system.