systemd Unit File Cheatsheet
systemd unit file directives at a glance: [Unit] dependencies, service types, Exec commands, restart policy, users and environment, resource limits, hardening, timers, and drop-in overrides.
A unit file is the plain-text configuration systemd reads to decide how to start, stop, and supervise a service, timer, or other unit. This cheatsheet lists the directives you reach for most often when you write a .service or .timer file, plus the commands that install, override, and verify it.
File Locations
systemd reads units from several directories. /etc overrides /run, which overrides /usr/lib.
| Path | Description |
|---|---|
/etc/systemd/system/ | Units you write or override as the administrator |
/run/systemd/system/ | Runtime units, gone after a reboot |
/usr/lib/systemd/system/ | Units installed by packages; do not edit these |
/etc/systemd/system/name.service.d/*.conf | Drop-in overrides for one unit |
~/.config/systemd/user/ | Per-user units, managed with systemctl --user |
/etc/systemd/user/ | User units for every account on the system |
systemctl cat name.service | Print the unit and every drop-in that applies to it |
Unit Types
The file extension decides what kind of unit systemd creates.
| Extension | Description |
|---|---|
.service | A process or daemon |
.timer | Schedule that activates another unit |
.socket | Listening socket that starts a service on demand |
.target | Group of units, used as a boot milestone |
.path | Watches a file or directory and activates a unit on change |
.mount | Filesystem mount point |
.slice | Resource control group for other units |
[Unit] Section
Description, ordering, and dependencies.
| Directive | Description |
|---|---|
Description=My API server | Name shown by systemctl status |
Documentation=man:nginx(8) https://nginx.org/ | Documentation links |
After=network-online.target | Start after the named unit; ordering only |
Before=nginx.service | Start before the named unit |
Wants=network-online.target | Pull in a unit, keep running if it fails |
Requires=postgresql.service | Pull in a unit, stop if it is explicitly stopped |
BindsTo=dev-sdb1.device | Stop as soon as the named unit stops |
PartOf=app.target | Stop and restart together with the named unit |
Conflicts=shutdown.target | Stop the named unit when this one starts, and the reverse |
ConditionPathExists=/etc/app.conf | Skip the start quietly when the check fails |
AssertPathExists=/etc/app.conf | Fail the start when the check fails |
StartLimitIntervalSec=60 | Window for counting start attempts |
StartLimitBurst=5 | Starts allowed inside that window |
Wants= and Requires= do not set start order. Pair them with After= when one unit must be up first.
Service Types
Type= tells systemd when the service counts as started.
| Directive | Description |
|---|---|
Type=simple | Started as soon as the process is forked; the default |
Type=exec | Started once the binary has been executed successfully |
Type=forking | Started when the parent exits; the daemon forks itself |
PIDFile=/run/app.pid | PID file to track for a forking daemon |
Type=oneshot | Runs to completion; later units wait for it to exit |
RemainAfterExit=yes | Keep a oneshot unit shown as active after it exits |
Type=notify | Started when the process sends READY=1 via sd_notify |
Type=notify-reload | Like notify, and reloads by sending SIGHUP |
Type=dbus | Started when the name in BusName= appears on D-Bus |
Type=idle | Like simple, but waits until other boot jobs finish |
Exec Commands
Commands systemd runs at each stage of the service lifecycle.
| Directive | Description |
|---|---|
ExecStart=/usr/bin/app --port 8080 | Main command; use an absolute path |
ExecStartPre=/usr/bin/app --check-config | Run before ExecStart=; a failure stops the start |
ExecStartPost=/usr/bin/notify-ready | Run after the service has started |
ExecReload=/bin/kill -HUP $MAINPID | Run by systemctl reload |
ExecStop=/usr/bin/app --shutdown | Run by systemctl stop before the signal is sent |
ExecStopPost=/usr/bin/cleanup | Run after the service has stopped or failed |
ExecStartPre=-/usr/bin/mkdir /tmp/app | - prefix: ignore a non-zero exit code |
ExecStartPre=+/usr/bin/chown app /data | + prefix: run with full privileges, ignoring User= and sandboxing |
ExecStart=:/usr/bin/echo $HOME | : prefix: pass $ through without variable expansion |
Commands are not run through a shell, so pipes, >, and && do not work. Wrap them in /bin/sh -c '...' or move them into a script.
Restart and Timeouts
Control what happens when the process exits or hangs.
| Directive | Description |
|---|---|
Restart=on-failure | Restart on non-zero exit, signal, timeout, or watchdog |
Restart=always | Restart no matter how the process exited |
Restart=on-abnormal | Restart on signal, timeout, or watchdog, not on exit codes |
Restart=no | Never restart; the default |
RestartSec=5 | Wait five seconds before restarting |
RestartSteps=5 | Grow the delay over five restarts |
RestartMaxDelaySec=60 | Upper limit for the growing delay |
TimeoutStartSec=30 | Fail the start after 30 seconds |
TimeoutStopSec=30 | Escalate to SIGKILL after 30 seconds |
SuccessExitStatus=143 | Treat an extra exit code as a clean exit |
RestartPreventExitStatus=2 | Never restart after this exit code |
KillMode=mixed | SIGTERM to the main process, SIGKILL to the rest of the group |
WatchdogSec=30 | Mark the service failed when keep-alive pings stop |
A unit that restarts more than StartLimitBurst= times inside StartLimitIntervalSec= is refused further starts until the interval passes or you run systemctl reset-failed.
User, Environment, and Directories
Who the process runs as and what it sees.
| Directive | Description |
|---|---|
User=app | Run as this user instead of root |
Group=app | Run with this primary group |
DynamicUser=yes | Allocate a temporary user for each run |
WorkingDirectory=/opt/app | Directory the process starts in |
Environment="NODE_ENV=production" "PORT=8080" | Set variables inline |
EnvironmentFile=/etc/app/app.env | Read KEY=value lines from a file |
EnvironmentFile=-/etc/default/app | Same, but skip the file when it is missing |
UMask=0027 | Default permission mask for new files |
StateDirectory=app | Create /var/lib/app owned by the service user |
RuntimeDirectory=app | Create /run/app, removed when the service stops |
LogsDirectory=app | Create /var/log/app |
ConfigurationDirectory=app | Create /etc/app |
StandardOutput=journal | Send stdout to the journal; the default |
SyslogIdentifier=app | Tag used in journalctl output |
Resource Limits
Cap what one service can consume.
| Directive | Description |
|---|---|
LimitNOFILE=65536 | Maximum open files |
LimitNPROC=4096 | Maximum processes for the service user |
MemoryMax=512M | Hard memory limit; the OOM killer acts above it |
MemoryHigh=400M | Throttle and reclaim memory above this level |
CPUQuota=50% | Half of one CPU; 200% means two CPUs |
CPUWeight=50 | Relative CPU share; the default is 100 |
IOWeight=50 | Relative disk I/O share; the default is 100 |
TasksMax=100 | Maximum threads and processes |
Nice=10 | Scheduling priority, from -20 to 19 |
systemctl set-property app.service MemoryMax=1G | Change a limit at runtime and save it as a drop-in |
ulimit values set in a shell do not apply to services. Use the Limit*= directives instead.
Security Hardening
Sandboxing options that most services tolerate.
| Directive | Description |
|---|---|
NoNewPrivileges=yes | Block privilege gain through setuid binaries |
ProtectSystem=strict | Mount the whole file system read-only |
ProtectSystem=full | Mount /usr, /boot, and /etc read-only |
ReadWritePaths=/var/lib/app | Keep one path writable under ProtectSystem= |
ProtectHome=yes | Hide /home, /root, and /run/user |
ProtectHome=read-only | Show home directories, but read-only |
PrivateTmp=yes | Give the service its own /tmp and /var/tmp |
PrivateDevices=yes | Hide physical devices under /dev |
ProtectKernelTunables=yes | Make /proc/sys and /sys read-only |
ProtectKernelModules=yes | Block kernel module loading |
AmbientCapabilities=CAP_NET_BIND_SERVICE | Let a non-root user bind ports below 1024 |
CapabilityBoundingSet=CAP_NET_BIND_SERVICE | Drop every other capability |
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX | Allow only IP and Unix sockets |
SystemCallFilter=@system-service | Allow the system calls a typical service needs |
systemd-analyze security app.service | Score the unit’s exposure and list what is missing |
[Install] Section
Read only by systemctl enable and disable.
| Directive | Description |
|---|---|
WantedBy=multi-user.target | Start at boot on servers and desktops |
WantedBy=graphical.target | Start only when the graphical session target is reached |
WantedBy=default.target | Start with the user session, for --user units |
WantedBy=timers.target | Activate a timer at boot |
RequiredBy=app.target | Hard dependency created on enable |
Alias=api.service | Extra name created as a symlink on enable |
Also=app.socket | Enable or disable another unit together with this one |
A unit without an [Install] section can be started by hand but not enabled.
Timer Units
A name.timer activates name.service by default.
| Directive | Description |
|---|---|
OnCalendar=daily | Every day at midnight |
OnCalendar=*-*-* 02:30:00 | Every day at 02:30 |
OnCalendar=Mon..Fri 09:00 | Weekdays at 09:00 |
OnCalendar=*:0/15 | Every 15 minutes |
OnCalendar=*-*-01 00:00 | First day of every month |
OnBootSec=5min | Five minutes after boot |
OnUnitActiveSec=1h | One hour after the unit last started |
Persistent=true | Run a missed calendar event after downtime |
RandomizedDelaySec=10m | Spread the start by up to 10 minutes |
AccuracySec=1s | Tighten the default one-minute accuracy |
Unit=backup.service | Activate a unit with a different name |
systemd-analyze calendar "Mon..Fri 09:00" | Check an expression and show the next run |
systemctl list-timers | Show active timers and their next run |
Overrides and Drop-ins
Change a packaged unit without editing the file under /usr/lib.
| Command | Description |
|---|---|
sudo systemctl edit nginx.service | Create or edit a drop-in override |
sudo systemctl edit --full nginx.service | Copy the whole unit to /etc and edit it |
sudo systemctl revert nginx.service | Remove overrides and return to the vendor unit |
systemctl cat nginx.service | Show the unit with every drop-in applied |
systemd-delta | List units that are overridden or extended |
ExecStart= | Empty line in a drop-in clears the list before a new ExecStart= |
sudo systemctl daemon-reload | Reload unit files after a manual change |
systemctl edit reloads the configuration on save. Edits made with a normal editor need daemon-reload.
Specifiers
Placeholders systemd expands inside unit files.
| Specifier | Description |
|---|---|
%n | Full unit name, such as app@web.service |
%N | Unit name without the type suffix |
%i | Instance name in a template unit, the part after @ |
%H | Host name |
%h | Home directory of the user running the manager |
%u | User name of the manager |
%t | Runtime directory: /run or $XDG_RUNTIME_DIR |
%% | A literal percent sign |
A template file such as app@.service is started as app@web.service, with %i set to web.
Install and Verify
Put a new unit in place and confirm it loads.
| Command | Description |
|---|---|
sudo cp app.service /etc/systemd/system/ | Install the unit file |
systemd-analyze verify /etc/systemd/system/app.service | Check syntax and unknown directives |
sudo systemctl daemon-reload | Make systemd read the new file |
sudo systemctl enable --now app.service | Enable at boot and start now |
systemctl status app.service | Show state and the latest log lines |
journalctl -u app.service -f | Follow the service log |
systemctl show app.service -p Restart,User | Print the effective value of directives |
systemctl list-dependencies app.service | Show the dependency tree |
systemctl --user daemon-reload | Reload units in ~/.config/systemd/user/ |
sudo loginctl enable-linger username | Keep user units running after logout |
Related Guides
Deeper reading on services and systemd.
| Guide | Description |
|---|---|
| How to Create a systemd Service File | Write, install, and manage a service step by step |
| systemd Service File Generator | Build a .service file in the browser |
| systemctl Command in Linux | Start, stop, enable, and inspect units |
| How to List Services with systemctl | Filter units by state and type |
| journalctl Command in Linux | Read and filter service logs |
| systemctl cheatsheet | Quick reference for systemctl |
| journalctl cheatsheet | Quick reference for journalctl |