Skip to main content
RunBook Academy

LinuxVII · systemd and Service ManagementDiagnostics

systemd-analyze, security review, and boot diagnostics

Intermediate⏱ ~10 minbashsystemd-analyze

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

Not yet marked complete on this device.

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

Read-only / Safesystemd-analyze
$ systemd-analyze
Startup finished in 1.234s (kernel) + 4.567s (userspace) = 5.801s
graphical.target reached after 4.501s in userspace

Illustrative output

Read-only / Safesystemd-analyze blame
$ systemd-analyze blame | head
5.123s myapp.service
1.234s sshd.service
0.890s NetworkManager-wait-online.service
0.456s chrony.service
...

Illustrative output

Read-only / Safecritical-chain
$ systemd-analyze critical-chain sshd.service
The 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.633s

Illustrative output

Security review

Read-only / Safesecurity 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.
Read-only / Safeoffline security audit
$ 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

Read-only / Safeboot plot
$ systemd-analyze plot > boot.svg

Illustrative output

Common findings

FindingFix
Service runs as rootAdd User=myapp, Group=myapp; add DynamicUser=yes if no persistent state
ProtectSystem=full or unsetSet 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=noSet PrivateDevices=true
SystemCallFilter= unsetAdd an explicit @system-service filter
Boot time > 30sRun systemd-analyze blame to find the slow unit; consider deferring with WantedBy=

Knowledge check

Knowledge check · 3 questions

  1. Q1. What does `systemd-analyze blame` show?

  2. Q2. `systemd-analyze security` returns an exposure score for each finding.

  3. Q3. Which of the following are correct systemd diagnostics practices? Select all that apply.

Passing score: 75%. Answers are checked in this browser.