Skip to content

systemd unit directives

Synopsis

# /etc/systemd/system/<NAME>.service
[Unit]
Description=<one-line description>
Documentation=man:<page>(<section>) https://example.com/docs
Requires=<unit> …
Wants=<unit> …
After=<unit> …
Before=<unit> …
Conflicts=<unit> …
ConditionPathExists=[!]<absolute path>

[Service]
Type=simple|exec|forking|oneshot|dbus|notify|notify-reload|idle
ExecStartPre=[-+]<command>
ExecStart=<command>
ExecReload=<command>
ExecStop=<command>
RemainAfterExit=yes|no
TimeoutStartSec=<time span>
TimeoutStopSec=<time span>
KillMode=control-group|mixed|process|none
Restart=no|on-success|on-failure|on-abnormal|on-watchdog|on-abort|always
RestartSec=<time span>
User=<user>
Group=<group>
WorkingDirectory=[-]<absolute path>|~
Environment="<VAR>=<value>" …
EnvironmentFile=[-]<absolute path>
StandardOutput=journal|null|inherit|tty|kmsg|file:<path>|append:<path>|…

[Install]
WantedBy=<unit> …
RequiredBy=<unit> …
Alias=<name> …

<NAME> is the unit name without its suffix; the other placeholders are described with each directive below.

Description

A systemd unit file describes one object that systemd manages: a service, socket, timer, mount, target and so on. The file's suffix gives the unit type (.service, .target, .timer, …). Unit files use an INI-like syntax: section headers in square brackets, then Key=value lines. Lines starting with # or ; are comments, and a line ending in \ continues on the next line.

Settings fall into three groups:

  • [Unit]: generic settings shared by every unit type, mainly description, dependencies, ordering and conditions (systemd.unit(5)).
  • A type-specific section, such as [Service] for .service units (systemd.service(5)). Settings about the process environment, such as User= and Environment=, are shared by every unit type that starts processes and are documented in systemd.exec(5).
  • [Install]: used by systemctl enable, disable, preset and is-enabled (and when re-enabling a unit), never when the unit runs.

Unless a directive below says otherwise, a directive that takes a list may be repeated, each occurrence adding to the list, and assigning an empty value (Wants=) resets the list built up so far. Time spans are a number of seconds (5) or a value with units (500ms, 2min 30s).

This page was checked against systemd 257 (Debian 13, AlmaLinux 10 and Rocky Linux 10), 259 (Ubuntu 26.04) and 262 (Arch), and the systemd.unit(5), systemd.service(5) and systemd.exec(5) man pages. Every directive on this page is available in all of these versions. Gentoo with OpenRC, its default, doesn't use unit files; see OpenRC and other init systems.

[Unit] section

