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?

FeatureBenefit
Automatic restartService recovers from crashes without manual intervention
Boot integrationStarts automatically when the system boots
Dependency orderingStart after network or database services are ready
Centralized logsAll output captured by journald
Resource limitsCap CPU and memory per service

Unit File Locations

PathPurpose
/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

SectionKey directives
[Unit]Description, After, Requires, Wants
[Service]Type, User, ExecStart, Restart
[Install]WantedBy determines when the service starts

Common Service Types

TypeBehavior
simpleDefault; the process started by ExecStart is the service
forkingProcess forks and the parent exits
oneshotRuns once and exits; useful for scripts
notifyService 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

CommandPurpose
systemctl stop myappStop the service
systemctl restart myappRestart the service
systemctl reload myappReload configuration without restart
systemctl disable myappPrevent start on boot
systemctl is-active myappCheck current state

Adding Resource Limits

[Service]
MemoryMax=512M
CPUQuota=50%
LimitNOFILE=65535

Common Errors

ErrorCause
status=203/EXECWrong path in ExecStart or not executable
status=200/CHDIRWorkingDirectory does not exist
Service starts then immediately stopsUsing Type=simple for a daemon that forks; switch to forking
Changes not appliedForgot systemctl daemon-reload