Systemd Services: Run and Manage Background Processes on Linux

Systemd Services: Run and Manage Background Processes on Linux


If you’ve ever needed to run a script, web app, or service in the background on Linux — and have it start automatically on boot, restart on crash, and log output properly — systemd is how you do it. It’s the init system and service manager on virtually every modern Linux distribution.

This guide covers the practical essentials: managing services with systemctl, creating your own service files, viewing logs with journalctl, and handling timers as a modern alternative to cron.

What is Systemd?

Systemd is the first process that runs when Linux boots (PID 1). It manages:

  • Services: Background processes (daemons) like nginx, sshd, Docker
  • Targets: Groups of services that define system states (like runlevels)
  • Timers: Scheduled tasks (modern replacement for cron)
  • Mounts: Filesystem mount points
  • Sockets: Network socket activation

Every service is defined by a unit file — a simple text configuration file.

Managing Services with systemctl

Basic Commands

# Start a service
sudo systemctl start nginx

# Stop a service
sudo systemctl stop nginx

# Restart a service
sudo systemctl restart nginx

# Reload configuration without full restart
sudo systemctl reload nginx

# Check status
systemctl status nginx

Enable/Disable Auto-Start on Boot

# Enable (start automatically on boot)
sudo systemctl enable nginx

# Disable (don't start on boot)
sudo systemctl disable nginx

# Enable AND start immediately
sudo systemctl enable --now nginx

# Disable AND stop immediately
sudo systemctl disable --now nginx

Check if a Service is Running

# Is it active?
systemctl is-active nginx
# Output: active

# Is it enabled on boot?
systemctl is-enabled nginx
# Output: enabled

# Is it failed?
systemctl is-failed nginx

List Services

# List all running services
systemctl list-units --type=service --state=running

# List all services (including inactive)
systemctl list-units --type=service --all

# List enabled services
systemctl list-unit-files --type=service --state=enabled

# List failed services
systemctl --failed

Creating a Custom Service

This is where systemd really shines. You can turn any script or application into a managed service.

Step 1: Create the Service File

Service files live in /etc/systemd/system/. Create one:

sudo nano /etc/systemd/system/myapp.service

Basic Service File

[Unit]
Description=My Application
After=network.target

[Service]
Type=simple
User=www-data
Group=www-data
WorkingDirectory=/opt/myapp
ExecStart=/usr/bin/node /opt/myapp/server.js
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

Step 2: Reload and Start

# Reload systemd to pick up the new file
sudo systemctl daemon-reload

# Start the service
sudo systemctl start myapp

# Enable on boot
sudo systemctl enable myapp

# Check status
systemctl status myapp

Service File Anatomy

[Unit] Section

[Unit]
Description=Human-readable description
Documentation=https://example.com/docs
After=network.target postgresql.service
Requires=postgresql.service
Wants=redis.service
  • Description: What the service does
  • After: Start this service after the listed units
  • Requires: Hard dependency — if the required unit fails, this unit fails too
  • Wants: Soft dependency — if the wanted unit fails, this unit still starts

[Service] Section

[Service]
Type=simple
User=appuser
Group=appgroup
WorkingDirectory=/opt/myapp
ExecStart=/opt/myapp/start.sh
ExecStop=/opt/myapp/stop.sh
ExecReload=/bin/kill -HUP $MAINPID
Restart=always
RestartSec=10
Environment=NODE_ENV=production
EnvironmentFile=/opt/myapp/.env
StandardOutput=journal
StandardError=journal

Type options:

TypeDescription
simpleDefault. The process started by ExecStart is the main process
forkingThe process forks and the parent exits (traditional daemons)
oneshotThe process exits after doing its job (like a script)
notifyLike simple, but the service sends a notification when ready

Restart options:

ValueRestarts on…
noNever restart
on-failureNon-zero exit code, signal, timeout
on-abnormalSignal, timeout
alwaysAlways restart, regardless of exit status

[Install] Section

[Install]
WantedBy=multi-user.target
  • WantedBy=multi-user.target: Start in normal multi-user mode (most common)
  • WantedBy=graphical.target: Start in graphical/desktop mode

Real-World Examples

Node.js Application

[Unit]
Description=Node.js Express App
After=network.target

[Service]
Type=simple
User=node
WorkingDirectory=/opt/express-app
ExecStart=/usr/bin/node app.js
Restart=always
RestartSec=10
Environment=NODE_ENV=production
Environment=PORT=3000

