LinuxVII · systemd and Service ManagementDiagnostics
systemd-analyze, security review, and boot diagnostics
What you'll learn
- Use systemd-analyze to find slow boot units
- Identify security weaknesses with systemd-analyze security
- Read blame and critical-chain output
- Find what blocks boot
Prerequisites
Verified against Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL 9.x · Rocky Linux 9.x · AlmaLinux 9.x · Linux kernel 6.1 LTS / 6.6 LTS · systemd 255+ · OpenSSH 8.7p1 (RHEL 9) / 9.6p1 (Ubuntu 24.04) · nftables 1.0.x · chrony 4.x · Pacemaker 2.1.x · Corosync 3.1.x · 2026-08-09
systemd ships a self-diagnostic tool: systemd-analyze. Three
modes cover the most common production investigations.
systemd-analyze security scores a unit on the sandboxing and
resource directives it sets. This lesson uses it as a
diagnostic — read the score, understand what it is measuring —
and does not require you to know those directives yet. Part
XXXVII covers them in full, and is where you go to act on a
bad score.
Time spent at boot
$ systemd-analyzeStartup finished in 1.234s (kernel) + 4.567s (userspace) = 5.801s
graphical.target reached after 4.501s in userspaceIllustrative output
$ systemd-analyze blame | head5.123s myapp.service
1.234s sshd.service
0.890s NetworkManager-wait-online.service
0.456s chrony.service
...Illustrative output
$ systemd-analyze critical-chain sshd.serviceThe time when unit became active with startup times in parentheses.
multi-user.target @4.501s
└─sshd.service @3.267s +1.234s
└─network-online.target @3.200s +0.067s
└─NetworkManager-wait-online.service @3.133s +0.067s
└─NetworkManager.service @2.500s +0.633sIllustrative output
Security review
$ systemd-analyze security myapp.service NAME DESCRIPTION EXPOSURE
✗ RootDirectory/EmergencyUID Service runs as root 0.5
✗ User=/DynamicUser= Service runs as root 0.4
✗ SupplementaryGroups= Has supplementary groups 0.1
✗ PrivateDevices= Service has access to /dev 0.2
✗ ProtectSystem= Filesystem is shared 0.6
✗ ProtectHome= Service has access to /home 0.1
✗ MountAPI= Service may mount filesystems 0.3
✗ RestrictAddressFamilies= Service may use AF_INET, AF_UNIX 0.1
✗ RestrictNamespaces= Service may create namespaces 0.4
✗ SystemCallArchitectures= Service may use non-native arch 0.2
...Illustrative output
The security analysis checks dozens of hardening directives:
- RootDirectory / User — runs as root.
- SupplementaryGroups — has supplementary groups.
- PrivateDevices / PrivateNetwork / PrivateTmp — namespace isolation.
- ProtectSystem / ProtectHome — filesystem visibility.
- SystemCallFilter / SystemCallArchitectures — syscall restrictions.
- CapabilityBoundingSet — capability restrictions.
- NoNewPrivileges — cannot escalate.
$ systemd-analyze security --offline=true ./myapp.service✓ PrivateMounts= Service cannot install system mounts
✗ UMask= Files created by service are world-readable by default 0.1
→ Overall exposure level for myapp.service: 8.9 EXPOSED 🙁Illustrative output
--offline=true needs the units named. Without them it does not
fall back to scanning anything:
$ systemd-analyze security --offline=true --user
Option --offline= requires one or more units to perform a security review.
For CI, add --threshold= so the exposure score becomes a pass
or fail rather than something a human has to read. It exits
non-zero when any unit scores above the threshold:
# Fails the build if any unit is worse than 50
systemd-analyze security --offline=true --threshold=50 \
./units/*.service
Pick the threshold from where your units are today and ratchet it down, rather than starting at a number nothing passes.
Plot SVG
$ systemd-analyze plot > boot.svgIllustrative output
Common findings
| Finding | Fix |
|---|---|
| Service runs as root | Add User=myapp, Group=myapp; add DynamicUser=yes if no persistent state |
ProtectSystem=full or unset | Set ProtectSystem=strict, then re-open only what the service writes with StateDirectory=, LogsDirectory=, RuntimeDirectory= or ReadWritePaths=. Applied alone, strict breaks any service with persistent state at its first write |
PrivateDevices=no | Set PrivateDevices=true |
SystemCallFilter= unset | Add an explicit @system-service filter |
| Boot time > 30s | Run systemd-analyze blame to find the slow unit; consider deferring with WantedBy= |
Knowledge check
Knowledge check · 3 questions
Q1. What does `systemd-analyze blame` show?
Q2. `systemd-analyze security` returns an exposure score for each finding.
Q3. Which of the following are correct systemd diagnostics practices? Select all that apply.
Passing score: 75%. Answers are checked in this browser.