How to Run a Raspberry Pi Program on Startup

Pages
Contributors: Shawn Hymel
Favorited Favorite 20

Method 3: systemd

systemd is the preferred method of starting applications on startup, but it is also one of the most complicated to use. With systemd, you have the benefit of being able to tell Linux to start certain programs only after certain services have started. As a result, it is a very robust tool for initializing your scripts and applications.

systemd is a relatively new suite of tools in the Linux world, and one of its intended purposes is to manage system processes after booting. When it was first released, systemd was meant to replace the init.d tool for starting programs. As of 2015, most of the major distributions include systemd, and since many kept init.d around for legacy support, you have the option of using either one. Be aware, however, that init.d may be deprecated, so systemd seems to be the future (for now).

systemd can be quite complicated, and this tutorial only covers the absolute basics to get you started running your programs on boot. If you would like to dig deeper into the world of systemd, we recommend reading this getting started guide.

Create a Unit File

A unit file is a plain text file that gives information to systemd about a service, device, mount point, etc. We'll create a unit file that starts our program as a service (a process that runs in the background). Below are two examples of unit files: the first runs the blink.py example before the graphical desktop loads (useful for headless environments), and the second runs the clock.py example after the graphical desktop loads (useful if you are making a dashboard or GUI).

Unit File (No GUI)

If your program does not require a GUI (such as our blink.py example), then you can use the following template to create a systemd service. If you do need a GUI (e.g. you require the X Windows System to have started), see the next section. Creating a unit file without requiring a GUI means you can also run your program on boot in a headless environment.

Create a new .service file in the systemd directory:

language:shell
sudo nano /lib/systemd/system/blink.service

Enter the following text into the document:

[Unit]
Description=Blink my LED
After=multi-user.target

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

[Install]
WantedBy=multi-user.target

Feel free to change the Description as desired. The After key denotes when our program should run. multi-user.target is the system state where control is given over to the user (a "multi-user shell") but before the X Windows System is started. That means our program will run even without logging in! You can change this, depending on which services you need active before running your program (for example, network.target if you need networking). See here for a listing of all targets.

ExecStart is the command (or set of commands) used to start our program. Notice that we are using absolute paths to the version of Python we want as well as the location of our program.

WantedBy in the [Install] section specifies the target we want our service to be included with. In this example, we want our service to run when the multi-user.target unit is run (or, more specifically, just after it, based on the After parameter).

Writing a systemd service to blink an LED on boot

Save and exit with ctrl + x, followed by y when prompted to save, and then enter.

We need to tell systemd to recognize our service, so enter:

language:shell
sudo systemctl daemon-reload

Note that you will need to enter this command every time you change your .service file, as systemd needs to know it has been updated.

We then need to tell systemd that we want our service to start on boot, so enter:

language:shell
sudo systemctl enable blink.service

Reboot with sudo reboot to verify that your program works. The LED should begin blinking after the Pi boots!

Unit File (GUI)

If your program requires graphical components (as in our clock.py example), then the following template is recommended for creating a systemd service.

Create a new .service file in the systemd directory:

language:shell
sudo nano /lib/systemd/system/clock.service

Enter the following text into the document:

[Unit]
Description=Start Clock

[Service]
Environment=DISPLAY=:0
Environment=XAUTHORITY=/home/pi/.Xauthority
ExecStart=/usr/bin/python3 /home/pi/clock.py
Restart=always
RestartSec=10s
KillMode=process
TimeoutSec=infinity

[Install]
WantedBy=graphical.target

Description can be any text you want to help you remember what this service does.

Under [Service], we specify some environment variables. We want to connect to our primary display (this assumes only one display is connected to our Pi), so we set DISPLAY to :0, and we tell our application where to find the necessary credentials to use the X windows system with XAUTHORITY. ExecStart is the command we want to run (starting our Python clock program, in this case).

Unfortunately with systemd, we cannot tell exactly when the X system will start, and we cannot necessarily guarantee that a user will be logged in (unless you have enabled auto-login with sudo raspi-config). To account for this, we will brute force our program to restart (with Restart) every 10 seconds (with RestartSec) if it fails or exits. KillMode tells systemd to kill off any processes associated with our program if the service fails (or exits), and TimeoutSec=infinity means that we don't ever want to stop trying to execute our program.

