Systemd Services
What Is systemd?
systemd is PID 1 on most modern distros. It parallelizes boot, tracks service dependencies, collects logs via journald, mounts filesystems, and manages cgroups for resource control.
systemctl status
ps -p 1 -o comm=
systemd --version
Units are defined in .service, .timer, .mount, .target, and .socket files under /usr/lib/systemd/system/ (packages) and /etc/systemd/system/ (admin overrides).
Service Lifecycle
sudo systemctl start nginx
sudo systemctl stop nginx
sudo systemctl restart nginx # stop then start
sudo systemctl reload nginx # SIGHUP — reload config if supported
sudo systemctl try-reload nginx # reload or restart if reload unsupported
sudo systemctl enable nginx # start on boot
sudo systemctl disable nginx
sudo systemctl enable --now nginx # enable + start in one step
systemctl is-active nginx
systemctl is-enabled nginx
systemctl is-failed nginx
List and filter units:
systemctl list-units --type=service
systemctl list-units --type=service --state=running
systemctl list-units --failed
systemctl list-unit-files --type=service --state=enabled
systemctl status ssh.service
Boot Targets
| Target | Purpose |
|---|---|
| multi-user.target | Multi-user CLI (typical server default) |
| graphical.target | Desktop environment |
| rescue.target | Single-user recovery |
| emergency.target | Minimal shell, no services |
systemctl get-default
sudo systemctl set-default multi-user.target
sudo systemctl isolate rescue.target # caution: drops to rescue now
Writing a Unit File
# /etc/systemd/system/myapp.service
[Unit]
Description=My Application API
Documentation=https://docs.example.com/myapp
After=network-online.target postgresql.service
Wants=network-online.target
Requires=postgresql.service
[Service]
Type=simple
User=myapp
Group=myapp
WorkingDirectory=/opt/myapp
Environment=NODE_ENV=production
ExecStart=/opt/myapp/bin/server --port 8080
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=5
LimitNOFILE=65535
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
Deploy and verify:
sudo systemctl daemon-reload
sudo systemctl enable --now myapp.service
systemctl cat myapp.service # show effective unit (with overrides)
journalctl -u myapp -f
Service Types
| Type | Use when |
|---|---|
| simple | Process stays in foreground (default) |
| forking | Daemon double-forks (legacy) |
| oneshot | Runs once and exits (setup tasks) |
| notify | Process sends sd_notify when ready |
Drop-In Overrides
Prefer overrides over editing vendor units:
sudo systemctl edit myapp.service
# Creates /etc/systemd/system/myapp.service.d/override.conf
# Example override content:
# [Service]
# Environment=LOG_LEVEL=debug
sudo systemctl daemon-reload
sudo systemctl restart myapp
Timers (cron Alternative)
# /etc/systemd/system/backup.timer
[Unit]
Description=Daily backup timer
[Timer]
OnCalendar=daily
Persistent=true
RandomizedDelaySec=300
[Install]
WantedBy=timers.target
Pair with backup.service (Type=oneshot), then:
sudo systemctl enable --now backup.timer
systemctl list-timers --all
Timers offer logging via journal, dependency awareness, and missed-run catch-up (Persistent=true).
Journal Logs
journalctl -xe # recent errors with hints
journalctl -u nginx --since today
journalctl -u nginx --since "2026-06-01 08:00" --until "2026-06-01 09:00"
journalctl -p err -b # errors since last boot
journalctl -f # follow all logs
journalctl -k # kernel messages
# Disk usage
journalctl --disk-usage
sudo journalctl --vacuum-size=500M
Configure retention in /etc/systemd/journald.conf (SystemMaxUse=, MaxRetentionSec=).
Troubleshooting Failed Units
systemctl status myapp.service -l --no-pager
journalctl -u myapp -n 100 --no-pager
systemctl reset-failed # clear failed state after fix
# Boot performance
systemd-analyze
systemd-analyze blame # slow-starting units
systemd-analyze critical-chain myapp.service
Common failures: wrong User, missing ExecStart path, port already in use, missing daemon-reload after edit.
Best Practices
| Practice | Reason |
|---|---|
Always daemon-reload after unit changes |
systemd caches unit definitions |
Use Restart=on-failure |
Auto-recover from crashes |
Set LimitNOFILE and MemoryMax |
Prevent resource exhaustion |
Log to journal (StandardOutput=journal) |
Centralized querying with journalctl |
Test with systemctl start before enable |
Avoid boot loops |
Common Mistakes
| Mistake | Consequence |
|---|---|
Editing files in /usr/lib/systemd/ |
Overwritten on package upgrade |
Forgetting User= directive |
Service runs as root |
Type=forking with wrong PIDFile |
systemd thinks service failed |
No After=network-online.target |
Service starts before network ready |
Production Scenario
A Node.js API crashes under memory pressure:
# override.conf
[Service]
MemoryMax=1G
MemoryHigh=800M
Restart=on-failure
RestartSec=10
StartLimitBurst=5
StartLimitIntervalSec=300
Combined with alerting on systemctl is-failed myapp and journalctl -p err -u myapp, ops gets paged before users notice repeated restart loops.
systemd replaces ad-hoc init scripts with declarative units — master systemctl, journalctl, and unit file anatomy and you control server behavior from boot through shutdown.