systemd Deep Dive
Quick Reference
# Service basics
systemctl status service-name
systemctl start|stop|restart service-name
systemctl enable|disable service-name
# View logs
journalctl -u service-name -f
journalctl -u service-name --since "10 min ago"
# Find why it failed
systemctl status service-name
journalctl -u service-name -b --no-pager
# Boot analysis
systemd-analyze blame
systemd-analyze critical-chain
Unit Management
Unit Types
| Type | Extension | Purpose |
|---|---|---|
Service |
|
Daemons, background processes |
Socket |
|
Socket-based activation |
Timer |
|
Scheduled tasks (cron replacement) |
Mount |
|
Filesystem mounts |
Target |
|
Grouping units (like runlevels) |
Path |
|
Path-based activation |
Device |
|
Device-based activation |
Slice |
|
Resource management |
Listing Units
# All loaded units
systemctl list-units
# All unit files (loaded or not)
systemctl list-unit-files
# Filter by type
systemctl list-units --type=service
systemctl list-units --type=timer
# Failed units
systemctl --failed
# Units in specific state
systemctl list-units --state=running
systemctl list-units --state=failed
Unit Status
# Detailed status
systemctl status nginx.service
# Check if enabled/active
systemctl is-enabled nginx
systemctl is-active nginx
systemctl is-failed nginx
# Show all properties
systemctl show nginx.service
# Specific property
systemctl show nginx.service -p MainPID
Managing Services
# Start/stop
sudo systemctl start nginx
sudo systemctl stop nginx
sudo systemctl restart nginx
sudo systemctl reload nginx # Reload config without restart
sudo systemctl reload-or-restart nginx
# Enable/disable at boot
sudo systemctl enable nginx
sudo systemctl disable nginx
sudo systemctl enable --now nginx # Enable AND start
# Mask (completely prevent starting)
sudo systemctl mask nginx
sudo systemctl unmask nginx
Unit Files
Locations (Priority Order)
| Path | Purpose | Priority |
|---|---|---|
|
Local admin overrides |
Highest |
|
Runtime units |
Medium |
|
Package-installed units |
Lowest |
# Find unit file location
systemctl show nginx.service -p FragmentPath
# Show unit file contents
systemctl cat nginx.service
Service Unit Structure
# /etc/systemd/system/myapp.service
[Unit]
Description=My Application
Documentation=https://myapp.example.com/docs
After=network.target postgresql.service
Requires=postgresql.service
Wants=redis.service
[Service]
Type=simple
User=myapp
Group=myapp
WorkingDirectory=/opt/myapp
Environment=NODE_ENV=production
EnvironmentFile=/etc/myapp/env
ExecStartPre=/opt/myapp/bin/preflight.sh
ExecStart=/opt/myapp/bin/start.sh
ExecReload=/bin/kill -HUP $MAINPID
ExecStop=/opt/myapp/bin/stop.sh
Restart=on-failure
RestartSec=5
TimeoutStartSec=30
TimeoutStopSec=30
[Install]
WantedBy=multi-user.target
Service Types
| Type | Behavior | Use Case |
|---|---|---|
|
Process started by ExecStart is main process |
Default, most services |
|
Process forks, parent exits |
Traditional daemons |
|
Process expected to exit |
Setup scripts |
|
Sends sd_notify when ready |
systemd-aware apps |
|
Acquires D-Bus name when ready |
D-Bus services |
|
Like simple, but waits for jobs |
Console output |
Restart Options
[Service]
# When to restart
Restart=always # Always restart
Restart=on-failure # Only on non-zero exit
Restart=on-abnormal # Signal, timeout, watchdog
Restart=no # Never auto-restart
# Restart timing
RestartSec=5 # Wait 5s before restart
# Restart limits
StartLimitIntervalSec=60
StartLimitBurst=5 # Max 5 starts in 60s
Security Hardening
[Service]
# User/Group isolation
User=myapp
Group=myapp
DynamicUser=yes # Auto-create transient user
# Filesystem restrictions
ProtectSystem=strict # Read-only except /dev, /proc, /sys
ProtectHome=yes # No access to /home, /root, /run/user
ReadWritePaths=/var/lib/myapp
PrivateTmp=yes # Private /tmp
# Network restrictions
PrivateNetwork=yes # No network (if not needed)
RestrictAddressFamilies=AF_INET AF_INET6
# Capability restrictions
CapabilityBoundingSet=
NoNewPrivileges=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes
# System call filtering
SystemCallFilter=@system-service
SystemCallArchitectures=native
Dependencies
[Unit]
# Ordering (when to start)
After=network.target # Start after network
Before=httpd.service # Start before httpd
# Requirements (what must exist)
Requires=postgresql.service # Hard dependency
Wants=redis.service # Soft dependency
BindsTo=other.service # Start/stop together
# Conflicts
Conflicts=other.service # Cannot run simultaneously
Override Files
# Create override directory
sudo systemctl edit nginx.service
# This creates /etc/systemd/system/nginx.service.d/override.conf
# Only specify what you want to change
# Example: Increase timeout
[Service]
TimeoutStartSec=120
# After editing
sudo systemctl daemon-reload
sudo systemctl restart nginx
# View effective configuration
systemctl cat nginx.service
Timers
Timer vs Cron
| Feature | systemd Timer | cron |
|---|---|---|
Logging |
journalctl integration |
Separate log files |
Dependencies |
Full systemd dependency system |
None |
Missed runs |
Persistent=true catches up |
Lost |
Resource control |
cgroups, CPU/memory limits |
None |
Boot-relative |
OnBootSec, OnUnitActiveSec |
Not possible |
Timer Unit Structure
# /etc/systemd/system/backup.timer
[Unit]
Description=Daily backup timer
[Timer]
OnCalendar=daily
OnCalendar=*-*-* 02:00:00 # Alternative: specific time
Persistent=true # Run missed jobs after boot
RandomizedDelaySec=1h # Spread load
[Install]
WantedBy=timers.target
# /etc/systemd/system/backup.service
[Unit]
Description=Daily backup
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
Timer Expressions
# OnCalendar examples
OnCalendar=daily # Every day at 00:00
OnCalendar=weekly # Every Monday at 00:00
OnCalendar=monthly # First of month
OnCalendar=*-*-* 02:00:00 # Every day at 02:00
OnCalendar=Mon-Fri 09:00 # Weekdays at 09:00
OnCalendar=*:0/15 # Every 15 minutes
OnCalendar=hourly # Every hour
# Monotonic timers (relative)
OnBootSec=5min # 5 min after boot
OnUnitActiveSec=1h # 1h after last run
OnStartupSec=10min # 10 min after systemd start
# Test expression
systemd-analyze calendar "Mon-Fri 09:00"
journalctl
Basic Usage
# All logs
journalctl
# Follow (like tail -f)
journalctl -f
# Specific unit
journalctl -u nginx.service
# Current boot only
journalctl -b
# Previous boot
journalctl -b -1
# Kernel messages (dmesg replacement)
journalctl -k
Filtering
# By time
journalctl --since "2024-01-15"
journalctl --since "10 min ago"
journalctl --since "09:00" --until "10:00"
# By priority (0=emerg through 7=debug)
journalctl -p err # err and above
journalctl -p warning # warning and above
# By field
journalctl _UID=1000
journalctl _PID=1234
journalctl _COMM=sshd
# Multiple units
journalctl -u nginx -u php-fpm
Boot Analysis
Timing Analysis
# Overall boot time
systemd-analyze
# Blame (slowest units)
systemd-analyze blame
# Critical chain (blocking dependencies)
systemd-analyze critical-chain
# Critical chain for specific unit
systemd-analyze critical-chain nginx.service
# Generate SVG boot chart
systemd-analyze plot > boot.svg
Targets (Runlevels)
Troubleshooting
Service Won’t Start
# 1. Check status
systemctl status service.service
# 2. Check logs
journalctl -u service.service -b --no-pager
# 3. Check dependencies
systemctl list-dependencies service.service
# 4. Verify unit file
systemd-analyze verify /etc/systemd/system/service.service
# 5. Check if masked
systemctl is-enabled service.service
# If "masked", unmask:
sudo systemctl unmask service.service
Quick Reference
Essential Commands
# Service control
systemctl start|stop|restart|reload|status unit
systemctl enable|disable|mask|unmask unit
# Unit info
systemctl cat unit # Show unit file
systemctl show unit # All properties
systemctl list-dependencies unit
# System state
systemctl list-units # Loaded units
systemctl list-unit-files # All unit files
systemctl --failed # Failed units
# Logs
journalctl -u unit -f # Follow logs
journalctl -b # Current boot
journalctl --since "10 min ago"
# Boot
systemd-analyze blame
systemd-analyze critical-chain
# Reload after changes
sudo systemctl daemon-reload