TL;DR

First step after editing any unit file: systemctl daemon-reload, or systemd keeps using the old config. systemctl restart = stop+start; systemctl reload sends SIGHUP without interrupting the process (service must support it). In scripts use systemctl is-active svc (exit code 0=active, 3=inactive, 4=unknown) — never parse status output text. Top pitfalls: ① ExecStart does not support pipes/redirection/$VAR — wrap shell syntax as /bin/sh -c '...'; ② % is an escape character in units — write literal %%; ③ journald's default size limit is 10% of disk or 4G (whichever is smaller), it rotates automatically, but journalctl --vacuum-size=200M frees space immediately.

Service Lifecycle Command Table

ActionCommandNotes / exit codes
Startsystemctl start svcNo-op if already running; returns 0
Stopsystemctl stop svcSIGTERM first, SIGKILL after timeout (default 90s)
Restartsystemctl restart svc= stop + start; use after config changes
Reload configsystemctl reload svcSends SIGHUP, no downtime; service must implement it
Enable on bootsystemctl enable --now svcenable + start in one step
Disable on bootsystemctl disable svcRemoves symlink only; does not stop a running process
Masksystemctl mask svcFully disabled; even start is refused (incl. dependency pulls)
Show statussystemctl status svcShows active/inactive/failed + recent logs
Script checksystemctl is-active svcExit: 0=active, 3=inactive, 4=unknown, non-zero=failed
Failed checksystemctl is-failed svc0=failed; handy for monitoring alerts

Common Query Commands

NeedCommandNotes
List running servicessystemctl list-units --type=service --state=runningCount with `\wc -l`
List enabled servicessystemctl list-unit-files --state=enabledShows enabled/disabled/static
Show dependency treesystemctl list-dependencies svcIncludes .target nodes
Show process treesystemctl status svc (CGroup section)Main PID + children
Show full unitsystemctl cat svcMerged result incl. overrides
Override part of configsystemctl edit svcCreates drop-in: /etc/systemd/system/<svc>.d/override.conf
Rewrite whole unitsystemctl edit --full svcCopies full unit to /etc then edits
Reload systemd itselfsystemctl daemon-reloadRequired after editing/installing units
Show default targetsystemctl get-defaultUsually multi-user.target or graphical.target
Switch targetsystemctl isolate multi-user.targetOld-style runlevel 3

Core Unit Directives (copy-paste for .service files)

Section/KeyPurposeExample
[Unit] Description=Human-readable name shown by systemctl statusDescription=My Web API
[Unit] After=Ordering only (no dependency)After=network-online.target
[Unit] Wants= / Requires=Weak/strong dependency; Requires failure fails this unit tooWants=mysql.service
[Service] Type=simple (default; ExecStart stays in foreground) / forking (daemonizes; needs PIDFile=) / notify (process sends READY=1 via sd_notify)Type=simple
[Service] ExecStart=Start command; no pipes/redirection/$VAR; use /bin/sh -c '...' for shell; escape % as %%ExecStart=/usr/bin/python3 /opt/app/main.py
[Service] Environment= / EnvironmentFile=Env vars; file uses one KEY=VALUE per line; a missing file fails startupEnvironmentFile=/etc/app.env
[Service] User= / Group=Drop privileges — always set for securityUser=www-data
[Service] Restart=Restart policy: no (default) / on-failure / always / on-abnormal; pair with RestartSec=3Restart=on-failure
[Service] WorkingDirectory=Working dir; relative ExecStart paths resolve from hereWorkingDirectory=/opt/app
[Service] ExecStartPre= / ExecStartPost=Pre/post-start hooks (multiple allowed, run in order)ExecStartPre=/bin/mkdir -p /run/app
[Install] WantedBy=Target to attach to when enabledWantedBy=multi-user.target

systemd vs SysVinit Comparison

ScenarioSysVinit (legacy)systemd (modern)
Start serviceservice nginx startsystemctl start nginx
Stop serviceservice nginx stopsystemctl stop nginx
Enable on bootchkconfig nginx on (or /etc/rc.d scripts)systemctl enable nginx
Check statusservice nginx statussystemctl status nginx
Runlevelrunlevel / telinit 3systemctl get-default / isolate multi-user.target
LogsApp-written /var/log files + syslogjournalctl -u nginx centralized
Startup script/etc/init.d/nginx (shell script)/etc/systemd/system/nginx.service (declarative)

