On modern Debian systems (Debian 12 “bookworm”, Debian 13 “trixie”, and derivatives as of 2026), systemd is the default init system and service manager. It replaced SysV init and Upstart because it offers:
- Parallel startup — dramatically faster boot by starting independent services concurrently
- Dependency-based activation — services only start after their declared prerequisites are ready
- Socket activation — on-demand starting of services when first connection arrives
- Cgroups-based resource control — per-service CPU, memory, I/O limits without external tools
- Unified logging — journald captures stdout/stderr + metadata
- Transactional unit changes — atomic enable/disable/reload operations
- Declarative configuration — everything defined in unit files (.service, .socket, .timer, .target, .mount, etc.)
Understanding systemd units and their lifecycle is more important than memorizing commands — most operational mistakes come from not grasping ordering, wants/requires, and restart policies.
Core systemd Concepts
Unit Types (most relevant for servers)
- .service — long-running processes (nginx, postgresql, sshd)
- .socket — socket activation (on-demand sshd, docker)
- .timer — scheduled tasks (like cron replacements)
- .target — synchronization points (multi-user.target ≈ runlevel 3)
Unit States
- enabled — starts automatically at boot (symlink created in /etc/systemd/system/*.wants/)
- disabled — does not start at boot
- active (running) — currently executing
- active (exited) — one-shot service that finished successfully
- failed — exited with error or timeout
- inactive (dead) — stopped or never started
Dependency Types (critical for correct startup order)
- Requires= — hard dependency: if this unit fails/stops, the dependent unit stops
- Wants= — soft dependency: start this unit if possible, but continue even if it fails
- After= / Before= — ordering constraint (no automatic start)
- BindsTo= — very strict: dependent unit stops if this one stops
Essential Commands for Daily Management
| Goal | Command Example | Notes / Theory Insight |
|---|---|---|
| List all loaded units | systemctl list-units –type=service | Shows current state |
| List only running services | systemctl list-units –type=service –state=running | Faster overview |
| List all installed unit files | systemctl list-unit-files –type=service | Shows enabled/disabled/static |
| Check status of a service | systemctl status ssh | Shows PID, memory, recent logs, cgroup |
| Start a service (now) | systemctl start nginx | Does not enable at boot |
| Stop a service | systemctl stop nginx | Graceful shutdown if ExecStop= defined |
| Restart (stop + start) | systemctl restart postgresql | Triggers reload if supported |
| Reload configuration (no downtime) | systemctl reload nginx | Only if unit has ExecReload= |
| Enable at boot | systemctl enable nginx | Creates symlink in .wants/ |
| Disable at boot | systemctl disable nginx | Removes symlink |
| Mask (prevent start even manually) | systemctl mask bluetooth | Stronger than disable |
| Show dependencies | systemctl list-dependencies multi-user.target | Reveals boot ordering |
| Show reverse dependencies | systemctl list-dependencies –reverse nginx.service | Who depends on this unit? |
| Edit unit file safely | systemctl edit nginx | Creates override in /etc/systemd/system/ |
| Show full resolved unit file | systemctl cat nginx | Merges main file + overrides |
Practical Patterns on Debian Servers
Safe service changes Always use systemctl reload for daemons that support it (nginx, apache2, postfix, unbound, etc.) → zero-downtime config changes.
Debugging startup failures
Bashsystemctl status unit-name journalctl -u unit-name -b -xe systemctl show unit-name -p ExecMainStatusResource limiting (built-in cgroup v2 control)
Override file example:
ini[Service] MemoryMax=2G CPUQuota=50% IOWeight=200 Restart=always RestartSec=5Apply: systemctl daemon-reload && systemctl restart unit
Socket activation (very powerful for on-demand services)
ssh.socket + ssh@.service pattern → sshd only starts when someone connects → saves memory on low-traffic servers.
Timers instead of cron
Debian prefers systemd timers for scheduled maintenance:
- More logging, dependency control, accuracy
- Example: apt-daily.timer, logrotate.timer already present
Quick Reference Cheat Sheet (Most Frequent)
- Check everything about a service: systemctl status <name>
- Make it start at boot: systemctl enable <name>
- Immediate action: start, stop, restart, reload
- See recent logs: journalctl -u <name> -e
- Override settings safely: systemctl edit <name>
- Reload systemd after changes: systemctl daemon-reload
Best Practices Summary (2026 Debian Context)
- Never edit /lib/systemd/system/*.service directly — always use systemctl edit for overrides
- Prefer Wants= over Requires= unless failure must propagate
- Use Restart=always + RestartSec= for critical always-on services
- Monitor failed units: systemctl –failed or Netdata/Prometheus alerts
- Keep unit files minimal; leverage drop-ins for tuning
- Use socket activation for rarely-used services
- Understand targets: multi-user.target is the normal server state
Mastering systemd means thinking in terms of dependencies, lifecycle states, and resource isolation — not just start/stop commands.