The ultimate guide on using systemd to autostart scripts on the Raspberry Pi

I keep hearing of users that have problems with crontab -e to autostart their scripts and I believe it’s time to generally use systemd instead.

Systemd can be customized in many ways to make sure that your script is only started when certain requirements have been met and I find it a much more reliable option than using the older crontab -e method.

And yet, systemd is believed to be more complex. To debunk this myth, I will show you exactly how it works and answer the most common questions that still cater to some confusion around this most useful command.

This is important for us home-brewers of digital picture frames because depending on your configuration, you will have a number of services (i.e. scripts) running and systemd will make sure that they are launched and monitored comme il faut.

The history of systemd

When systemd was introduced to Linux, it faced the initial opposition of many die-hard Linux developers. After all, there were plenty of solutions to manage services and the early implementation has quite a few bugs so that Linus Torvalds himself in 2014 refused to merge code from the systems developer into the Linux kernel.

But after a rocky start, it was introduced with the Jessie release to the Raspberry Pi community in 2018. It has since convinced users and developers with its reliability and flexibility and has become the new standard for managing services and running scripts.

The placement of systemd files

Using systemd requires the creation of a small configuration file which is placed into the

/etc/systemd/system/

directory

Often times, you will read instructions where you are told to put this file into the /lib/systemd/system/ directory but this is bad practise. Don’t do it.

The difference between these two file locations is as follows:

Files that come in packages downloaded from a distribution repository go into /usr/lib/systemd/.

Local modifications (e.g. python scripts) that are manually placed by the user for ad-hoc software installations that are not in the form of a package go into /etc/systemd/system/.

The configuration logic of systemd

It always sounds a bit scary for Raspberry Pi novices to create new system files, but it isn’t. At the end of this article, using systemd will be much more natural for you than using crontab -e.

To create a new service file in systemd, type

sudo nano /etc/systemd/system/name-of-your-service.service

By using this command, the file is already placed in the right directory, so you don’t have to navigate through the maze of Linux directories.

The command is the same if your want to later change your service file. If it doesn’t exist, it creates the file. If it already exists, it opens it.

Every systemd file has three sections called

[Unit]
[Service]
[Install]

The Unit section

The Unit section provides basic information about the service and what conditions precedent (e.g. network connectivity) need to be met before a service is started.

This will be shown in the services overview, just call it as you like.

Description=My custom service

In this case, the service will be run after the user accounts are available to the system during the boot process.

After=multi-user.target

Or if you want it to wait until the desktop appears.

After=graphical.target

If your script requires network connectivity, use

Requires=network.target

A resulting typical Unit section looks like this:

[Unit]
Description=This is my great service
After=multi-user.target

The Service section

The Service section provides instructions on how to control the service.

Type=idle

The ‘Type’ option of ‘idle’ tells systemd to wait until all other services have completed. This waits until the boot process has been fully completed.

User=pi

The User defines which user the program will run under.

ExecStart=/usr/bin/python3 /home/pi/your-script.py

These are the commands and arguments executed when the service starts. Note that you must give the full path of both the program you are running (Python3) and the script.

Restart specifies what should happen in the event of a service ending either intentionally or by crashing.

There are four often used options:

Restart=always # always restart
Restart=no # the service will not be restarted. This is the default.
Restart=on-success # Restart only when the service process exits cleanly (exit code 0)
Restart=on-failure # Restart only when the service process does not exit cleanly (node-zero exit code)

This is followed by

RestartSec=60

which specifies the time in seconds to sleep before restarting a service.

A resulting typical Service section to start a Python script looks like this:

[Service]
Type=idle

User=pi
ExecStart=/usr/bin/python3 /home/pi/your-python-script.py

Restart=always
RestartSec=60

The Install section

The Install section provides instructions on how systemd triggers the custom service if enabled with systemctl enable. This is mostly used for starting the custom service on boot.

A resulting typical Install section looks like this:

[Install]
WantedBy=multi-user.target

This is only an excerpt of options that I believe are the most relevant to readers of this blog, but if you really want to dive into this subject, I recommend this page.

Example of starting a Python script

There are plenty of Python script examples on this blog, which you can activate with systemd.

Create a new service

So create a new file with

sudo nano /etc/systemd/system/name-of-your-service.service

This typical setup works for the Python scripts on this blog. Just copy & paste

[Unit]
Description=name-of-your-service Service
After=multi-user.target

[Service]
Type=idle
User=pi
ExecStart=/usr/bin/python3 /home/pi/your-Python-script.py
Restart=always

[Install]
WantedBy=multi-user.target

save the file with CTRL+O and close the editor with with CTRL+X.

Change file permissions

Now, we need to change the file permissions to make it readable by all by typing

sudo chmod 644 /etc/systemd/system/name-of-your-service.service

Inform the system

As the last step, you need to tell the system that you have added this file and want to enable this service so that it starts at boot.

sudo systemctl daemon-reload
sudo systemctl enable name-of-your-service.service

Reboot your Pi, and you are all set!

If you need to make edits

If at any point you need to edit the script, just call it up again with

sudo nano /etc/systemd/system/name-of-your-service.service

edit and save it.

As long as you don’t change the file name of the .service file,, there is no need to invoke the systemctl commands again.

However, after deleting a file, you should reload the systemd process with

sudo systemctl daemon-reload

systemd commands

You already used the sudo systemctl reload and sudo systemctl enable command above, but let’s take a look at a few other ones that can be useful for debugging.

Starting a service

To start a service manually

sudo systemctl start name-of-your-service.service

Stopping a service

To stop it

sudo systemctl stop name-of-your-service.service

Restarting a service

To restart it

sudo systemctl reload name-of-your-service.service

Enabling a service to start at boot

To tell systemd to start a service automatically at boot, you must enable it. That’s what we did in the previous chapter.

To start a service at boot, use the enable command:

sudo systemctl enable name-of-your-service.service

Disabling a service to start at boot

And the same syntax to disable a service at boot

sudo systemctl disable name-of-your-service.service

Note that enabling a service does not start right away. Should you want to start the service and enable it at boot, you will have to issue both the start and enable commands.

Check if a service is active

To check if a service is currently active

sudo systemctl status name-of-your-service.service

Check if a service is enabled

To check if a service has been enabled to start at boot

sudo systemctl is-enabled name-of-your-service.service

List all active services

To see a list of all active services

systemctl list-units --type=service

Using systemd for interval triggered scripts

Often crontab -e is used to launch a script based on a fixed interval, e.g., every hour.

This systemd example will launch the script “say-the-time.py” every hour:

[Unit]
Description=This script will start launch
After=multi-user.target

[Service]
Type=simple
ExecStart=/usr/bin/python3 /home/pi/say-the-time.py

Restart=always
RestartSec=3600

[Install]
WantedBy=multi-user.target

Note that this launches the script 3600 seconds after the previous call has been completed, not on the hour of the clock.

Using systemd for absolute time triggered scripts

If you want to understand how to schedule realtime tasks by the clock with systemd, please head over to my article “How to start realtime systemd timer services” to learn how to do it.

Conclusion

systemd is a powerful way to reliably control Python scripts and many other services.

It’s time so say good-bye to crontab -e and use systemd instead!

Let me know what your favourite solution for autostarting scripts is!

Was this article helpful?


Thank you for your support and motivation.


Scroll to Top