Directive Value Description
Description= Free text Short human-readable name, shown by systemctl status and in boot messages.
Documentation= Space-separated URIs Where to read more. Accepted schemes: http://, https://, file:, info: and man:. Shown by systemctl status and systemctl help.
Wants= Unit names Weak requirement: starting this unit also starts the listed units, but this unit still starts if they fail or are missing. Equivalent to a symlink in a <NAME>.service.wants/ directory.
Requires= Unit names Strong requirement: the listed units are started too, and this unit is stopped or restarted when one of them is explicitly stopped or restarted. If a listed unit fails to start and this unit is also ordered After= it, this unit isn't started. Equivalent to a symlink in <NAME>.service.requires/.
After= Unit names Ordering only: if both units are being started, this unit waits until the listed units have finished starting. When shutting down, the order is reversed. Doesn't cause the listed units to start.
Before= Unit names The inverse of After=: the listed units wait until this unit has finished starting.
Conflicts= Unit names Negative requirement: starting this unit stops the listed units, and starting one of them stops this unit. Implies no ordering; add After= or Before= if order matters.
ConditionPathExists= Absolute path, optionally prefixed with ! The unit only starts if the path exists (or, with !, doesn't exist). A failed condition skips the unit silently: it's not an error and doesn't mark the unit failed. Related checks include ConditionPathIsDirectory=, ConditionFileNotEmpty= and the Assert…= variants, which make the start fail instead of skipping it.

Dependencies (Wants=, Requires=, Conflicts=) and ordering (After=, Before=) are independent. A unit listed in Wants= without After= starts in parallel with the unit that wants it.

Restart rate limiting is also configured in [Unit]: StartLimitIntervalSec= and StartLimitBurst= set how many starts (including automatic restarts) are allowed in a time window before systemd stops trying and leaves the unit failed. Their defaults come from DefaultStartLimitIntervalSec= and DefaultStartLimitBurst= in systemd-system.conf(5) (10 seconds and 5 starts unless changed).

[Service] section

Directive Value Description
Type= simple, exec, forking, oneshot, dbus, notify, notify-reload, idle How systemd decides the service has finished starting. See the table below. Defaults to simple when ExecStart= is set (and to oneshot when it isn't).
ExecStart= Command line The main command. Exactly one is required, except with Type=oneshot, which allows several (run in order) or none.
ExecStartPre= Command line Commands run, in order, before ExecStart=. If one fails, the service fails and ExecStart= doesn't run, unless the command is prefixed with -. ExecStartPost= runs commands after the start has succeeded.
ExecReload= Command line Command run by systemctl reload. Without it, the service can't be reloaded, only restarted. A common form is ExecReload=kill -HUP $MAINPID.
ExecStop= Command line Commands run by systemctl stop, only if the service started successfully. Afterwards, any remaining processes are killed according to KillMode=. Without ExecStop=, systemd sends SIGTERM (or the signal in KillSignal=) straight away. The command should wait until the service has actually stopped.
RemainAfterExit= Boolean Whether the service counts as active after all its processes have exited. Default no. Mostly used with Type=oneshot.
TimeoutStartSec= Time span, or infinity How long to wait for the service to finish starting before treating it as failed and stopping it. Default DefaultTimeoutStartSec= from systemd-system.conf(5) (90 seconds unless changed); disabled by default for Type=oneshot.
TimeoutStopSec= Time span, or infinity How long to wait for each ExecStop= command and then for the service to exit, before sending SIGKILL. Default DefaultTimeoutStopSec= (90 seconds unless changed).
KillMode= control-group, mixed, process, none Which processes are killed on stop. control-group (default): every process left in the unit's control group. mixed: SIGTERM to the main process, then SIGKILL to all remaining processes. process: only the main process. none: none, only ExecStop= runs. process and none let processes escape systemd's control and are discouraged upstream.
Restart= no, on-success, on-failure, on-abnormal, on-watchdog, on-abort, always When to restart the service after its process exits, is killed or times out. Default no. Stopping the service with systemctl stop or restart never triggers an automatic restart. See the table below.
RestartSec= Time span How long to wait before an automatic restart. Default 100 ms.
User= User name or numeric UID Run the service's processes as this user. Default root for system services.
Group= Group name or numeric GID Run as this group. Defaults to the primary group of User=.
WorkingDirectory= Absolute path, or ~ Working directory for the processes. ~ means the home directory of User=. Prefix with - to make a missing directory non-fatal. Default / for system services.
Environment= One or more VAR=value assignments Set environment variables. Quote the whole assignment if the value contains spaces: Environment="GREETING=hello world". No shell-style expansion happens in the value.
EnvironmentFile= Absolute path, optionally prefixed with - Read VAR=value lines from a file. Empty lines and lines starting with # or ; are ignored. The file is read each time the service starts. With -, a missing file is ignored; otherwise it's an error. Settings from EnvironmentFile= override Environment= settings for the same variable.
StandardOutput= journal, null, inherit, tty, kmsg, journal+console, kmsg+console, file:<path>, append:<path>, truncate:<path>, socket, fd:<name> Where the processes' standard output goes. Default DefaultStandardOutput= from systemd-system.conf(5), normally journal. StandardError= takes the same values and defaults to following standard output.

Type= values

Value systemd considers the service started…
simple Immediately after forking the process, before the program has run.
exec After the program has been executed successfully (so a missing binary or wrong user makes systemctl start fail).
forking When the started process exits, having forked a background daemon. Use PIDFile= to point systemd at the daemon's PID.
oneshot When the process exits successfully. Used for tasks that run once rather than daemons; combine with RemainAfterExit=yes to keep the unit active afterwards.
dbus When the service has acquired the D-Bus name in BusName=.
notify When the program sends READY=1 through sd_notify(3).
notify-reload Like notify, and the program also supports reloading through a signal (SIGHUP by default) with readiness notification.
idle Like simple, but the program is delayed until other start-up jobs have finished (at most a few seconds), to keep console output tidy.

Restart= values

"Clean" exit means exit code 0, one of the signals SIGHUP, SIGINT, SIGTERM or SIGPIPE (for types other than oneshot), or a status listed in SuccessExitStatus=.

Cause of exit no always on-success on-failure on-abnormal on-abort on-watchdog
Clean exit Restart Restart
Unclean exit code Restart Restart
Unclean signal Restart Restart Restart Restart
Timeout Restart Restart Restart
Watchdog Restart Restart Restart Restart

Command lines

ExecStart=, ExecStartPre=, ExecReload= and the other Exec…= directives take a command and its arguments, not a shell command line. Redirection (>), pipes (|), && and background & are not interpreted; to use them, run a shell explicitly, for example ExecStart=/bin/sh -c 'exec <command> >>/var/log/<file>'. $VAR and ${VAR} are replaced with environment variables set by Environment= or EnvironmentFile=; $VAR on its own is split into several arguments at whitespace, ${VAR} is kept as one. The command should be an absolute path; a bare name is looked up in a fixed list of system directories, not the user's PATH.

The command may be prefixed with special characters, including:

Prefix Effect
- A failing exit status is recorded but treated as success.
+ Run with full privileges, ignoring User=, Group= and sandboxing settings for this command only.
: Don't substitute environment variables in this command line.
@ The second word becomes argv[0] of the process.

[Install] section

[Install] describes what systemctl enable does; systemctl disable undoes it. Enabling creates symlinks in /etc/systemd/system/; it doesn't start the unit (add --now to also start it).

Directive Value Description
WantedBy= Unit names systemctl enable creates a symlink in <UNIT>.wants/ for each listed unit, which adds a Wants= dependency from that unit to this one. For services that should start at boot, the usual value is multi-user.target.
RequiredBy= Unit names As WantedBy=, but creates the symlink in <UNIT>.requires/, adding a Requires= dependency.
Alias= Unit names with the same suffix Additional names for the unit. systemctl enable creates a symlink with each name in /etc/systemd/system/, after which the unit can be started, stopped and referred to by that name. Aliases are only active while the unit is enabled.

Other [Install] directives are Also= (enable or disable other units together with this one) and DefaultInstance= (for template units).

Files

Unit file locations

For the system manager, systemd searches these directories, from highest to lowest precedence. When a unit file with the same name exists in more than one, the first one found is used and the others are ignored entirely.

Path Used for
/etc/systemd/system.control/, /run/systemd/system.control/ Settings made with systemctl set-property
/run/systemd/transient/ Transient units created at run time, for example by systemd-run
/run/systemd/generator.early/ Units generated at boot, high precedence
/etc/systemd/system/ Units created by the administrator, and the symlinks made by systemctl enable
/etc/systemd/system.attached/ Units from persistently attached portable services
/run/systemd/system/ Runtime units, lost at reboot
/run/systemd/system.attached/ Units from portable services attached until the next reboot
/run/systemd/generator/ Units generated at boot, for example .mount units from /etc/fstab
/usr/local/lib/systemd/system/ Units installed by the administrator outside the package manager
/usr/lib/systemd/system/ Units installed by packages (on Debian and Ubuntu, /lib is a symlink to /usr/lib, so /lib/systemd/system/ is the same directory)
/run/systemd/generator.late/ Units generated at boot, low precedence

Never edit files under /usr/lib/systemd/system/: package updates overwrite them. Override them in /etc/systemd/system/ instead, either with a complete replacement file of the same name or with a drop-in.

Drop-ins

A drop-in directory <NAME>.service.d/ next to (or in any search path as) a unit holds .conf files whose settings are applied on top of the unit file. Drop-ins change individual settings without copying the whole file, so package updates to the rest of the unit still take effect.

  • Drop-in files are read in lexicographic order of their file names across all search directories; a later file overrides an earlier one. If two directories contain a drop-in with the same file name, only the one in the higher-precedence directory is used.
  • A drop-in must repeat the section header for the settings it changes ([Service]).
  • Settings that take a list (ExecStart=, Environment=, Wants=, …) are added to, not replaced. To replace one, clear it first with an empty assignment, then set it:

    # /etc/systemd/system/<NAME>.service.d/override.conf
    [Service]
    ExecStart=
    ExecStart=/usr/local/bin/<NAME> --verbose
    
  • Top-level drop-ins apply to every unit of a type, for example /etc/systemd/system/service.d/*.conf for all services. They have lower precedence than drop-ins for a specific unit.

  • systemctl edit <NAME>.service opens an editor on /etc/systemd/system/<NAME>.service.d/override.conf and reloads systemd on save. systemctl edit --full edits a full copy of the unit in /etc/systemd/system/ instead.

Seeing the result

Command Shows
systemctl cat <NAME>.service The unit file in use and every drop-in, each preceded by its path
systemctl show <NAME>.service -p <PROPERTY> The effective value of one setting after all files are merged
systemd-delta Units and drop-ins that override, extend or mask others
systemd-analyze verify <FILE> Syntax and dependency errors in a unit file
systemctl daemon-reload Not a view: makes systemd re-read all unit files. Needed after creating or changing unit files by hand.

Examples

A minimal long-running service, run as a dedicated heartbeat user, that takes its interval from a variable:

# /etc/systemd/system/heartbeat.service
[Unit]
Description=Example heartbeat writer

[Service]
Type=exec
User=heartbeat
Group=heartbeat
Environment=HEARTBEAT_INTERVAL=10
ExecStart=/usr/local/bin/heartbeat.sh ${HEARTBEAT_INTERVAL}
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

A drop-in that changes the interval and the restart delay without editing the unit file. The later assignment of HEARTBEAT_INTERVAL replaces the one in the unit:

# /etc/systemd/system/heartbeat.service.d/10-interval.conf
[Service]
Environment=HEARTBEAT_INTERVAL=30
RestartSec=10

After systemctl daemon-reload and a restart of the service, systemctl show heartbeat.service -p Environment shows HEARTBEAT_INTERVAL=30.

A full worked example is in How to run a script as a service.

See also