Overview
systemd is the init system on nearly every modern Linux distribution. It starts services at boot, restarts them after failures, and captures their logs. This tutorial shows how to wrap any application in a systemd unit so it runs reliably in production.
Why Use systemd?
| Feature | Benefit |
| Automatic restart | Service recovers from crashes without manual intervention |
| Boot integration | Starts automatically when the system boots |
| Dependency ordering | Start after network or database services are ready |
| Centralized logs | All output captured by journald |
| Resource limits | Cap CPU and memory per service |
Unit File Locations
| Path | Purpose |
/etc/systemd/system/ | Administrator-defined services (recommended) |
/lib/systemd/system/ | Package-installed services |
~/.config/systemd/user/ | User-level services |
Step 1: Create the Unit File
Create /etc/systemd/system/myapp.service:
[Unit]
Description=My Node.js Application
After=network.target
Wants=network-online.target
[Service]
Type=simple
User=appuser
Group=appuser
WorkingDirectory=/opt/myapp
ExecStart=/usr/bin/node /opt/myapp/server.js
Restart=on-failure
RestartSec=5
Environment=NODE_ENV=production
Environment=PORT=3000
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
Section Reference
| Section | Key directives |
[Unit] | Description, After, Requires, Wants |
[Service] | Type, User, ExecStart, Restart |
[Install] | WantedBy determines when the service starts |
Common Service Types
| Type | Behavior |
simple | Default; the process started by ExecStart is the service |
forking | Process forks and the parent exits |
oneshot | Runs once and exits; useful for scripts |
notify | Service signals readiness with sd_notify |
Step 2: Enable and Start
sudo systemctl daemon-reload
sudo systemctl enable myapp
sudo systemctl start myapp
sudo systemctl status myapp
Step 3: View Logs
journalctl -u myapp
journalctl -u myapp -f
journalctl -u myapp --since "1 hour ago"
Managing the Service
| Command | Purpose |
systemctl stop myapp | Stop the service |
systemctl restart myapp | Restart the service |
systemctl reload myapp | Reload configuration without restart |
systemctl disable myapp | Prevent start on boot |
systemctl is-active myapp | Check current state |
Adding Resource Limits
[Service]
MemoryMax=512M
CPUQuota=50%
LimitNOFILE=65535
Common Errors
| Error | Cause |
status=203/EXEC | Wrong path in ExecStart or not executable |
status=200/CHDIR | WorkingDirectory does not exist |
| Service starts then immediately stops | Using Type=simple for a daemon that forks; switch to forking |
| Changes not applied | Forgot systemctl daemon-reload |