Copy-Paste Full Unit (Python service)

# /etc/systemd/system/myapp.service
[Unit]
Description=My Python Web API
After=network-online.target mysql.service
Wants=mysql.service

[Service]
Type=simple
User=www-data
Group=www-data
WorkingDirectory=/opt/myapp
EnvironmentFile=/etc/myapp.env
ExecStart=/usr/bin/python3 -m uvicorn main:app --host 127.0.0.1 --port 8000
Restart=on-failure
RestartSec=3

[Install]
WantedBy=multi-user.target
# Deploy a service (with permissions + reload)
sudo cp myapp.service /etc/systemd/system/myapp.service
sudo chmod 644 /etc/systemd/system/myapp.service
sudo systemctl daemon-reload
sudo systemctl enable --now myapp
# Verify
systemctl is-active myapp        # prints active, exit code 0
systemctl status myapp --no-pager | head -n 15
journalctl -u myapp -n 50 --no-pager   # last 50 log lines
# Check service state in a script (use exit codes, not status text)
import subprocess
r = subprocess.run(["systemctl", "is-active", "myapp"], capture_output=True, text=True)
print(r.returncode, r.stdout.strip())   # 0 active / 3 inactive / non-zero failed

# Type=notify with sdnotify (systemd considers the service up only after READY=1)
try:
    import sdnotify
    n = sdnotify.SystemdNotifier()
    n.notify("READY=1")
except ImportError:
    pass  # still works when running outside systemd (local dev)

High-Frequency Pitfall Troubleshooting

SymptomCause → Fix
Config change ignored after restartMissing systemctl daemon-reload → reload, then restart
Service fails instantly with few log linesUsually a missing EnvironmentFile= → verify path and permissions (file must be readable, 644 even for root)
Failed to execute: No such file or directoryWrong ExecStart path or script without exec bit → use absolute path, chmod +x, run the command manually first
`\ or >` in config has no effectExecStart has no shell syntax → use `/bin/sh -c 'cmd \grep x'`
% characters eaten in logs% is an escape char in units → write literal %%, e.g. ExecStart=/bin/echo 100%%
Legacy daemonizing script "fails" on start/stopMissing Type=forking + PIDFile= → declare Type=forking and PIDFile=/run/app.pid
systemctl stop hangs for a long timeProcess ignores SIGTERM; waits out the default 90s timeout before SIGKILL → graceful shutdown in the app, or shorten with TimeoutStopSec=10
Service keeps restarting (Restart=always + crash on boot)Infinite restart loop → use Restart=on-failure + StartLimitIntervalSec, check n-restarts in systemctl status
journalctl fills the diskDefault limit is 10% of disk or 4G (smaller) → journalctl --vacuum-size=200M immediately; long-term: set SystemMaxUse= in /etc/systemd/journald.conf

FAQ

Why don't my systemd service config changes take effect?

Almost always because you skipped systemctl daemon-reload. systemd loads units into memory at startup; after editing a file you must run daemon-reload, then systemctl restart svc for the new config to apply.

Is "active (exited)" in systemctl status normal?

Yes. exited means the main process ran and finished as expected (one-shot tasks, Type=oneshot init scripts) — it is as healthy as active (running). Only failed is abnormal.

How do I make a service start on boot?

sudo systemctl enable svc (or enable --now to also start it immediately). The unit needs an [Install] section with WantedBy=multi-user.target or enable will error. Use disable to remove autostart, mask to fully block it.

journalctl logs are eating the disk — what do I do?

Run journalctl --vacuum-size=200M to free space immediately. To cap it long-term, set SystemMaxUse=200M in /etc/systemd/journald.conf and systemctl restart systemd-journald. The default limit is 10% of the disk or 4G, whichever is smaller.

My service keeps failing to start — fastest way to debug?

systemctl status svc for the error and recent logs → journalctl -u svc -n 50 --no-pager for the full picture → run the ExecStart command manually to prove the command itself works (mind environment variables and working-directory differences).

Sources

最后更新:2026-08-11