systemd Services
We use systemd services to run a variety of binaries, scripts etc. These are
configured by /etc/systemd/system/*.service files. The structure of these
depends on the service in question but there are a number of commonalities and
best practices you should keep in mind. We list these here by file section. The
various options are documented in the systemd man pages.
-
[Unit]: At minimum you're going to want to add aDescription. Optionally, useAfterto declare ordering dependencies andWants/Requiresfor soft/hard dependencies. A very common pattern is e.g. the following for services that require a network connection:Description=My program After=network-online.target Wants=network-online.target -
[Service]: - Add a
Type, usuallyexecwhich is preferable oversimpleon systemd versions that support it for better startup error reporting. - Add
User/Groupfor services that don't absolutely need to be run as root. - Specify the command to run (absolute path) via
ExecStart. Binaries should usually be located under/usr/local/bin. - If your service can be reloaded via a mechanism such as SIGHUP, specify this
via
ExecReload, very commonly:ExecReload=/bin/kill -HUP $MAINPID. - You are almost always going to want to add
Restart=on-failure(to keep the service up but still allow for intentional stopping) and aRestartSecif the 100ms default could cause crashloops that are too tight. A reasonable number is 5 seconds unless you need the service to come back up asap after a crash. - If your program needs environment files to be set, do this via
EnvironmentFileinstead of putting them into the service file itself. This should usually be/etc/default/my-program. - You should likely set
WorkingDirectoryjust in case, if your program reads/writes files via relative paths things will break otherwise. This should usually be/var/lib/my-program. -
Add basic security hardening flags, protecting the file system with
ProtectSystem=strict(whitelisting write-paths withReadWritePaths)ProtectHome=true,PrivateTmp=trueand restricting the service's access to the system viaNoNewPrivileges=true,RestrictSUIDSGID=true,ProtectKernelTunables=trueandProtectControlGroups=true. -
[Install]: Unless you have some very specific reason not to, just useWantedBy=multi-user.target.
Here is a full example:
[Unit]
Description=My program
After=network-online.target
Wants=network-online.target
[Service]
Type=exec
User=my-program-user
Group=my-program-user
ExecStart=/usr/local/bin/my-program \
-some-flag \
-some-other-flag
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=5
EnvironmentFile=/etc/default/my-program
WorkingDirectory=/var/lib/my-program
ProtectSystem=strict
ReadWritePaths=/var/lib/my-program
ProtectHome=true
PrivateTmp=true
NoNewPrivileges=true
RestrictSUIDSGID=true
ProtectKernelTunables=true
ProtectControlGroups=true
[Install]
WantedBy=multi-user.target