Writing a Custom systemd Service

Sometimes, we need to create a custom systemd service by hand to get our application up and running. For example, imagine you’ve just downloaded a shiny new Prometheus Node Exporter in a tarball. It doesn’t include a systemd unit file—so it’s time to create one manually.

Creating a custom systemd service allows you to automate the management of your own applications or scripts as native Linux services. In this article, we’ll walk through two common scenarios: a long-running application using Type=simple, and a one-time initialization task using Type=oneshot. We’ll also briefly revisit other service types for completeness.


Custom systemd Service Example 1: Type=simple

This type is suitable for programs that run in the foreground and do not fork into the background.

Use Case

Running a simple Python HTTP server.

Service Unit: /etc/systemd/system/simple-http.service

[Unit]
Description=Simple HTTP Server
After=network.target

[Service]
Type=simple
ExecStart=/usr/bin/python3 -m http.server 8080 --directory /var/www
Restart=always
User=www-data

[Install]
WantedBy=multi-user.target

Explanation

  • Type=simple: systemd considers the service started as soon as the process is launched.
  • ExecStart: runs the HTTP server directly.
  • Restart=always: restarts the service automatically if it crashes.

Enable and start the service:

sudo systemctl enable --now simple-http.service

Custom systemd Service Example 2: Type=oneshot

This type is used for short-lived scripts or commands that run once and then exit.

Use Case

Running a system initialization script to configure firewall rules.

Service Unit: /etc/systemd/system/setup-firewall.service

[Unit]
Description=Configure Firewall Rules
Before=network-pre.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/setup-firewall.sh
RemainAfterExit=true

[Install]
WantedBy=multi-user.target

Explanation

  • Type=oneshot: systemd waits for the command to complete before considering the service started.
  • RemainAfterExit=true: keeps the service marked as active even after the script exits.
    Why? While not strictly necessary, this setting makes systemctl status setup-firewall.service display the service as active (in a calm green color), which is a nice touch.

Enable and start the service:

sudo systemctl enable --now setup-firewall.service

Understanding the Difference: simple vs oneshot

FeatureType=simpleType=oneshot
Runs in background?Yes (long-running)No (short task)
Considered startedWhen the process startsWhen the command finishes
Restarts?Can be restarted (if configured)Not restarted (unless triggered by a timer)
Typical useWeb servers, daemonsScripts, initialization tasks

Other Service Types Available

To gain a complete understanding of systemd, it helps to know the other available service types.

Type=forking

Used when the service daemonizes itself (i.e., forks and runs in the background).

[Service]
Type=forking
ExecStart=/usr/sbin/mysqld
PIDFile=/var/run/mysqld.pid
  • PIDFile is required so systemd knows which process to monitor, as the original one forks.

Type=notify

Used when a service needs time to initialize and must signal when it’s ready using systemd-notify.

[Service]
Type=notify
ExecStart=/usr/local/bin/complex-app
NotifyAccess=all

Type=dbus

Used for services that register on D-Bus. The service is considered started once the specified BusName is acquired.

[Service]
Type=dbus
BusName=org.freedesktop.NetworkManager
ExecStart=/usr/sbin/NetworkManager

Final Thoughts

Choosing the correct Type= for your systemd service ensures that systemd can properly manage the lifecycle of your application or script. Use simple for persistent, foreground applications, and oneshot for short-lived setup tasks. Other types exist for legacy daemons, asynchronous services, or D-Bus-integrated components.

Designing your services with the right type in mind leads to smoother system integration, easier debugging, and greater reliability.

** Related documentation can be found here.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top