systemd Cheatsheet

Targets and Boot

Use this systemd reference while you build software engineering projects, review code, or refresh the syntax you reach for most.

What a Target Actually Is

A .target is a unit with no process. It exists purely as a synchronization point and a name to hang dependencies on, replacing SysV runlevels.

# /etc/systemd/system/web.target
[Unit]
Description=Web stack
Requires=nginx.service
Wants=myapp.service myapp-worker.service
After=network-online.target
systemctl start web.target
systemctl list-dependencies web.target
systemctl enable web.target

Nothing in a target's file starts anything by itself. Units join it from their own [Install] section:

[Install]
WantedBy=multi-user.target

That line, plus systemctl enable, creates the symlink /etc/systemd/system/multi-user.target.wants/myapp.service. Everything a target "contains" is just symlinks in a .wants/ or .requires/ directory, and you can look at them:

ls /etc/systemd/system/multi-user.target.wants/
ls /usr/lib/systemd/system/multi-user.target.wants/

The Targets You Will Actually Use

TargetReached when
sysinit.targetEarly boot done, filesystems mounted, swap on, udev settled
basic.targetSockets, timers, paths and slices up. The baseline for normal services
multi-user.targetFull non-graphical system. Equivalent to old runlevel 3
graphical.targetmulti-user.target plus the display manager. Old runlevel 5
default.targetAlias for whatever the machine boots into
network.targetThe networking stack has been brought up, not necessarily configured
network-online.targetAn address is actually configured and routable
nss-lookup.targetHost name resolution is available
time-sync.targetThe clock has been synchronized
local-fs.target, remote-fs.target/etc/fstab entries mounted
sockets.target, timers.target, paths.targetActivation units up
getty.targetText consoles
rescue.targetSingle user, local filesystems, root shell. Old runlevel 1
emergency.targetRoot shell only, / read-only, nothing else started
shutdown.targetConflicts with everything, pulled in on any shutdown
reboot.target, poweroff.target, halt.target, kexec.targetThe four ways out
soft-reboot.targetUserspace-only restart, kernel stays up (v254+)
suspend.target, hibernate.target, hybrid-sleep.targetSleep states

Order against network-online.target, not network.target, when your service needs to bind or dial out, and remember to Wants= it as well, because it is not pulled in by default:

[Unit]
Wants=network-online.target
After=network-online.target

network-online.target is only meaningful if a waiter is installed and enabled, such as systemd-networkd-wait-online.service or NetworkManager-wait-online.service.

Default Target and Isolating

systemctl get-default                       # usually graphical.target
sudo systemctl set-default multi-user.target  # boot to text, no GUI
systemctl isolate multi-user.target         # switch now, stop what is not wanted
systemctl isolate rescue.target
systemctl rescue                            # isolate rescue.target and notify users
systemctl emergency
systemctl default                           # back to default.target

set-default just re-points the /etc/systemd/system/default.target symlink. isolate is the aggressive one: it starts the target and stops every unit not required by it, which will drop your SSH session if you isolate something below multi-user.target. Only units with AllowIsolate=yes can be isolated, which is why it works on targets and not on services.

Overriding at the boot prompt, appended to the kernel command line:

systemd.unit=rescue.target
systemd.unit=emergency.target
systemd.unit=multi-user.target
init=/bin/bash
systemd.log_level=debug systemd.log_target=console
rd.systemd.unit=emergency.target      # in the initrd

Old runlevel words still work as aliases: runlevel3.target maps to multi-user.target, runlevel5.target to graphical.target, and telinit 3 is translated for you.

Measuring the Boot

systemd-analyze                    # total, split kernel / initrd / userspace
systemd-analyze time
systemd-analyze blame              # slowest units, longest first
systemd-analyze critical-chain     # the chain that actually gated boot
systemd-analyze critical-chain nginx.service
systemd-analyze plot > boot.svg    # timeline graphic
systemd-analyze dot | dot -Tsvg > deps.svg
$ systemd-analyze
Startup finished in 3.412s (kernel) + 8.109s (userspace) = 11.521s
graphical.target reached after 8.021s in userspace

blame and critical-chain answer different questions. blame ranks units by how long each took, including ones that started in parallel and delayed nothing. critical-chain follows the ordering dependencies that actually held the boot back, with @ marking the time a unit finished and + how long it took. Optimize the chain, not the blame list.

Boot State and Shutdown

systemctl is-system-running        # running, degraded, starting, maintenance...
systemctl --failed                 # what made it degraded
systemctl list-jobs                # jobs still queued, if boot is hanging
systemd-analyze verify default.target
is-system-runningMeaning
initializingEarly boot, before basic.target
startingBetween basic.target and default.target
runningFully up, nothing failed
degradedUp, but at least one unit failed
maintenanceIn rescue or emergency mode
stoppingShutting down
offlinesystemd is not the init system here
systemctl reboot
systemctl poweroff
systemctl halt
systemctl kexec                        # reboot into a preloaded kernel
systemctl soft-reboot                  # restart userspace only, v254+
systemctl suspend
systemctl reboot --boot-loader-entry=help
shutdown -r +5 "rebooting for kernel update"
shutdown -c                            # cancel a scheduled shutdown

Hangs on shutdown are almost always a unit ignoring SIGTERM until TimeoutStopSec= expires. The journal names it as "A stop job is running for ...". Reduce DefaultTimeoutStopSec= in /etc/systemd/system.conf if the default 90 seconds is unbearable, and fix the unit's signal handling properly.