How to create a systemd service on Linux
systemd is now the init system on basically every mainstream Linux distro. There are a handful of holdouts (Alpine's OpenRC, Devuan's sysvinit, Void's runit), but they're niche. Ubuntu, Fedora, RHEL, Debian, Pop!_OS and Arch all ship systemd by default.
Because systemd is everywhere, knowing how to write a service for it is a basic skill. This is a walk through the file format, plus the commands to install and enable your own.
What is a systemd service?
A service is a process (or group of processes) started, stopped, and supervised by systemd according to rules you define. You don't run them by hand — you tell systemd what they are and when to bring them up. A unit file is the small declarative file that describes the service in a format systemd understands.
Anatomy of a unit file
A systemd unit file has three main sections: [Unit], [Service], and [Install]. The file extension is .service, and # starts a comment. Here's Nginx's:
# nginx signals reference doc:# http://nginx.org/en/docs/control.html[Unit]Description=A high performance web server and a reverse proxy serverAfter=syslog.target network.target remote-fs.target nss-lookup.target[Service]Type=forkingPIDFile=/run/nginx.pidExecStartPre=/usr/sbin/nginx -tExecStart=/usr/sbin/nginxExecReload=/bin/kill -s HUP $MAINPIDExecStop=/bin/kill -s QUIT $MAINPIDPrivateTmp=true[Install]WantedBy=multi-user.target[Unit]
Metadata and ordering hints.
- Description — short human-readable description.
- After — start this service after the listed targets/services. In the example, Nginx waits for the network. Use this to express "I depend on X being up first."
- Before — the inverse: "I should be up before X." Useful when something downstream depends on yours, e.g. you have a custom service that's started by Nginx as a backend.
[Service]
How to run the process.
- ExecStart — the command to start the service.
- ExecReload — optional; how to reload (used by
systemctl reload <name>). - Type — process model:
simple,exec,forking,oneshot,dbus,notify,idle. Most modern services that don't fork should usesimple.forkingis for traditional daemons that double-fork into the background. - Restart — when to restart on exit:
no,on-success,on-failure,always.on-failureis the most common pick — it'll bring the service back up if it crashes but won't loop on a clean exit.
[Install]
How systemctl enable should hook the service into the boot process.
- WantedBy — which target should pull in this service when it activates. The most common value is
multi-user.target— the equivalent of "the system is booted to a multi-user state." - RequiredBy — stronger version of
WantedBy; if this service fails to start, the target it's required by also fails.
For context, here's how runlevels map to systemd targets on Ubuntu:
ls -al /lib/systemd/system/runlevel*lrwxrwxrwx 1 root root 15 Sep 8 12:58 /lib/systemd/system/runlevel0.target -> poweroff.targetlrwxrwxrwx 1 root root 13 Sep 8 12:58 /lib/systemd/system/runlevel1.target -> rescue.targetlrwxrwxrwx 1 root root 17 Sep 8 12:58 /lib/systemd/system/runlevel2.target -> multi-user.targetlrwxrwxrwx 1 root root 17 Sep 8 12:58 /lib/systemd/system/runlevel3.target -> multi-user.targetlrwxrwxrwx 1 root root 17 Sep 8 12:58 /lib/systemd/system/runlevel4.target -> multi-user.targetlrwxrwxrwx 1 root root 16 Sep 8 12:58 /lib/systemd/system/runlevel5.target -> graphical.targetlrwxrwxrwx 1 root root 13 Sep 8 12:58 /lib/systemd/system/runlevel6.target -> reboot.targetShipping your own service
- Drop your unit file under
/etc/systemd/system/. The filename ends in.service. - Tell systemd to re-read its config:
sudo systemctl daemon-reload- Enable it to start at boot:
sudo systemctl enable my-service.serviceTo reverse that:
sudo systemctl disable my-service.service- Start it now:
sudo systemctl start my-service.serviceTail the logs while you debug:
journalctl -u my-service.service -f