CloudsArk
systemd Linux

systemd Service Units — Writing, Enabling, and Troubleshooting

A practical guide to writing systemd service unit files — covering ExecStart, dependencies, restart policies, and how to diagnose failed services with journalctl.

What is a unit file?

A systemd unit file is a plain-text configuration file that tells systemd how to manage a process. Service units end in .service and live in one of these directories:

/etc/systemd/system/          # admin-created (highest priority)
/usr/lib/systemd/system/      # vendor-provided by packages
/run/systemd/system/          # runtime units (not persistent)

Anatomy of a service unit

[Unit]
Description=My Application
Documentation=https://example.com/docs
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=myapp
Group=myapp
WorkingDirectory=/opt/myapp
ExecStart=/opt/myapp/bin/server --port 8080
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=5s
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target

Key directives

[Unit] section: - After= — start this unit after these others are active - Requires= — hard dependency; fails if dependency fails - Wants= — soft dependency; continues even if dependency fails

[Service] section: - Type=simple — default; ExecStart process is the main process - Type=forking — process forks; systemd tracks the PID file - Type=notify — process sends sd_notify() when ready - Type=oneshot — runs once and exits (use with RemainAfterExit=yes) - ExecStartPre= — run before ExecStart - ExecStartPost= — run after ExecStart succeeds - Restart=on-failure — restart if exit code is non-zero or killed by signal

Enable and start a service

# Copy unit file (if not in /etc/systemd/system/ already)
sudo cp myapp.service /etc/systemd/system/

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

# Enable (creates symlink so it starts at boot)
sudo systemctl enable myapp.service

# Start now
sudo systemctl start myapp.service

# Enable and start in one command
sudo systemctl enable --now myapp.service

Check status

systemctl status myapp.service

# ● myapp.service - My Application
#    Loaded: loaded (/etc/systemd/system/myapp.service; enabled)
#    Active: active (running) since Fri 2026-05-25 09:00:00 UTC; 2h ago
#  Main PID: 1234 (server)

Read logs with journalctl

# All logs for the service
journalctl -u myapp.service

# Follow live output
journalctl -u myapp.service -f

# Since last boot
journalctl -u myapp.service -b

# Last 50 lines
journalctl -u myapp.service -n 50

# Since a specific time
journalctl -u myapp.service --since "2026-05-25 08:00"

Diagnose a failed service

# 1. Check status and last error
systemctl status myapp.service

# 2. Read full journal for context
journalctl -u myapp.service --no-pager -n 100

# 3. Test the ExecStart command manually
sudo -u myapp /opt/myapp/bin/server --port 8080

# 4. Check if the binary exists and is executable
ls -l /opt/myapp/bin/server

Common patterns

# Override a vendor unit without editing it directly
sudo systemctl edit nginx.service
# Creates /etc/systemd/system/nginx.service.d/override.conf

# List all failed units
systemctl --failed

# Stop and disable (survives reboots)
sudo systemctl disable --now myapp.service

# Mask (prevents any start, even manual)
sudo systemctl mask myapp.service

Key takeaways

  • Place custom unit files in /etc/systemd/system/ — never edit vendor files in /usr/lib/systemd/system/.
  • Always run systemctl daemon-reload after adding or modifying unit files.
  • Restart=on-failure with RestartSec=5s is the safe default for long-running services.
  • journalctl -u <service> -f is your first tool when something goes wrong.