[Install]
WantedBy=multi-user.target

Python Flask with Gunicorn

[Unit]
Description=Flask App with Gunicorn
After=network.target

[Service]
Type=simple
User=flask
Group=flask
WorkingDirectory=/opt/flask-app
ExecStart=/opt/flask-app/venv/bin/gunicorn --workers 4 --bind 0.0.0.0:8000 app:app
Restart=always
RestartSec=5
EnvironmentFile=/opt/flask-app/.env

[Install]
WantedBy=multi-user.target

Go Binary

[Unit]
Description=Go Web Service
After=network.target

[Service]
Type=simple
User=goapp
ExecStart=/opt/goapp/server
Restart=on-failure
RestartSec=5
LimitNOFILE=65535

[Install]
WantedBy=multi-user.target

Simple Bash Script

[Unit]
Description=Backup Script
After=network.target

[Service]
Type=oneshot
ExecStart=/opt/scripts/backup.sh
StandardOutput=journal

Viewing Logs with journalctl

Systemd captures all service output (stdout and stderr) in the journal. Use journalctl to view it.

# View logs for a specific service
journalctl -u nginx

# Follow logs in real-time (like tail -f)
journalctl -u myapp -f

# Show logs since last boot
journalctl -u myapp -b

# Show last 50 lines
journalctl -u myapp -n 50

# Show logs from the last hour
journalctl -u myapp --since "1 hour ago"

# Show logs between dates
journalctl -u myapp --since "2024-01-01" --until "2024-01-02"

# Show only errors
journalctl -u myapp -p err

# Output as JSON
journalctl -u myapp -o json-pretty

# Show disk usage of journal
journalctl --disk-usage

# Clean up old logs (keep last 1GB)
sudo journalctl --vacuum-size=1G

# Clean up logs older than 30 days
sudo journalctl --vacuum-time=30d

Systemd Timers (Modern Cron)

Systemd timers can replace cron jobs with better logging and dependency management.

Create a Timer

Service file (/etc/systemd/system/backup.service):

[Unit]
Description=Daily Backup

[Service]
Type=oneshot
ExecStart=/opt/scripts/backup.sh

Timer file (/etc/systemd/system/backup.timer):

[Unit]
Description=Run backup daily

[Timer]
OnCalendar=daily
Persistent=true

[Install]
WantedBy=timers.target

Enable the timer:

sudo systemctl enable --now backup.timer

# List active timers
systemctl list-timers

# Check when it last ran
systemctl status backup.timer

OnCalendar examples:

OnCalendar=hourly
OnCalendar=daily
OnCalendar=weekly
OnCalendar=*-*-* 03:00:00       # Every day at 3 AM
OnCalendar=Mon *-*-* 09:00:00   # Every Monday at 9 AM
OnCalendar=*-*-* *:*:00         # Every minute
OnCalendar=*-*-01 00:00:00      # First of every month

Security Hardening

Systemd offers built-in security features to restrict what a service can do:

[Service]
# Run as unprivileged user
User=myapp
Group=myapp

# Filesystem restrictions
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/opt/myapp/data

# Network restrictions
PrivateNetwork=false

# No new privileges
NoNewPrivileges=true

# Restrict system calls
SystemCallArchitectures=native

# Private /tmp
PrivateTmp=true

# Read-only /etc
ProtectEtc=true

Check the security score of a service:

systemd-analyze security myapp.service

Troubleshooting

# Check why a service failed
systemctl status myapp
journalctl -u myapp -n 30 --no-pager

# Verify a service file for syntax errors
systemd-analyze verify /etc/systemd/system/myapp.service

# Show boot time analysis
systemd-analyze blame

# Show service dependencies
systemctl list-dependencies myapp

# Reset a failed service
sudo systemctl reset-failed myapp

Quick Reference

CommandAction
systemctl start <svc>Start service
systemctl stop <svc>Stop service
systemctl restart <svc>Restart service
systemctl status <svc>Check status
systemctl enable <svc>Auto-start on boot
systemctl disable <svc>Remove from boot
systemctl daemon-reloadReload unit files
journalctl -u <svc> -fFollow logs
systemctl list-unitsList all units
systemctl --failedShow failed units

Summary

Systemd is the standard way to manage services on Linux. Once you know how to write a service file, you can turn any application into a reliable background process with automatic restarts, logging, and boot integration.

Key resources:

Create a service file, daemon-reload, enable --now, and your application runs forever.