On this page
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.targetensures systemd waits for the network to initialize before attempting to start this application. - [Service]: Defines the execution parameters.
ExecStartis the exact command to run.Restart=on-failuretells 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.targethooks 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
- systemd.unit - Unit configuration - freedesktop.org
- systemd.service - Service unit configuration - freedesktop.org