systemd Unit Files#
Service unit skeleton#
# /etc/systemd/system/myapp.service
[Unit]
Description=My Application
After=network.target
Wants=network.target
[Service]
Type=simple
User=myapp
Group=myapp
WorkingDirectory=/opt/myapp
ExecStart=/opt/myapp/bin/myapp --config /etc/myapp/config.toml
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=5s
StandardOutput=journal
StandardError=journal
Environment="NODE_ENV=production"
EnvironmentFile=-/etc/myapp/env # dash = ignore if missing
[Install]
WantedBy=multi-user.target
Timer unit (cron replacement)#
# /etc/systemd/system/backup.timer
[Unit]
Description=Daily backup
[Timer]
OnCalendar=*-*-* 02:30:00
Persistent=true # run missed timers on next boot
[Install]
WantedBy=timers.target
# /etc/systemd/system/backup.service (paired service)
[Unit]
Description=Run backup
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
Essential commands#
# Reload unit files after editing
systemctl daemon-reload
# Enable + start
systemctl enable --now myapp.service
# Status / logs
systemctl status myapp.service
journalctl -u myapp.service -f # follow
journalctl -u myapp.service --since "1 hour ago"
journalctl -u myapp.service -n 50 --no-pager
# Restart / reload
systemctl restart myapp.service
systemctl reload myapp.service # sends SIGHUP (if ExecReload set)
# List timers
systemctl list-timers --all
# Show unit dependencies
systemctl list-dependencies myapp.service
# Check why a unit failed
systemctl status myapp.service
journalctl -xe
Service types#
| Type | Use case |
|---|
simple | Process is the main process; starts immediately |
forking | Process forks; PID file required |
oneshot | Runs once, exits; systemd waits |
notify | Process calls sd_notify() when ready |
dbus | Ready when D-Bus name acquired |
idle | Like simple but waits for other jobs to finish |