Systemd is the init system and service manager used by most modern Linux distributions. Understanding how to create and manage custom service files gives you full control over how your applications start, stop, restart, and recover from failures.
Basic Service File Structure
# /etc/systemd/system/myapp.service
[Unit]
Description=My Application Server
Documentation=https://docs.myapp.com
After=network.target postgresql.service
Wants=postgresql.service
[Service]
Type=simple
User=appuser
Group=appgroup
WorkingDirectory=/opt/myapp
ExecStart=/opt/myapp/bin/server --config /etc/myapp/config.yml
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=5
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
Service Types
- simple: The default. The process started by ExecStart is the main process.
- forking: The process forks and the parent exits. Use for traditional daemons.
- oneshot: Process exits after completing its task. Good for scripts.
- notify: Like simple, but the process signals readiness via sd_notify.
- idle: Like simple, but execution is delayed until all jobs are dispatched.
Restart Policies
[Service]
# Restart options
Restart=always # Always restart
Restart=on-failure # Restart only on non-zero exit
Restart=on-abnormal # Restart on signal, timeout, watchdog
Restart=on-abort # Restart only on signal
RestartSec=5 # Wait 5 seconds before restart
StartLimitIntervalSec=300 # Rate limit: within 300 seconds...
StartLimitBurst=5 # ...allow max 5 restarts
Environment and Security
[Service]
# Environment variables
Environment=NODE_ENV=production
Environment=PORT=3000
EnvironmentFile=/etc/myapp/env
# Security hardening
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/lib/myapp /var/log/myapp
PrivateTmp=true
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
Managing Services
# Reload systemd after editing service files
sudo systemctl daemon-reload
# Start/Stop/Restart
sudo systemctl start myapp
sudo systemctl stop myapp
sudo systemctl restart myapp
# Enable at boot
sudo systemctl enable myapp
# Check status and logs
systemctl status myapp
journalctl -u myapp -f
journalctl -u myapp --since "1 hour ago"
Practical Examples
Node.js Application
[Unit]
Description=Node.js API Server
After=network.target
[Service]
Type=simple
User=nodejs
WorkingDirectory=/opt/api
ExecStart=/usr/bin/node server.js
Environment=NODE_ENV=production
Environment=PORT=3000
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
Python Application with Gunicorn
[Unit]
Description=Gunicorn WSGI Server
After=network.target
[Service]
Type=notify
User=www-data
WorkingDirectory=/opt/webapp
ExecStart=/opt/webapp/venv/bin/gunicorn --bind 0.0.0.0:8000 --workers 4 wsgi:app
ExecReload=/bin/kill -s HUP $MAINPID
Restart=on-failure
[Install]
WantedBy=multi-user.target
Systemd Timers (Cron Alternative)
# /etc/systemd/system/cleanup.timer
[Unit]
Description=Run cleanup daily
[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true
RandomizedDelaySec=600
[Install]
WantedBy=timers.target
# /etc/systemd/system/cleanup.service
[Unit]
Description=Log Cleanup Task
[Service]
Type=oneshot
ExecStart=/usr/local/bin/cleanup-logs.sh
Debugging Tips
- Always run
systemctl daemon-reloadafter editing service files - Check syntax with
systemd-analyze verify myapp.service - View full logs with
journalctl -u myapp -e - Check dependencies with
systemctl list-dependencies myapp - Use
systemctl cat myappto see the effective configuration
Systemd service files give you fine-grained control over your applications. Use the security options to harden your services and the restart policies to build resilient systems that recover automatically from failures.