WSS
On this page
Containers & OrchestrationRequiredUpdated

systemd unit files

Defining and managing long-running background services, dependencies, and restart policies using the Linux init system.

What it is

systemd is the standard init system and service manager for modern Linux distributions. It is the first process that starts when a Linux machine boots (PID 1), and it is responsible for bringing up the rest of the system.

A unit file is a declarative configuration file that tells systemd how to manage a specific resource. While systemd manages sockets, devices, and mount points, the most common type of unit file is a .service file. This file instructs systemd on how to start a background application, what environment variables it requires, under what user it should run, and what to do if the application crashes.

Why it matters

Before systemd, administrators wrote complex, imperative bash scripts (SysVinit scripts) to start and stop services. These scripts were notoriously fragile, lacked standardized logging, and struggled to handle complex dependency chains (e.g., “do not start the web server until the network interface is up and the database is running”).

Systemd unit files replace thousands of lines of bash with a few lines of declarative text. They provide a unified, highly reliable method for ensuring critical processes start at boot, stay running despite crashes, and log their output centrally to the journald facility. Even in containerized environments, the underlying host operating system relies on systemd to manage the container runtime daemon (like Docker or containerd).

How to implement

Unit files are typically placed in /etc/systemd/system/. They are divided into sections, denoted by square brackets.

[Unit]
Description=My Background Worker
After=network.target

[Service]
Type=simple
User=appuser
Environment="APP_ENV=production"
ExecStart=/opt/myapp/worker --port 8080
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
  • [Unit]: Defines metadata and dependencies. After=network.target ensures systemd waits for the network to initialize before attempting to start this application.
  • [Service]: Defines the execution parameters. ExecStart is the exact command to run. Restart=on-failure tells systemd to automatically restart the process if it exits with a non-zero status code.
  • [Install]: Defines how the service is enabled to start at boot. WantedBy=multi-user.target hooks the service into the standard boot sequence.

After creating or modifying a unit file, you must reload the systemd daemon to read the changes, and then start the service:

systemctl daemon-reload
systemctl enable --now my-worker.service

Common mistakes

Forking vs. Simple Services

The Type= directive dictates how systemd tracks the process. The default is Type=simple, which assumes the process started by ExecStart is the main process. If the application forks into the background (daemonizes) and the initial process exits, systemd will assume the service failed and attempt to restart it endlessly. For applications that daemonize, you must configure Type=forking and often provide a PIDFile so systemd knows which process to monitor.

Ignoring Rate Limits on Restarts

If a service is fundamentally broken (e.g., a syntax error in its configuration file), Restart=on-failure will cause it to crash and restart thousands of times a minute, burning CPU cycles and flooding the central logs. Systemd mitigates this with burst limits (e.g., StartLimitBurst=5), which permanently disable the service if it restarts too many times in a short window. Administrators must monitor for services that have hit this limit and are marked failed.

Storing Output in Custom Log Files

Legacy applications often write their logs to /var/log/myapp.log. When migrating to systemd, this bypasses the central logging facility. Applications should be configured to write logs directly to standard output (STDOUT) and standard error (STDERR). Systemd automatically captures these streams and routes them to the system journal, providing standardized log rotation and querying capabilities without any application-level configuration.

Verification

To verify the status of a service, use the systemctl status command:

systemctl status my-worker.service

This output is critical. It shows whether the service is active (running), failed, or inactive. It displays the main PID, the exact command that was executed, memory usage, and the most recent log lines.

To query the logs for the specific service across time, use the journalctl utility:

journalctl -u my-worker.service -f

The -f flag follows the logs in real-time, providing immediate visibility into the background process’s execution.

Related topics

Sources & further reading