tutorial · step 1 / 2
What is systemd? And why should you care?
- hardware
- Raspberry Pi 5
- os
- Raspberry Pi OS Bookworm
- verified
Before we start writing any service files, it’s worth spending a few minutes on what systemd actually is and why it exists.
The Boot Process in Plain English
When you power on a Raspberry Pi (or any Linux computer), a very specific sequence of events unfolds. The processor runs a small piece of firmware stored in ROM, which locates the bootloader on your SD card. The bootloader then loads the Linux kernel into memory and starts it. Now that the kernel is running, it initialises memory, filesystems and device drivers. At the very end of this setup phase, it needs to hand control over to the rest of the operating system.
The kernel does this by starting exactly one process and giving it process ID 1. This process is the init system and it becomes the parent of everything else that runs on the machine. On virtually every modern Linux distribution such as Raspberry Pi OS, Ubuntu, Debian or Fedora that process is systemd.
This is a common point of confusion for many people at first. I was one of them. The init process is the special PID 1 process that starts and manages the rest of the userspace processes. On modern Linux systems, systemd is the program that serves as the init process, so systemd is PID 1. In other words, “init” describes the role, while “systemd” is the program performing that role.
So, yes. You thought right! There are other programs out there other than systemd which performs ‘init’ role and become PID 1. Some of them are SysVinit, Upstart, OpenRC and runit. Systemd became dominant because it provided a broader and more integrated way to manage a Linux system. It ended up establishing itself as the common standard after major distributions adopted it.
From the moment systemd starts, it is in charge. It reads configuration files called unit files. ‘unit files’ describe everything the system needs to do such as mounting filesystems, configuring the network, starting background services and bringing up the desktop environment. It does all of this in a carefully ordered, parallelised way that’s much faster and more reliable than the older shell-script-based init systems it replaced.
This is the key insight you need as a developer: if you want your application to be part of that startup sequence and be managed, monitored, and restarted by the system itself, you just need to write a unit file that describes it. Once systemd knows about your application, it treats it like any other first-class system component.
LINUX KERNEL
│
│ starts
▼
┌─────────────────────┐
│ systemd │
│ PID = 1 │
│ │
│ "init process" │
└─────────────────────┘
│
┌──────────┼──────────┐
│ │ │
▼ ▼ ▼
sshd(100) cron(200) ...
│
▼
bash(150)
│
┌─────┼─────┐
▼ ▼ ▼
ls cat vim
Unit Files: Configuration for Everything
Now you know that systemd manages everything through unit files. They are small, structured text files that follow a consistent format. ‘unit files’ let us tell systemd these things:
- What program should start?
- When should it start?
- What does it depend on?
- Should it restart if it crashes?
- Which user should run it as?
- Where should its output/logs go?
The one you’ll work with most is the service unit, which describes a long-running process (a daemon). Other types include timer units (for scheduled tasks, covered in the examples section), socket units (for socket-activated services), and mount units (for filesystem mounts).
All service unit files share the same three-section structure:
[Unit]
# Metadata, description, and dependency declarations
[Service]
# How to start, stop, and manage the actual process
[Install]
# How this service integrates into the system boot targets
You’ll become familiar with each of these sections throughout this tutorial. The important thing to know now is that systemd reads these files from /etc/systemd/system/. So, this is where you’ll put your own service files. Files in this directory take precedence over systemd’s built-in defaults. And files stored here are completely immune to system upgrades.
The Two Essential Tools: systemctl and journalctl
You interact with systemd through two command-line tools, and you will end up using them often. So, let’s learn them first.
systemctl is the control interface for systemd allowing you to start, stop, enable, disable, and inspect services. The most important thing to understand is the difference between these two pairs of concepts: start vs. enable and stop vs. disable.
The first pair is start vs enable. systemctl start launches a service right now, in the current session. If you reboot, it won’t start again unless you’ve also enabled it. systemctl enable creates a symbolic link that tells systemd to start the service at boot. However, it doesn’t start it right now. In everyday use you almost always want to run both commands together after creating a new service.
The second pair is stop vs disable. systemctl stop stops a running service immediately, but it’ll start again at the next reboot if it’s enabled. systemctl disable removes the boot-time symlink so it won’t start at the next reboot, but it doesn’t stop the currently running instance. In practice you often want both when removing a service.
journalctl is how you can look at the logs that systemd collects from every service it manages. Systemd captures everything your service prints to stdout and stderr and stores it in a structured binary database called the journal. So, it makes filtering, searching, and correlating logs across services much more powerful than traditional log files.
The command you’ll end up using most often is journalctl -u my-app.service. It shows all historical logs for a specific service. Adding -f follows the log in real time, like tail -f. Adding -b shows only logs from the current boot.
Why should you care as an Embedded Systems engineer?
If you’re running a Raspberry Pi as a headless embedded device such as a sensor node, a home automation hub or an edge computing device, the consequences of your application crashing and never restarting are dire. Which is even worse if the device is in a remote location or inside a piece of equipment, you might not notice for hours, days, or weeks.
There are alternatives people often reach for first. Putting commands in /etc/rc.local, using cron @reboot, or writing shell scripts in /etc/init.d/ are some of them. They all work after a fashion, but they give you almost none of the reliability features systemd provides out of the box. They don’t automatically restart crashed processes. They don’t provide ordered startup relative to hardware and network dependencies. They don’t capture logs in a queryable, structured way. And they don’t give you a clean interface for checking whether your application is actually running and healthy.
systemd gives you all of that, and all it costs is learning how to write a service file. In the rest of this tutorial I will teach you how to write service files building from the simplest possible example up to a production-grade configuration.
Comments