| name | systemd-services |
| description | Create and manage systemd services and timers. Configure service dependencies and resource limits. Use when managing system services. |
| license | MIT |
| metadata | {"author":"devops-skills","version":"1.0"} |
Systemd Services
Create, manage, and monitor systemd services and timers. Covers unit file authoring, dependency management, socket activation, resource limits, journalctl log analysis, and production hardening.
When to Use
- Deploying an application as a managed background service
- Replacing cron jobs with systemd timers for better logging and dependency control
- Setting up socket activation for on-demand service startup
- Configuring resource limits (CPU, memory, I/O) for services
- Debugging service startup failures and runtime crashes
- Managing service dependencies and ordering
Prerequisites
- Linux system running systemd (most modern distributions)
- Root or sudo access for creating system-level unit files
- Application binary or script to run as a service
- Understanding of the application's start/stop lifecycle
Service Unit File -- Complete Example
[Unit]
Description=MyApp Production Server
Documentation=https://docs.example.com/myapp
After=network-online.target postgresql.service
Wants=network-online.target
Requires=postgresql.service
[Service]
Type=notify
User=myapp
Group=myapp
WorkingDirectory=/opt/myapp
EnvironmentFile=/etc/myapp/env
Environment=NODE_ENV=production
Environment=PORT=8080
ExecStartPre=/opt/myapp/bin/migrate --check
ExecStart=/opt/myapp/bin/server --config /etc/myapp/config.yaml
ExecStartPost=/opt/myapp/bin/healthcheck.sh
ExecReload=/bin/kill -HUP $MAINPID
ExecStop=/opt/myapp/bin/graceful-stop.sh
Restart=on-failure
RestartSec=5
StartLimitIntervalSec=300
StartLimitBurst=5
TimeoutStartSec=30
TimeoutStopSec=30
WatchdogSec=60
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
ReadWritePaths=/var/lib/myapp /var/log/myapp
CapabilityBoundingSet=
AmbientCapabilities=
StandardOutput=journal
StandardError=journal
SyslogIdentifier=myapp
[Install]
WantedBy=multi-user.target
Service Management Commands
systemctl daemon-reload
systemctl start myapp
systemctl stop myapp
systemctl restart myapp
systemctl reload myapp
systemctl enable myapp
systemctl enable --now myapp
systemctl disable --now myapp
systemctl status myapp
systemctl is-active myapp
systemctl is-enabled myapp
systemctl is-failed myapp
systemctl list-units --type=service --state=running
systemctl list-units --type=service --state=failed
systemctl show myapp
systemctl show myapp -p MainPID,MemoryCurrent,CPUUsageNSec
systemctl mask myapp
systemctl unmask myapp
systemctl reset-failed myapp
Timer Units (Cron Replacement)
Timer File
[Unit]
Description=Daily backup timer
[Timer]
OnCalendar=*-*-* 02:30:00
Persistent=true
RandomizedDelaySec=900
Unit=backup.service
[Install]
WantedBy=timers.target
Corresponding Service File
[Unit]
Description=Daily backup job
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
User=backup
ExecStart=/usr/local/bin/run-backup.sh
StandardOutput=journal
StandardError=journal
Timer Management
systemd-analyze calendar "Mon..Fri *-*-* 09:00"
systemctl list-timers --all
systemctl enable --now backup.timer
systemctl start backup.service
Socket Activation
[Unit]
Description=MyApp Socket
[Socket]
ListenStream=8080
Accept=no
[Install]
WantedBy=sockets.target
[Unit]
Description=MyApp Server
Requires=myapp.socket
[Service]
Type=notify
User=myapp
ExecStart=/opt/myapp/bin/server
[Install]
WantedBy=multi-user.target
systemctl enable --now myapp.socket
systemctl status myapp.socket
systemctl list-sockets
Dependency Management
systemctl list-dependencies myapp
systemctl list-dependencies myapp --reverse
systemd-analyze critical-chain myapp.service
Resource Limits (cgroups v2)
[Service]
MemoryMax=1G
MemoryHigh=768M
CPUQuota=200%
CPUWeight=100
IOWeight=50
IOReadBandwidthMax=/dev/sda 100M
IOWriteBandwidthMax=/dev/sda 50M
LimitNOFILE=65535
LimitNPROC=4096
TasksMax=512
OOMPolicy=continue
mkdir -p /etc/systemd/system/myapp.service.d/
cat <<'EOF' > /etc/systemd/system/myapp.service.d/limits.conf
[Service]
MemoryMax=1G
CPUQuota=200%
EOF
systemctl daemon-reload
systemctl restart myapp
systemctl status myapp
systemd-cgtop
systemctl edit myapp
Journalctl Log Analysis
journalctl -u myapp -f
journalctl -u myapp -b
journalctl -u myapp --since "2025-01-15 08:00" --until "2025-01-15 12:00"
journalctl -u myapp -p err
journalctl -u myapp -n 100 --no-pager -l
journalctl -u myapp -o json-pretty --no-pager | head -50
journalctl --disk-usage
journalctl --rotate
journalctl --vacuum-time=7d
journalctl --vacuum-size=500M
Troubleshooting
| Symptom | Diagnostic Command | Common Fix |
|---|
| Service fails to start | systemctl status myapp, journalctl -u myapp -n 50 | Check ExecStart path, permissions, config syntax |
| Service keeps restarting | journalctl -u myapp --since "5 min ago" | Check StartLimitBurst; look for crash in logs |
| "Main process exited, code=exited, status=217" | journalctl -u myapp | User or group in unit file does not exist |
| "Failed to set up mount namespacing" | Check ProtectSystem/PrivateTmp | Kernel too old or SELinux blocking; relax directives |
| Timer not firing | systemctl list-timers, systemctl status backup.timer | Ensure timer is enabled; validate OnCalendar expression |
| Service starts before dependency | Check After= and Requires= | Add After=dependency.service for ordering |
| OOM killed | journalctl -k | grep oom, dmesg | Increase MemoryMax or optimize application memory |
| Cannot bind to port 80 | Check AmbientCapabilities | Add CAP_NET_BIND_SERVICE or use a higher port |
Related Skills
linux-administration -- General system administration context
performance-tuning -- Kernel tuning and resource optimization
user-management -- Service accounts and permissions
backup-recovery -- Scheduling backups with systemd timers