Systemd Cheat Sheet
Commands and unit-file syntax for managing services, timers, and system state with systemd on modern Linux distributions.
systemctl Basics
Control and inspect services.
systemctl start nginx # Start a service nowsystemctl stop nginx # Stop a servicesystemctl restart nginx # Restart a servicesystemctl reload nginx # Reload config without restartsystemctl enable nginx # Start automatically at bootsystemctl disable nginx # Remove from bootsystemctl status nginx # Show current state and recent logssystemctl is-active nginx # Print active/inactivesystemctl daemon-reload # Reload unit files after editing
Service Unit File
Define a custom long-running service.
# /etc/systemd/system/myapp.service[Unit]Description=My ApplicationAfter=network.target[Service]Type=simpleUser=appuserWorkingDirectory=/opt/myappExecStart=/opt/myapp/bin/start.shRestart=on-failureRestartSec=5Environment=NODE_ENV=production[Install]WantedBy=multi-user.target
Timer Unit (cron alternative)
Schedule a service to run periodically.
# /etc/systemd/system/backup.timer[Timer]OnCalendar=dailyPersistent=true[Install]WantedBy=timers.target# /etc/systemd/system/backup.service[Service]Type=oneshotExecStart=/usr/local/bin/backup.sh# Enable with: systemctl enable --now backup.timer
Logs & Debugging
journalctl and troubleshooting commands.
- journalctl -u nginx- Show logs for a specific unit
- journalctl -u nginx -f- Follow logs in real time (like tail -f)
- journalctl -b- Show logs since the current boot
- journalctl -p err- Filter logs by priority (err, warning, info, etc.)
- systemctl list-units --failed- List all units currently in a failed state
- systemd-analyze blame- Show which units took longest to initialize at boot
Dependency Ordering & Wants/Requires
Control startup order and hard/soft dependency semantics between units.
[Unit]Description=API server# Wants = soft dependency: start target too, but don't fail if it failsWants=redis.service# Requires = hard dependency: this unit fails if the dependency failsRequires=postgresql.service# After/Before only affect ORDERING, not dependency resolution# (Wants/Requires alone do not guarantee start order)After=postgresql.service redis.service network-online.targetWants=network-online.target# BindsTo is stricter than Requires: if the bound unit stops,# this unit is stopped too (not just on failure)BindsTo=sys-subsystem-net-devices-eth0.device[Service]ExecStart=/opt/api/bin/serverRestart=on-failure
Resource Control (cgroups v2)
Constrain CPU, memory, and IO for a unit directly via systemd, no separate cgroup tooling needed.
[Service]ExecStart=/opt/worker/bin/run# CPUCPUWeight=200 # relative weight (default 100)CPUQuota=150% # cap at 1.5 cores# MemoryMemoryHigh=512M # soft limit: throttled, not killedMemoryMax=768M # hard limit: OOM-killed if exceededMemorySwapMax=0 # disallow swap for this unit# IOIOWeight=500IOReadBandwidthMax=/dev/sda 50M# Tasks (thread/process count)TasksMax=200# Inspect live values:# systemctl show myapp.service -p MemoryCurrent,CPUUsageNSec# systemd-cgtop
Service Sandboxing & Hardening
Lock down a unit's filesystem, kernel, and privilege surface using systemd's built-in sandboxing directives.
[Service]ExecStart=/opt/myapp/bin/start.shDynamicUser=yes # ephemeral UID/GID, no shared user neededNoNewPrivileges=yes # block setuid/setgid privilege escalationProtectSystem=strict # entire FS read-only except paths belowProtectHome=yes # /home, /root, /run/user inaccessibleReadWritePaths=/var/lib/myappPrivateTmp=yes # isolated /tmp and /var/tmpPrivateDevices=yes # no access to physical devicesProtectKernelTunables=yesProtectKernelModules=yesProtectControlGroups=yesRestrictAddressFamilies=AF_INET AF_INET6 AF_UNIXRestrictNamespaces=yesSystemCallFilter=@system-serviceSystemCallErrorNumber=EPERM# Score the unit's exposure:# systemd-analyze security myapp.service
Socket Activation
Let systemd own the listening socket and lazily start the service only on first connection.
# /etc/systemd/system/myapp.socket[Socket]ListenStream=8080Accept=no[Install]WantedBy=sockets.target# /etc/systemd/system/myapp.service[Unit]Description=My App (socket-activated)[Service]# systemd passes the listening fd via LISTEN_FDS; the app must# accept sd_listen_fds()/inherited fd 3 instead of binding itselfExecStart=/opt/myapp/bin/server# Enable only the socket; the service starts on demand:# systemctl enable --now myapp.socket# systemctl status myapp.service # inactive until first connection
Advanced Analysis & Introspection
Diagnostic commands beyond basic journalctl/status for boot performance and dependency graphs.
- systemd-analyze critical-chain- Show the chain of units that determined the longest boot-time path
- systemd-analyze plot > boot.svg- Render a full graphical timeline of the boot sequence
- systemctl list-dependencies myapp- Print the dependency tree (Wants/Requires) for a unit
- systemctl cat myapp.service- Show the fully resolved unit file including drop-in overrides
- systemctl edit myapp.service- Create a drop-in override without editing the vendor unit file
- systemctl mask myapp.service- Symlink the unit to /dev/null so it cannot be started even manually
- journalctl --disk-usage / vacuum-time=7d- Inspect and trim the on-disk journal size
- systemctl show -p Type,MainPID,ExecMainStatus myapp- Query specific low-level unit properties for scripting
After editing any unit file, always run 'systemctl daemon-reload' before starting/restarting the service — otherwise systemd keeps using the cached in-memory version of the unit.