Ubuntu & Linux

Systemd Service Files: Run Any App as a Linux Service

Learn how to create systemd service files to run Node.js, Python, Go, or any application as a background service that starts on boot and auto-restarts on crash.

March 19, 20262 min read

Why Systemd Instead of nohup or screen?

Many developers deploy apps with nohup python app.py & or inside a screen session. This works until:

  • The server reboots and your app doesn't start
  • The app crashes at 3am and nobody restarts it
  • You need to check logs and they're scattered or lost
  • You're running multiple services and lose track

Systemd solves all of these:
  • Auto-start on boot — your app starts when the server does
  • Auto-restart on crash — configurable restart policies
  • Centralized loggingjournalctl collects all output
  • Resource management — set memory and CPU limits
  • Dependency management — start after database, network, etc.

Creating Your First Service File

A systemd service file lives in /etc/systemd/system/ and has a .service extension.

Example: Python FastAPI application
sudo nano /etc/systemd/system/myapi.service

[Unit]

Description=My FastAPI Application

After=network.target

[Service]

Type=simple

User=deploy

Group=deploy

WorkingDirectory=/home/deploy/myapi

EnvironmentFile=/home/deploy/myapi/.env

ExecStart=/home/deploy/myapi/venv/bin/uvicorn app.main:app --host 127.0.0.1 --port 8000

Restart=always

RestartSec=5

StandardOutput=journal

StandardError=journal

[Install]

WantedBy=multi-user.target

Key fields explained:
  • After=network.target — wait for networking before starting
  • User=deploy — run as non-root user
  • EnvironmentFile — load environment variables from .env
  • Restart=always — restart on any exit (crash, signal, etc.)
  • RestartSec=5 — wait 5 seconds before restarting
  • WantedBy=multi-user.target — start in normal multi-user mode

Service Files for Different Stacks

Node.js Application:
[Service]

Type=simple

User=deploy

WorkingDirectory=/home/deploy/myapp

ExecStart=/usr/bin/node server.js

Restart=always

Environment=NODE_ENV=production

Environment=PORT=3000

Go Binary:
[Service]

Type=simple

User=deploy

ExecStart=/home/deploy/myapp/server

Restart=always

LimitNOFILE=65535

Docker Compose:
[Service]

Type=oneshot

RemainAfterExit=yes

User=deploy

WorkingDirectory=/home/deploy/myapp

ExecStart=/usr/bin/docker compose up -d

ExecStop=/usr/bin/docker compose down

The pattern is always the same: specify the working directory, the start command, and the user.

Managing Your Service

After creating the service file:

# Reload systemd to pick up new file

sudo systemctl daemon-reload

# Start the service

sudo systemctl start myapi

# Enable auto-start on boot

sudo systemctl enable myapi

# Check status

sudo systemctl status myapi

# View logs (last 50 lines)

sudo journalctl -u myapi -n 50

# Follow logs in real-time

sudo journalctl -u myapi -f

# Restart after code changes

sudo systemctl restart myapi

# Stop the service

sudo systemctl stop myapi

The enable command is what makes it start on boot. Without it, the service only runs when you manually start it.

Advanced: Resource Limits and Hardening

Prevent a runaway process from taking down your server:

[Service]

# Memory limit (kill if exceeded)

MemoryMax=512M

MemoryHigh=400M

# CPU limit (50% of one core)

CPUQuota=50%

# Security hardening

NoNewPrivileges=yes

ProtectSystem=strict

ProtectHome=read-only

PrivateTmp=yes

ReadWritePaths=/home/deploy/myapi/data

  • MemoryMax — hard kill if memory exceeds 512MB
  • MemoryHigh — throttle at 400MB (soft limit)
  • CPUQuota — limit CPU usage
  • ProtectSystem=strict — make the entire filesystem read-only except specified paths
  • PrivateTmp — give the service its own /tmp directory

These settings are especially important for production services handling untrusted input.

systemdlinuxubuntuservicedeploymentnode.jspython

Related Articles