Writing systemd unit files

Linux · Advanced · 14 min

A proper unit file gives your app automatic restarts, logging, boot-time start and security sandboxing for free.

Custom units go in /etc/systemd/system/name.service. A service unit has three sections:

  • [Unit]: Description=, and ordering/dependencies such as After=network-online.target with Wants=network-online.target, or Requires=postgresql.service.
  • [Service]: how to run it. ExecStart= needs an absolute path and runs the program directly, without a shell, so pipes and $(...) do not work unless you call /bin/sh -c. Type=simple (default: the process stays in the foreground), notify (the program signals readiness) or oneshot (scripts that exit). Add User=, WorkingDirectory=, Environment= or EnvironmentFile=, and Restart=on-failure with RestartSec=5.
  • [Install]: WantedBy=multi-user.target makes enable start it at boot.

After creating or editing a unit run systemctl daemon-reload. To change a unit shipped by a package, do not edit it in /usr/lib/systemd/system: run systemctl edit name, which creates a drop-in override in /etc/systemd/system/name.service.d/.

systemd can sandbox a service with a few lines: NoNewPrivileges=yes, ProtectSystem=strict (read-only filesystem except ReadWritePaths=), ProtectHome=yes, PrivateTmp=yes, or DynamicUser=yes for a throwaway user. systemd-analyze security name scores how exposed a service is. The app should log to stdout/stderr and let the journal handle storage.

Example

# /etc/systemd/system/myapp.service
[Unit]
Description=My web app
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=myapp
WorkingDirectory=/opt/myapp
EnvironmentFile=/etc/myapp/env
ExecStart=/opt/myapp/bin/server --port 8080
Restart=on-failure
RestartSec=5
NoNewPrivileges=yes
ProtectSystem=strict
ReadWritePaths=/var/lib/myapp
PrivateTmp=yes

[Install]
WantedBy=multi-user.target
# Then: sudo systemctl daemon-reload && sudo systemctl enable --now myapp
# Override a packaged unit instead of editing it: sudo systemctl edit nginx

Remember

  • [Unit] ordering, [Service] how to run, [Install] when to start
  • ExecStart runs without a shell; daemon-reload after edits
  • Use drop-ins via systemctl edit and add sandboxing options

Watch out for

  • Writing ExecStart=cd /opt/app && ./server: there is no shell, so && and cd do not work.

Check yourself

  1. You edited /etc/systemd/system/myapp.service. What must you run before restarting it?
  2. How should you customise a unit file installed by a package?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: www.freedesktop.org/software/systemd/man/latest/systemd.service.html