Writing a systemd service to display a GUI application on startup

Save and exit with ctrl + x, followed by y when prompted to save, and then enter.

We need to tell systemd to recognize our service, so enter:

language:shell
sudo systemctl daemon-reload

Note that you will need to enter this command every time you change your .service file, as systemd needs to know it has been updated.

We then need to tell systemd that we want our service to start on boot, so enter:

language:shell
sudo systemctl enable clock.service

Reboot with sudo reboot to verify that your program works. You should see your Python clock program running after you have logged into your graphical desktop.

Troubleshooting

Nothing Happens

If your program does not seem to run on boot, several things could be going on. To get insight into the systemd service, try logging output to a file or checking on the status of the service (see the troubleshooting techniques below).

Debugging

Output from systemd (e.g. print() statements or error messages) is captured by the journalctl system and can be viewed with the following command:

language:shell
journalctl -u clock.log

This can off some insight into what's going on with your service or program.

Logging to a File

If journalctl is not living up to your expectations, you can try logging output to a file. To do that, change the ExecStart call to the following (using clock.py as an example):

ExecStart=/bin/bash -c '/usr/bin/python3 /home/pi/clock.py > /home/pi/clock.log 2>&1'

This starts a new bash shell, runs your program, and redirects the output (stdout) to a new clock.log text file. The 2>&1 command says that any errors (stderr) should also be redirected (written to) the same log file. Any output (e.g. from Python print() commands) or errors will then be saved to clock.log. You can view the log with the following command (note that you might need to stop the service and program before viewing the log):

language:shell
cat clock.log

Use a Specific Version of Python

Because your systemd unit file will likely run before .bashrc can alias the command python to Python 3, you might need to explicitly call the python3 command. To do that, just make sure that your call to Python is an absolute file location, for example, /usr/bin/python3.

Checking the Service

If you suspect that your service is not running, then you might want to check the status of it. To do that, enter the following command:

language:shell
systemctl status clock.service

This should show you if your service is running or if there were any errors.

Checking a systemd service to determine if it is runninng

Starting and Stopping the Service

For some services, like our clock.service example, you will need to stop the service before stopping the program. That's because even if you stop the program (e.g. our Python GUI clock), the service will simply restart it 10 seconds later! To stop a service, enter the following command:

language:shell
sudo systemctl stop clock.service

Note that stopping the service should send a stop command (SIGTERM--terminate signal) to your program. In most cases, this should stop the service and your program. If your program does not stop, see below on stopping your program.

To start a service (such as our clock.service example), you can enter the command:

language:shell
sudo systemctl start clock.service

This can be helpful to restart a service if you've made changes to it without having to reboot the system. Just remember to run sudo systemctl daemon-reload if you do make any changes to a .service file!

How to Stop Your Program

Even if you stop the service, your program may still be running in the background. Just like in the rc.local and autostart examples, we will need to hunt down the process ID number and kill it manually. Enter the following in a terminal:

language:shell
sudo ps -ax | grep python

ps -ax tells Linux to list out all the currently processes. We send that output to grep, which allows us to search for the keyword "python" (feel free to change it to the name of your program). Find the process ID (PID) number to the left of the listed process, and use the kill command to terminate that process:

language:shell
sudo kill <PID>
Heads up! Make sure you type the PID correctly! If you kill the wrong process, you could halt Linux, and you would need to reboot again.

Terminating a process with the kill command

If you are using the clock.py example, you should see the application quit out.

How to Stop Your Program from Running on Boot

You can stop your service from running on boot with the following command (replacing clock.service with your particular service filename):

language:shell
sudo systemctl disable clock.service

Note that you do not need to delete the .service file, as disabling it will prevent it from running on startup. However, if you would like to delete the file, you can using the following commands (once again, replacing clock.service with your service filename):

language:shell
sudo rm /lib/systemd/system/clock.service
sudo systemctl daemon-reload

After that, reboot your system to make sure that your program no longer runs on boot.