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:
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:
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
Services can be added to runlevels with:
Or removed with:
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.
; ) ; )
.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:
) ) )
- 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.