systemd is the init system and service manager for most modern Linux distributions. It’s responsible for starting, stopping, and managing services (daemon processes) during the boot process and throughout the system’s runtime. Think of it as the conductor of an orchestra, ensuring all the right instruments (services) start at the right time and play their part correctly.

Let’s see systemd in action by creating a simple service that just logs a message every few seconds.

First, we need to create a script that our service will run. Let’s call it /usr/local/bin/my-logging-script.sh:

#!/bin/bash
echo "My custom service is running at $(date)" >> /var/log/my-custom-service.log

Make sure this script is executable:

sudo chmod +x /usr/local/bin/my-logging-script.sh

Now, we create a systemd service unit file. These files define how systemd should manage a service. We’ll put this in /etc/systemd/system/my-custom-service.service:

[Unit]
Description=My Custom Logging Service
After=network.target

[Service]
ExecStart=/usr/local/bin/my-logging-script.sh
Restart=always
User=root
Group=root

[Install]
WantedBy=multi-user.target

Let’s break this down:

  • [Unit] Section:

    • Description: A human-readable name for your service.
    • After=network.target: This tells systemd that our service should only start after the network is up. This is good practice if your service relies on networking.
  • [Service] Section:

    • ExecStart=/usr/local/bin/my-logging-script.sh: This is the command systemd will run to start your service.
    • Restart=always: This is a crucial directive. It tells systemd to automatically restart the service if it crashes or exits for any reason. This makes your service resilient.
    • User=root and Group=root: Specifies the user and group the service should run as. For simplicity, we’re using root, but in production, you’d typically create a dedicated, unprivileged user.
  • [Install] Section:

    • WantedBy=multi-user.target: This specifies when the service should be enabled. multi-user.target is the standard runlevel for a system with multiple users, typically after booting into a text-based multi-user environment. When you systemctl enable, systemd creates a symbolic link from multi-user.target.wants/ to your service file.

After creating the service file, we need to tell systemd to reload its configuration so it knows about our new service:

sudo systemctl daemon-reload

Now, let’s start our service:

sudo systemctl start my-custom-service.service

You can check its status:

sudo systemctl status my-custom-service.service

You should see output indicating it’s "active (running)".

To see it in action, check the log file:

tail /var/log/my-custom-service.log

You’ll see lines like: My custom service is running at Mon Oct 26 10:30:00 UTC 2023 My custom service is running at Mon Oct 26 10:30:05 UTC 2023 …and so on, every 5 seconds or so (depending on your script’s execution time and system load).

To make the service start automatically on boot, we enable it:

sudo systemctl enable my-custom-service.service

This command creates the symbolic link mentioned earlier.

To stop the service:

sudo systemctl stop my-custom-service.service

And to disable it from starting on boot:

sudo systemctl disable my-custom-service.service

The most surprising true thing about systemd is that it doesn’t just manage services; it’s a "system and service manager" that orchestrates the entire boot process, not just the services that run after boot. It uses a dependency-based model where units (services, mount points, sockets, etc.) declare what they need to run and what they provide, allowing for parallel startup and more efficient system initialization.

The ExecStart directive is powerful. It doesn’t just have to be a simple script; it can be a complex command, a Python interpreter running a script, or even a shell command with pipes and redirects. For instance, you could have ExecStart=/bin/sh -c '/usr/local/bin/my-script.sh --config /etc/my-app.conf > /var/log/my-app.log 2>&1'. This flexibility allows you to encapsulate almost any process into a systemd service.

What most people don’t realize is the power of Type in the [Service] section. While simple (the default, where the ExecStart command is the main process) is common, forking is used for older daemons that fork into the background. oneshot is for tasks that run once and then exit, like systemctl enable itself. notify and dbus are for services that signal readiness to systemd in specific ways, offering more precise control over service startup completion.

The next concept you’ll likely encounter is managing dependencies between services more intricately, using directives like Requires=, Wants=, Requisite=, and BindsTo= in conjunction with After= and Before=.

Want structured learning?

Take the full Linux & Systems Programming course →