Skip to main content

systemd Unit File Cheatsheet

By Dejan Panovski •Updated on • Download PDF

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.

PathDescription
/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/*.confDrop-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.servicePrint the unit and every drop-in that applies to it

Unit Types

The file extension decides what kind of unit systemd creates.

ExtensionDescription
.serviceA process or daemon
.timerSchedule that activates another unit
.socketListening socket that starts a service on demand
.targetGroup of units, used as a boot milestone
.pathWatches a file or directory and activates a unit on change
.mountFilesystem mount point
.sliceResource control group for other units

[Unit] Section

Description, ordering, and dependencies.

DirectiveDescription
Description=My API serverName shown by systemctl status
Documentation=man:nginx(8) https://nginx.org/Documentation links
After=network-online.targetStart after the named unit; ordering only
Before=nginx.serviceStart before the named unit
Wants=network-online.targetPull in a unit, keep running if it fails
Requires=postgresql.servicePull in a unit, stop if it is explicitly stopped
BindsTo=dev-sdb1.deviceStop as soon as the named unit stops
PartOf=app.targetStop and restart together with the named unit
Conflicts=shutdown.targetStop the named unit when this one starts, and the reverse
ConditionPathExists=/etc/app.confSkip the start quietly when the check fails
AssertPathExists=/etc/app.confFail the start when the check fails
StartLimitIntervalSec=60Window for counting start attempts
StartLimitBurst=5Starts 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.

DirectiveDescription
Type=simpleStarted as soon as the process is forked; the default
Type=execStarted once the binary has been executed successfully
Type=forkingStarted when the parent exits; the daemon forks itself
PIDFile=/run/app.pidPID file to track for a forking daemon
Type=oneshotRuns to completion; later units wait for it to exit
RemainAfterExit=yesKeep a oneshot unit shown as active after it exits
Type=notifyStarted when the process sends READY=1 via sd_notify
Type=notify-reloadLike notify, and reloads by sending SIGHUP
Type=dbusStarted when the name in BusName= appears on D-Bus
Type=idleLike simple, but waits until other boot jobs finish

Exec Commands

Commands systemd runs at each stage of the service lifecycle.

DirectiveDescription
ExecStart=/usr/bin/app --port 8080Main command; use an absolute path
ExecStartPre=/usr/bin/app --check-configRun before ExecStart=; a failure stops the start
ExecStartPost=/usr/bin/notify-readyRun after the service has started
ExecReload=/bin/kill -HUP $MAINPIDRun by systemctl reload
ExecStop=/usr/bin/app --shutdownRun by systemctl stop before the signal is sent
ExecStopPost=/usr/bin/cleanupRun 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.

DirectiveDescription
Restart=on-failureRestart on non-zero exit, signal, timeout, or watchdog
Restart=alwaysRestart no matter how the process exited
Restart=on-abnormalRestart on signal, timeout, or watchdog, not on exit codes
Restart=noNever restart; the default
RestartSec=5Wait five seconds before restarting
RestartSteps=5Grow the delay over five restarts
RestartMaxDelaySec=60Upper limit for the growing delay
TimeoutStartSec=30Fail the start after 30 seconds
TimeoutStopSec=30Escalate to SIGKILL after 30 seconds
SuccessExitStatus=143Treat an extra exit code as a clean exit
RestartPreventExitStatus=2Never restart after this exit code
KillMode=mixedSIGTERM to the main process, SIGKILL to the rest of the group
WatchdogSec=30Mark 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.

DirectiveDescription
User=appRun as this user instead of root
Group=appRun with this primary group
DynamicUser=yesAllocate a temporary user for each run
WorkingDirectory=/opt/appDirectory the process starts in
Environment="NODE_ENV=production" "PORT=8080"Set variables inline
EnvironmentFile=/etc/app/app.envRead KEY=value lines from a file
EnvironmentFile=-/etc/default/appSame, but skip the file when it is missing
UMask=0027Default permission mask for new files
StateDirectory=appCreate /var/lib/app owned by the service user
RuntimeDirectory=appCreate /run/app, removed when the service stops
LogsDirectory=appCreate /var/log/app
ConfigurationDirectory=appCreate /etc/app
StandardOutput=journalSend stdout to the journal; the default
SyslogIdentifier=appTag used in journalctl output

Resource Limits

Cap what one service can consume.

DirectiveDescription
LimitNOFILE=65536Maximum open files
LimitNPROC=4096Maximum processes for the service user
MemoryMax=512MHard memory limit; the OOM killer acts above it
MemoryHigh=400MThrottle and reclaim memory above this level
CPUQuota=50%Half of one CPU; 200% means two CPUs
CPUWeight=50Relative CPU share; the default is 100
IOWeight=50Relative disk I/O share; the default is 100
TasksMax=100Maximum threads and processes
Nice=10Scheduling priority, from -20 to 19
systemctl set-property app.service MemoryMax=1GChange 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.

DirectiveDescription
NoNewPrivileges=yesBlock privilege gain through setuid binaries
ProtectSystem=strictMount the whole file system read-only
ProtectSystem=fullMount /usr, /boot, and /etc read-only
ReadWritePaths=/var/lib/appKeep one path writable under ProtectSystem=
ProtectHome=yesHide /home, /root, and /run/user
ProtectHome=read-onlyShow home directories, but read-only
PrivateTmp=yesGive the service its own /tmp and /var/tmp
PrivateDevices=yesHide physical devices under /dev
ProtectKernelTunables=yesMake /proc/sys and /sys read-only
ProtectKernelModules=yesBlock kernel module loading
AmbientCapabilities=CAP_NET_BIND_SERVICELet a non-root user bind ports below 1024
CapabilityBoundingSet=CAP_NET_BIND_SERVICEDrop every other capability
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIXAllow only IP and Unix sockets
SystemCallFilter=@system-serviceAllow the system calls a typical service needs
systemd-analyze security app.serviceScore the unit’s exposure and list what is missing

[Install] Section

Read only by systemctl enable and disable.

DirectiveDescription
WantedBy=multi-user.targetStart at boot on servers and desktops
WantedBy=graphical.targetStart only when the graphical session target is reached
WantedBy=default.targetStart with the user session, for --user units
WantedBy=timers.targetActivate a timer at boot
RequiredBy=app.targetHard dependency created on enable
Alias=api.serviceExtra name created as a symlink on enable
Also=app.socketEnable 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.

DirectiveDescription
OnCalendar=dailyEvery day at midnight
OnCalendar=*-*-* 02:30:00Every day at 02:30
OnCalendar=Mon..Fri 09:00Weekdays at 09:00
OnCalendar=*:0/15Every 15 minutes
OnCalendar=*-*-01 00:00First day of every month
OnBootSec=5minFive minutes after boot
OnUnitActiveSec=1hOne hour after the unit last started
Persistent=trueRun a missed calendar event after downtime
RandomizedDelaySec=10mSpread the start by up to 10 minutes
AccuracySec=1sTighten the default one-minute accuracy
Unit=backup.serviceActivate a unit with a different name
systemd-analyze calendar "Mon..Fri 09:00"Check an expression and show the next run
systemctl list-timersShow active timers and their next run

Overrides and Drop-ins

Change a packaged unit without editing the file under /usr/lib.

CommandDescription
sudo systemctl edit nginx.serviceCreate or edit a drop-in override
sudo systemctl edit --full nginx.serviceCopy the whole unit to /etc and edit it
sudo systemctl revert nginx.serviceRemove overrides and return to the vendor unit
systemctl cat nginx.serviceShow the unit with every drop-in applied
systemd-deltaList units that are overridden or extended
ExecStart=Empty line in a drop-in clears the list before a new ExecStart=
sudo systemctl daemon-reloadReload 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.

SpecifierDescription
%nFull unit name, such as app@web.service
%NUnit name without the type suffix
%iInstance name in a template unit, the part after @
%HHost name
%hHome directory of the user running the manager
%uUser name of the manager
%tRuntime 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.

CommandDescription
sudo cp app.service /etc/systemd/system/Install the unit file
systemd-analyze verify /etc/systemd/system/app.serviceCheck syntax and unknown directives
sudo systemctl daemon-reloadMake systemd read the new file
sudo systemctl enable --now app.serviceEnable at boot and start now
systemctl status app.serviceShow state and the latest log lines
journalctl -u app.service -fFollow the service log
systemctl show app.service -p Restart,UserPrint the effective value of directives
systemctl list-dependencies app.serviceShow the dependency tree
systemctl --user daemon-reloadReload units in ~/.config/systemd/user/
sudo loginctl enable-linger usernameKeep user units running after logout

Deeper reading on services and systemd.

GuideDescription
How to Create a systemd Service FileWrite, install, and manage a service step by step
systemd Service File GeneratorBuild a .service file in the browser
systemctl Command in LinuxStart, stop, enable, and inspect units
How to List Services with systemctlFilter units by state and type
journalctl Command in LinuxRead and filter service logs
systemctl cheatsheetQuick reference for systemctl
journalctl cheatsheetQuick reference for journalctl