Linux Systemd Targets vs Traditional Runlevels

Modern Linux distributions have largely replaced legacy SysVinit runlevels with systemd targets to manage system initialization and operating states. While traditional runlevels relied on sequentially numbered operational modes (from 0 to 6) controlled by shell scripts, systemd organizes boot states as .target unit files. This article explains how targets like multi-user.target function, how they map to legacy runlevels, and how systemd orchestrates dependencies, parallel services, and administrative transitions between different system states.

The Shift from Runlevels to Targets

In the traditional SysVinit architecture, runlevels represented predefined static operating modes:

SysVinit processed these runlevels sequentially. Scripts prefixed with S (start) or K (kill) in directories like /etc/rc3.d/ executed in numeric order. This linear model introduced boot bottlenecks and lacked nuanced dependency resolution.

Systemd modernized this mechanism by treating operating states as target units (.target). A target does not execute code directly; instead, it serves as an aggregation point and synchronization anchor for services, sockets, mount points, and other targets.

Mapping Runlevels to Systemd Targets

To maintain backward compatibility, systemd defines exact equivalents and symlinks for historical runlevels:

Traditional Runlevel Systemd Target Unit Description
Runlevel 0 poweroff.target Shuts down and powers off the system
Runlevel 1 rescue.target Minimal base system and single-user shell
Runlevel 2, 3, 4 multi-user.target Non-graphical multi-user environment with networking
Runlevel 5 graphical.target Multi-user environment with GUI and display manager
Runlevel 6 reboot.target Shuts down and reboots the system
Emergency Mode emergency.target Minimal console with root file system mounted read-only

Legacy tools such as telinit 3 or init 5 still function on systemd-based distributions because systemd maps those commands to target activations behind the scenes.

How multi-user.target Operates

The multi-user.target is the standard operational state for headless servers and CLI workstations. Rather than running a static sequence of numbered scripts, systemd evaluates the target’s dependency tree:

  1. Dependency Definitions: Units declare their association with a target using configuration directives such as Wants=, Requires=, and PartOf=. For instance, networking services, SSH daemons, and system loggers declare WantedBy=multi-user.target in their [Install] sections.
  2. File System Symlinks: When a service is enabled via systemctl, systemd creates a symbolic link to the unit file inside /etc/systemd/system/multi-user.target.wants/. At boot time, reaching multi-user.target entails pulling in every unit linked in this directory.
  3. Parallel Execution: Systemd parses ordering dependencies (Before= and After=). If two services depend on multi-user.target but not on each other, systemd starts them simultaneously, drastically reducing system boot times compared to SysVinit.
  4. Target Hierarchy: Targets can inherit from other targets. For example, graphical.target includes multi-user.target as a prerequisite, which in turn depends on basic.target, sysinit.target, and local-fs.target.

Managing Targets with Systemctl

System administrators interact with system targets using the systemctl utility rather than editing configuration files manually.

Checking the Default Target

To view which target activates automatically at boot:

systemctl get-default

Changing the Default Boot Target

To switch a system permanently from a graphical desktop to a command-line interface:

sudo systemctl set-default multi-user.target

This command updates the symbolic link located at /etc/systemd/system/default.target to point directly to /lib/systemd/system/multi-user.target.

Switching Targets at Runtime

To change the system state immediately without rebooting, use the isolate command:

sudo systemctl isolate multi-user.target

The isolate operation acts like changing runlevels in SysVinit. Systemd stops all services that are not required by multi-user.target (such as Xorg, Wayland, or desktop display managers like GDM/LightDM) and starts any missing services defined under the target.

Inspecting Target Dependencies

To visualize all units required or pulled in by multi-user.target:

systemctl list-dependencies multi-user.target

This hierarchical view allows administrators to audit precisely which services participate in bringing the system into its operational multi-user state.