Table of Contents
- What Are Systemd Unit Files?
- Basic Structure of a Unit File
- Common Unit Types
- Key Sections in Detail
- Advanced Directives & Unit Types
- Validation & Debugging Unit Files
- Best Practices for Writing Unit Files
- Conclusion
- References
1. What Are Systemd Unit Files?
Systemd units are the building blocks of systemd’s management framework. A unit file is a plaintext configuration file that describes a unit’s purpose, behavior, and dependencies. Units can represent services, sockets, timers, mount points, targets (system states), and more.
Where Are Unit Files Stored?
Systemd looks for unit files in standardized directories, ordered by priority (highest to lowest):
/etc/systemd/system/: User-defined or modified units (highest priority)./run/systemd/system/: Runtime-generated units (temporary)./usr/lib/systemd/system/(or/lib/systemd/system/): Default units provided by the OS or packages (lowest priority).
2. Basic Structure of a Unit File
Unit files follow a simple INI-like structure, with:
- Sections: Enclosed in
[brackets](e.g.,[Unit],[Service]), grouping related directives. - Directives: Key-value pairs (
Key=Value) that configure the unit. - Comments: Lines starting with
#(ignored by systemd).
Example: Minimal Unit File
# /etc/systemd/system/myapp.service
[Unit]
Description=My Custom Application
After=network.target
[Service]
Type=simple
ExecStart=/usr/local/bin/myapp --config /etc/myapp.conf
Restart=on-failure
[Install]
WantedBy=multi-user.target
3. Common Unit Types
Systemd supports dozens of unit types, but these are the most frequently used:
| Unit Type | Suffix | Purpose |
|---|---|---|
| Service | .service | Manages a background daemon (e.g., nginx.service, sshd.service). |
| Socket | .socket | Controls network sockets; enables “socket activation” (e.g., sshd.socket). |
| Target | .target | Groups units to define system states (e.g., multi-user.target = “runlevel 3”). |
| Timer | .timer | Schedules units to run at specific times (replaces cron for systemd). |
| Mount | .mount | Manages filesystem mounts (e.g., /home.mount). |
| Automount | .automount | Triggers mounts on-demand (lazy mounting). |
| Path | .path | Monitors file/directory changes to trigger units (e.g., backup.path). |
4. Key Sections in Detail
The [Unit] Section: Metadata & Dependencies
The [Unit] section defines metadata (description, documentation) and dependencies (when to start/stop relative to other units).
Critical Directives:
-
Description=: A human-readable summary of the unit (required for clarity).Description=Nginx Web Server -
Documentation=: Links to docs (URLs or man pages).Documentation=man:nginx(8) https://nginx.org/en/docs/ -
After=/Before=: Control order relative to other units.After=Ameans “start this unit after A starts”.Before=Bmeans “start this unit before B starts”.After=network.target mysql.service # Start after network and MySQL Before=php-fpm.service # Start before PHP-FPM -
Requires=: Hard dependency. If the required unit fails, this unit is stopped.Requires=mysql.service # If MySQL stops, this unit stops too -
Wants=: Soft dependency. Encourages the required unit to start, but this unit runs even if it fails (preferred overRequires=).Wants=logging.service # Try to start logging, but proceed without it -
BindsTo=: Stronger thanRequires=. If the bound unit stops abnormally, this unit is stopped immediately.
The [Service] Section: Service-Specific Configuration
The [Service] section is unique to .service units and defines how the service runs.
Core Directives:
-
Type=: Defines the service’s process model (critical for systemd to manage it correctly).Type Behavior simpleDefault. The service starts immediately; ExecStartruns in the foreground. Systemd considers it “started” onceExecStartbegins.forkingThe service forks a child process (daemonizes). Systemd waits for the parent to exit before considering it “started”. Use for legacy daemons (e.g., sshd).oneshotThe service runs a short task and exits. Systemd waits for it to finish before starting dependent units (e.g., update-motd.service).dbusThe service acquires a D-Bus name. Systemd waits until the name is acquired. notifyThe service sends a signal to systemd when ready (via sd_notify(3)).idleSimilar to simple, but delays start until the system is “idle” (avoids mixing service output with boot messages).Example:
Type=forking # For daemons that fork (e.g., Apache httpd) -
ExecStart=: The command to start the service (required forsimple,forking, etc.). Use absolute paths.ExecStart=/usr/sbin/nginx -c /etc/nginx/nginx.conf -
ExecStartPre=/ExecStartPost=: Commands to run before or afterExecStart.ExecStartPre=/bin/mkdir -p /var/run/nginx # Create PID directory ExecStartPost=/usr/bin/touch /var/log/nginx/started.log -
ExecReload=/ExecStop=: Commands to reload (graceful restart) or stop the service.ExecReload=/usr/sbin/nginx -s reload ExecStop=/usr/sbin/nginx -s stop -
Restart=: When to restart the service (e.g., on crash).Value Behavior noNever restart (default). on-successRestart only if ExecStartexits with exit code 0.on-failureRestart if ExecStartexits with non-zero code, signal, or timeout.on-abnormalRestart on timeout or signal (not on clean exit). alwaysRestart always (even if the service exits cleanly). Restart=on-failure # Restart if the service crashes -
RestartSec=: Delay (in seconds) before restarting (default: 1s).RestartSec=5 # Wait 5s before restarting -
User=/Group=: Run the service as a specific user/group (critical for security).User=nginx Group=nginx -
WorkingDirectory=: Set the service’s working directory.WorkingDirectory=/var/www/html -
Environment=/EnvironmentFile=: Set environment variables. UseEnvironmentFile=for external config files (e.g.,/etc/default/myapp).Environment="PORT=8080" "LOG_LEVEL=info" EnvironmentFile=/etc/sysconfig/nginx # Load variables from a file
The [Install] Section: Enabling/Disabling Units
The [Install] section controls how units are “enabled” (linked to boot targets) or “disabled” (unlinked).
Key Directives:
-
WantedBy=: Defines which target(s) “want” this unit. Enabling the unit creates a symlink in/etc/systemd/system/TARGET.wants/.WantedBy=multi-user.target # Enable for multi-user (non-graphical) boot -
RequiredBy=: Similar toWantedBy=, but creates a hard dependency inTARGET.required/. -
Alias=: Alternative names for the unit (e.g.,Alias=webserver.service). -
Also=: Additional units to enable/disable when this unit is enabled/disabled.
5. Advanced Directives & Unit Types
Template Units
Template units use @ to create dynamic instances (e.g., [email protected] for multiple SSH ports). The %I specifier replaces the instance name:
# /etc/systemd/system/[email protected]
[Unit]
Description=OpenSSH Server on Port %I
[Service]
ExecStart=/usr/sbin/sshd -D -p %I
Enable with systemctl enable [email protected] to run SSH on port 2222.
[Socket] Section (for .socket units)
Configures network sockets for socket activation (start the service only when a connection arrives):
# /etc/systemd/system/myapp.socket
[Unit]
Description=MyApp Socket
[Socket]
ListenStream=127.0.0.1:8080 # Listen on TCP port 8080
Accept=no # Single process handles all connections
[Install]
WantedBy=sockets.target
[Timer] Section (for .timer units)
Schedules units with calendar events (e.g., daily backups):
# /etc/systemd/system/backup.timer
[Unit]
Description=Daily Backup Timer
[Timer]
OnCalendar=*-*-* 03:00:00 # Run daily at 3 AM
Persistent=true # Run missed jobs on startup
[Install]
WantedBy=timers.target
Pair with backup.service to define the backup task.
6. Validation & Debugging Unit Files
Even small syntax errors can break a unit. Use these tools to validate and debug:
-
systemd-analyze verify <unit>: Checks for syntax errors.systemd-analyze verify nginx.service -
systemctl cat <unit>: Shows the full unit file (including overrides).systemctl cat sshd.service -
systemctl show <unit>: Displays all active directives (useful for debugging dependencies).systemctl show nginx.service --property=After,Requires -
journalctl -u <unit>: Views logs for the unit.journalctl -u nginx.service -f # Follow real-time logs
7. Best Practices for Writing Unit Files
- Keep It Simple: Avoid overcomplicating with unnecessary directives.
- Use Absolute Paths: Always specify full paths for
Exec*commands (e.g.,/usr/bin/python3instead ofpython3). - Drop Privileges: Run services as non-root users with
User=/Group=. - Prefer
Wants=OverRequires=: Soft dependencies make the system more resilient. - Document Units: Use
Description=andDocumentation=for clarity. - Test Early: Validate with
systemd-analyze verifybefore deploying. - Separate Config from Code: Store environment variables in
EnvironmentFile=(e.g.,/etc/default/).
8. Conclusion
Systemd unit files are the backbone of Linux service management. By mastering their syntax—from the [Unit] section’s dependencies to the [Service] section’s process control—you gain fine-grained control over how services start, run, and interact with the system. With tools like systemd-analyze and journalctl, debugging and optimizing unit files becomes straightforward.
Whether you’re configuring a web server, scheduling backups, or managing network sockets, a deep understanding of unit files ensures efficient, reliable system operation.