Home

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:

ini
# 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.

[Service]

How to run the process.

[Install]

How systemctl enable should hook the service into the boot process.

For context, here's how runlevels map to systemd targets on Ubuntu:

bash
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.target

Shipping your own service

  1. Drop your unit file under /etc/systemd/system/. The filename ends in .service.
  2. Tell systemd to re-read its config:
bash
sudo systemctl daemon-reload
  1. Enable it to start at boot:
bash
sudo systemctl enable my-service.service

To reverse that:

bash
sudo systemctl disable my-service.service
  1. Start it now:
bash
sudo systemctl start my-service.service

Tail the logs while you debug:

bash
journalctl -u my-service.service -f

References