DEV Community

Cover image for Write a minimal linux service file
Supun Sriyananda
Supun Sriyananda

Posted on Originally published at bittobyteacademy.com

Write a minimal linux service file

Let's create a real, working service file from scratch. By the end you'll have a Python script registered with systemd, starting at boot, and showing up cleanly in systemctl status. Every line of the service file is explained so you understand not just what to type but why it's there.


The Application We're Registering

For this example, suppose you have a Python script at /home/admin/resources-tools/systemd/examples/python-service. The specific content doesn't matter much for learning systemd — what matters is that it's a long-running process (something that loops, listens for connections, or monitors sensors) rather than a script that runs once and exits.

If you want a concrete working example to follow along with, use the script from examples/python-service/hello_service.py in the GitHub repository. It's a simple loop that logs a heartbeat message every ten seconds. It is straightforward enough to understand at a glance, but long-running enough to behave like a real service.


Where Service Files are Located

Service files belong in /etc/systemd/system/. This directory is specifically reserved for service files you write and manage yourself. It is separate from the directories where systemd's own built-in services are, and separate from the directories package managers use. Putting your files here means they won't be overwritten by a system update, and they'll take precedence over any lower-priority defaults.

The file must have a .service extension. The name you give it becomes the name you use with systemctl. So the convention is to choose something short and descriptive. In this tutorial I will be using hello_python.service.


The Minimal Service File

Create the file with:

sudo nano /etc/systemd/system/hello_python.service
Enter fullscreen mode Exit fullscreen mode

Type or paste the following:

[Unit]
Description=My Application

[Service]
ExecStart=/usr/bin/python3 /home/pi/hello_python/main.py

[Install]
WantedBy=multi-user.target
Enter fullscreen mode Exit fullscreen mode

This is the absolute minimum. Let me explain what each line is doing.

what-enable-changes

The [Unit] Section

Description=My Application is a plain-English label. It appears whenever you inspect this service in systemctl status, in journal logs or in any system monitoring tool. If you write something vague, you or anyone else using the system won't be able to identify the service at a glance after some time. Always write descriptive messages. For now I will be using "My Application", but in a real project write something like "Temperature Sensor Data Pipeline" or "MQTT Edge Client".

The [Service] Section

ExecStart=/usr/bin/python3 /home/admin/resources-tools/systemd/examples/python-service is the command that launches your program. A critical rule: you must use the full, absolute path to every executable. You can't write python3 hello_service.py because systemd doesn't use your shell's PATH environment. It needs to know the exact location of the Python interpreter. You can find it with which python3 in your terminal. On most Raspberry Pi systems it'll be /usr/bin/python3. The path to your script must also be absolute.

The [Install] Section

Systemd organises the boot process into targets. Targets are milestones that represent specific states of the system. multi-user.target represents the state where the system is fully booted, all essential hardware is initialised, networking is available, and the system is ready for normal use but before any graphical desktop starts. It essentially means "everything is ready, start normal services" point in the boot sequence. By declaring WantedBy=multi-user.target, you're saying "start my service when the system reaches this state." This is the correct target for the vast majority of application services.

A note

If you are using my example, in the Install section you need to fill 'User' and 'Group'. To find yours run the command id. This returns the active User ID (UID), Primary Group ID (GID), and all Supplementary Groups.


Activating the Service

Writing the file isn't enough on its own. Now we need to tell systemd the file exists and then enable it.

Step 1: Reload systemd's Configuration

sudo systemctl daemon-reload
Enter fullscreen mode Exit fullscreen mode

systemd reads unit files when it starts and caches their contents. When we create or modify a service file, systemd doesn't automatically notice the change. When you run daemon-reload, it re-scans all unit file directories and updates its internal cache. Remember, you must run this every time you create or edit a service file, or your changes will be ignored.

from-file-to-running-service

Step 2: Enable the Service

sudo systemctl enable hello_python.service
Enter fullscreen mode Exit fullscreen mode

This creates a symbolic link inside the multi-user.target.wants/ directory that points to your service file. This symlink is what causes systemd to start our service at boot. If you are following this tutorial, go ahead and run this command. You can actually see the symlink after running enable.

ls -la /etc/systemd/system/multi-user.target.wants/ | grep hello_python
Enter fullscreen mode Exit fullscreen mode

Here is the output from my Pi:

admin@sbrsl:~ $ ls -la /etc/systemd/system/multi-user.target.wants/ | grep hello_python
lrwxrwxrwx  1 root root   40 Oct  5 14:51 hello_python.service -> /etc/systemd/system/hello_python.service
Enter fullscreen mode Exit fullscreen mode

Step 3: Start the Service Now

Enabling only affects future boots. If you want to start the service in the current session without rebooting:

sudo systemctl start hello_python.service
Enter fullscreen mode Exit fullscreen mode

In practice, when deploying a new service, I almost always run all three commands in sequence:

sudo systemctl daemon-reload
sudo systemctl enable hello_python.service
sudo systemctl start hello_python.service
Enter fullscreen mode Exit fullscreen mode

Verifying it's Running

sudo systemctl status hello_python.service
Enter fullscreen mode Exit fullscreen mode

If everything is working, the output will look something like this:

admin@sbrsl:~ $ sudo systemctl status hello_python.service
● hello_python.service - Python Heartbeat Service (Tutorial Example)
     Loaded: loaded (/etc/systemd/system/hello_python.service; enabled; preset:>
     Active: active (running) since Thu 2025-10-08 12:27:30 +0530; 4min 56s ago
   Main PID: 31967 (python3)
      Tasks: 1 (limit: 9563)
        CPU: 43ms
     CGroup: /system.slice/hello_python.service
             └─31967 /usr/bin/python3 hello_service.py

Oct 08 12:30:51 sbrsl hello-python[31967]: 2025-10-08 12:30:51,061 INFO: Heartb>
Oct 08 12:31:01 sbrsl hello-python[31967]: 2025-10-08 12:31:01,062 INFO: Heartb>
Oct 08 12:31:11 sbrsl hello-python[31967]: 2025-10-08 12:31:11,063 INFO: Heartb>
Oct 08 12:31:21 sbrsl hello-python[31967]: 2025-10-08 12:31:21,064 INFO: Heartb>
Oct 08 12:31:31 sbrsl hello-python[31967]: 2025-10-08 12:31:31,065 INFO: Heartb>
Oct 08 12:31:41 sbrsl hello-python[31967]: 2025-10-08 12:31:41,068 INFO: Heartb>
Oct 08 12:31:51 sbrsl hello-python[31967]: 2025-10-08 12:31:51,069 INFO: Heartb>
Oct 08 12:32:01 sbrsl hello-python[31967]: 2025-10-08 12:32:01,070 INFO: Heartb>
Oct 08 12:32:11 sbrsl hello-python[31967]: 2025-10-08 12:32:11,070 INFO: Heartb>
Oct 08 12:32:21 sbrsl hello-python[31967]: 2025-10-08 12:32:21,071 INFO: Heartb>
lines 1-19/19 (END)

Enter fullscreen mode Exit fullscreen mode

The above is the real output from my pi.

The most important line here is Active: active (running). The word enabled next to Loaded confirms it'll also start at boot. If you see active (dead), the service ran and exited. In this case, if you expected your script exits cleanly this is the right thing you should see. But if you expected it to keep running, this is a problem. If you see failed, the status output usually includes the last few log lines pointing to the cause. I will show you how to inspect them.


Testing Reboot Persistence

The real test of a service is whether it survives a reboot:

sudo reboot
Enter fullscreen mode Exit fullscreen mode

After the Pi/Linux machine comes back up, check the status again:

sudo systemctl status hello_python.service
Enter fullscreen mode Exit fullscreen mode

If it shows active (running) with a start time matching the current boot, our service is working correctly as a persistent, auto-starting system service.


One-Line Enable + Start Shortcut

On newer versions of systemd (v220 and later, which includes all current Raspberry Pi OS versions), we can combine enable and start with the --now flag:

sudo systemctl enable --now hello_python.service
Enter fullscreen mode Exit fullscreen mode

Functionally this is identical to running enable and start separately, but just more convenient.


This Minimal Service is Still Missing Something

The service you have right now works, but it's missing several things that matter for real-world use. If the application crashes, systemd will leave it dead and never restart it. If you're writing relative file paths in your Python code, they'll likely fail because systemd starts services from the root directory, not your project folder. And if you have any configuration like API keys, database passwords, and server addresses, there's no good place to put them yet. In the next two articles I will address all of these.

Deliberately stopping or forcefully killing a service isn't just about handling an absolute emergency. In day-to-day administration, there are several practical reasons you will need to step in and shut a process down:

  • Applying Software Updates: When you modify your code, update a Python package, or download a security patch, the old version of the program keeps running in the Pi's RAM. You must stop the service so systemd can load the fresh code.
  • Conserving System Resources: The Raspberry Pi has limited RAM and CPU power. If you are about to run a heavy task like compiling code or processing video, you may need to temporarily kill background services (like a database or web server) to free up hardware memory.
  • Testing and Debugging: If your script is misbehaving, it is much easier to stop the systemd service completely and run the script manually in your terminal. This allows us to see live print statements and interactive error messages in real-time.
  • Changing Configuration Settings: Many applications only read their configuration or environment files exactly once when they launch. So, if you change a setting, the running service won't notice until you stop and start it again.
  • Preventing Data Corruption: If you need to back up a database or copy a live log file from your SD card, stopping the service first ensures that no new data is being written mid-copy. This prevents your backup from becoming corrupted.

Force-kill the Python process with kill <PID> and then run systemctl status to see what systemd does. Observe this and the restart policies we will be discussing in the next article would become much more concrete.

Top comments (0)