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.targetsystemctl 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
| Target | Reached when |
|---|---|
sysinit.target | Early boot done, filesystems mounted, swap on, udev settled |
basic.target | Sockets, timers, paths and slices up. The baseline for normal services |
multi-user.target | Full non-graphical system. Equivalent to old runlevel 3 |
graphical.target | multi-user.target plus the display manager. Old runlevel 5 |
default.target | Alias for whatever the machine boots into |
network.target | The networking stack has been brought up, not necessarily configured |
network-online.target | An address is actually configured and routable |
nss-lookup.target | Host name resolution is available |
time-sync.target | The clock has been synchronized |
local-fs.target, remote-fs.target | /etc/fstab entries mounted |
sockets.target, timers.target, paths.target | Activation units up |
getty.target | Text consoles |
rescue.target | Single user, local filesystems, root shell. Old runlevel 1 |
emergency.target | Root shell only, / read-only, nothing else started |
shutdown.target | Conflicts with everything, pulled in on any shutdown |
reboot.target, poweroff.target, halt.target, kexec.target | The four ways out |
soft-reboot.target | Userspace-only restart, kernel stays up (v254+) |
suspend.target, hibernate.target, hybrid-sleep.target | Sleep 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 initrdOld 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-running | Meaning |
|---|---|
initializing | Early boot, before basic.target |
starting | Between basic.target and default.target |
running | Fully up, nothing failed |
degraded | Up, but at least one unit failed |
maintenance | In rescue or emergency mode |
stopping | Shutting down |
offline | systemd 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.