With the latest release of Raspbian I started to struggle to autorun Python scripts on bootup using Cron or rc.local. It appears that the Raspbian boot sequence has changed and these processes run at different points in that sequence. How much of an issue this is depends on what your Python script is trying to do and what resources it needs.
The point at which your Python script is run in the startup sequence is vital if your script relies on any system features being available at that point in time. For me this often includes :
- Network is connected and available
- The /home/pi directory is mounted and read for use
- System time has been updated by NTP
I decided to use “systemd” as this seems to the recommended way of launching custom features and lots of Linux distributions are adopting it. systemd is a software suite for central management and configuration of a Linux system and aims to replace other popular tools that previously fulfilled this role. As a result it seems to have plenty of enemies but you can read all about the controversy on the systemd Wikipedia page.
systemd is quite scary. Or easy. Depending on your level of experience. My goal was just to get it to launch one of my scripts with the minimum of fuss and without having to do too much typing I didn’t understand.
Step 1 – Your Python Script
My example script was stored in the /home/pi directory and named “myscript.py”. Obviously your script can be called something else but keep an eye on where it is referenced in the commands and text below.
Step 2 – Create A Unit File
Next we will create a configuration file (aka a unit file) that tells systemd what we want it to do and when :
sudo nano /lib/systemd/system/myscript.service
Add in the following text :
[Unit] Description=My Script Service After=multi-user.target [Service] Type=idle ExecStart=/usr/bin/python /home/pi/myscript.py WorkingDirectory=/home/pi User=pi [Install] WantedBy=multi-user.target
You can save and exit the nano editor using [CTRL-X], [Y] then [ENTER].
This defines a new service called “My Script Service” and we are requesting that it is launched once the multi-user environment is available. The “ExecStart” parameter is used to specify the command we want to run. The “Type” is set to “idle” ensures the ExecStart command is only run when everything else has loaded. For my GPIO based scripts the default type of “simple” didn’t work.
Note that the paths are absolute and fully define the location of Python as well as the location of our Python script.
In order to store the script’s text output in a log file you can change the ExecStart line to :
ExecStart=/usr/bin/python /home/pi/myscript.py > /home/pi/myscript.log 2>&1
The permission on the unit file needs to be set to 644 :
sudo chmod 644 /lib/systemd/system/myscript.service
Step 3 – Configure systemd
Now the unit file has been defined we can tell systemd to start it during the boot sequence :
sudo systemctl daemon-reload sudo systemctl enable myscript.service
Reboot the Pi and your custom service should run :
sudo reboot
Step 4 – Check status of your service
You can check the status of your service using :
sudo systemctl status myscript.service
Other useful resources for systemd
- DigitalOcean – How to use systemctl to manage systemd services and units
- ArchLinux – systemd
57 Comments
Just a little tweak: by default, std out/err are directed to journalctl (systemd’s component that replaces syslog); but with a python3 script, the output is double-buffered, which means it won’t appear normally in the logs (output will be flushed from time to time, which is not precictible), so it is best to launch python3 scripts with the -u flag.
Thank you!
Hey i’ve tried this method and many other methods online to auto run my python script. My python script records a video and uses a for loop to save multiple video files over a designated amount of time. When I used your method (along with others) the LED of the camera turns on as if the script did in fact auto run but I cannot find any video files afterwards! Why is this happening? I’ve tried so many ways.
Where are the files being recorded to? It might be that location isn’t mounted at boot time?
I’ve been tearing my hair out trying to get my Sense HAT to log to a google spreadsheet on boot up, this finally helped me crack it!. Many thanks for this.
One tip I have found, is that if you are reading from a file (like I am with my OAuth JSON) then you also need to provide a full path in the script as well. Discovered in the error messages in systemctl status, this might help Noah.
Thanks Matt – I have a feeling I’m going to be using this a LOT. 🙂
Thanks for this.
Spent a day trying to get this to work, until I found this tutorial.
Others seem to be missing the
sudo systemctl daemon-reload
sudo systemctl enable myscript.service
commands, which is vital to get the unit file to actually run.
Cheers, Ian
Most tutorials I found have this one…
systemctl enable myscript.service
but they don’t have this one…
systemctl daemon-reload
The fact that I didn’t have ‘systemctl daemon-reload’ is most likely why I never got systemd to work in Raspbian Jessie.
I switched to Arch Linux on the Raspberry Pi, and I got systemd to work fine. In Arch LInux I have never needed to use ‘systemctl daemon-reload’.
Lee
The unit file must start with the following line:
[Unit]
Hi, can someone confirm that this process would work for the issue I am having?
I have detailed the issue on the rpi forum here:
https://www.raspberrypi.org/forums/viewtopic.php?f=28&t=133880
Any guidance is welcomed.
Thanks, this has been a huge help for me, a Linux and Pi newbie.
For other newbies, let’s note that
sudo chmod644 /lib/systemd/system/myscript.service
should be
sudo chmod 644 /lib/systemd/system/myscript.service
Thanks Jim, I’ve updated the post to correct the mistake.
I have 3 Python programs that I want to start at reboot. Can one unit file be used to start the 3 Python programs at startup? Do I need a separate unit file for each Python program?
Manual says that commands can be separated with alone ;
http://www.freedesktop.org/software/systemd/man/systemd.service.html
How to auto run multiple python scripts???
This is great, thanks.
Now my question. What do you do in order to stop a script? I have my running on an infinite loop. Every 10 minutes it looks at a particular timeline for new tweets. But how do stop the script running to make changes?
This technique is really only suitable for a script that runs once. To run a script every 10 minutes I would use cron.
systemctl stop myscript service
systemctl start myscript service
And… In [service] section you can control the restart:
Restart=on-failure | always
RestartSec=…
…Configures the time to sleep before restarting a service (as configured with Restart=). Takes a unit-less value in seconds, or a time span value such as “5min 20s”. Defaults to 100ms.
Somehow it can’t start my Script. The log says: -bash: /home/pi/folder/script.py: Permission denied
But everyone has access (chmod 777) to the files: script.py, script.log, script.json.
And I have also run this Command: sudo chmod 644 /lib/systemd/system/myscript.service
How can I fix the Problem?
Put “User=pi” in the [Service] section. This will run the script with the same permissions as the Pi user.
Nice job. Thanks.
The reboot in step 3 is unnecessary. After ‘sudo systemctl enable myscript.service’ just start the service via: ‘sudo systemctl start myscript.service’.
I followed all the steps but i get an error message after typing “sudo systemctl enable myscript.service” it says no such file or service exists.
That error message means you either haven’t created the “myscript.service” file, or you called it something else or you created it in the wrong place. Use :
cd /lib/systemd/system/my*.service
to check that it exists in the correct directory and is named “myscript.service”.
This was exactly what I was looking for. Cheers.
Thanks for sharing! Working as expected! But not the log file. However I used:
ExecStart=/usr/bin/python /home/pi/myscript.py > /home/pi/myscript.log 2>1
Note the difference it’s 2>1 and it’s now logging in /var/log/messages and to see it, just run
journalctl -u myscript
or put the full path to your .service file:
sudo systemctl enable /some/folder/myscript.service
my python script imports modules installed using pip . this module is not getting imported when running through systemd. but when i run the python script it runs without any problems
use python3 in this line:
ExecStart=/usr/bin/python /home/pi/myscript.py
Same issue as MD above. I tried:
ExecStart=/usr/bin/python3 /home/pi/myscript.py
Still same problem. Other suggestions?
Your line has a space character after “python3”. Try :
ExecStart=/usr/bin/python3/home/pi/myscript.py
Hey nice guide! One gotcha: for user-defined services, they should live in /etc/systemd/system. The /lib/ location (or /usr/lib on some liinux distros) shouldn’t be touched by humans in general. As a broad rule, the only spots that humans should modify are /etc /home and /var (and sometimes /opt or /srv)
hello i cant see any log file created. how can i see my output? its not showing in the terminal also even without adding that log file line
You won’t see any output as there is no terminal session established when the script is run.
Thanks for this, I’m a n00b but trying to learn. getting very confused by older how-tos when I’m using Jessie.
Question, would this method be ok to launch another program at boot? I would like to start TTYMidi in the background by using
ExecStart=/usr/local/bin/ttymidi -s /dev/ttyAMA0 -b 38400 -v &
I’ve read about editing rc.local but want to find the ‘right’ way 🙂
Yogi
I have the same problem, as Faizan had – log file is not working. When I just run command from ExecStart in terminal everything work fine, but if it runs from systemctl, log file doesn’t fills.
Hi Matt,
A good explanation and tutorial and it works just fine.
Which bit is the best bit to delete/amend to stop it running on boot? I need to make some tweeks to the script but don’t want the old version running alongside the newer one. I’ve tried changing the name of the unit file slightly but that hasn’t stopped it running on reboot.
I think that I may have got it. There was a stray file “myscript” that was causing the problem.
Reg.
My application is written in Kivy and runs on pi3. If started manually runs ok but with Autorun touch buttons are not working. It was running ok on pi2 but I had to switch to pi3 to improve performance. I have tried crontab, rc local, profile with the same results. Any idea how to fix this? Again all the methods work fine with pi2 but with pi3 somehow Kivy is not initializing properly.
Thanks
Inka
Finaly found a solution! I just added this to run the service with my own account:
[Service]
User=pi
Brilliant! After my init.d script broke (called a python script to listen to a GPIO pin and set a GPIO pin) when using the latest Raspbian release, I found your explanation of the why, and the solution. I’ll stick with systemd unit files from on.
Thanks again.
This has been a great, and succinct methodology to get a service running. It worked great first time, thank you.
A problem I have encountered, which you may be able to shed a little light on, is I can find no evidence of a log file being created, despite having this line in my .service file:
ExecStart=/usr/bin/python /home/pi/myTestService.py > /home/pi/myTestService.log 2>&1
The Python script is a simple counter updates every 10 seconds and prints the counter + time to the screen. I expected the log file to contain this output.
Any pointers or should I start exploring the journal system?
Many thanks
Thanks, this was very helpful and got me up and running fast! Only thing I had to add was under the [Service] section, I added a WorkingDirectory line so my log file and INI file are saved in the same folder as my script. Without that, they were being saved to the root directory. So for example:
[Service]
Type=idle
ExecStart=/usr/bin/python /home/pi/myscript.py
WorkingDirectory=/home/pi
Thanks for the awesome tutorial bro. Can you please tell me how can I run multiple python scripts at startup using this method?
Thanks for this! Been pulling my hair out all day over this 🙂 Finally works!
+ 1 been pulling my hair out with cron @reboot, rc.local and init.d after hours trying to get those methods working, this was running in about 2 minutes. Thanks so much!
thank you, this was helpfull but I did add 2 lines into [Service] section
WorkingDirectory=/home/pi
User=pi
and change exec line
ExecStart=/bin/bash -c ‘/usr/bin/python3 /home/pi/me.py > /home/pi/me.log 2>&1’
sorry, I forgot to mentioned:
#1 those two lines doing that script is running by user “pi” in its home directory
#2 that change on exec make really redirect output to the file
Thanks you so much….such a wonderful blog Everything works fine
Simple, straight forward, to the point and most importantly….working!!!!
Thx so much!!
Few tips, mainly for any recent readers 🙂
1. The systemd docs have this to say about ExecStart – “redirection using “<", "<“, and “>>”, pipes using “|”, running programs in the background using “&”, and other elements of shell syntax are not supported.”
So for logging you should instead look at using the config “StandardOutput=” , or stick with the default (journal).
2. And about “Type=idle” – “Note that this type is useful only to improve console output, it is not useful as a general unit ordering tool”, you probably need to look at using “After=”
References:
https://www.freedesktop.org/software/systemd/man/systemd.service.html#Command%20lines
https://www.freedesktop.org/software/systemd/man/systemd.exec.html#StandardOutput=
https://www.freedesktop.org/software/systemd/man/systemd.service.html#Type=
https://www.freedesktop.org/software/systemd/man/systemd.unit.html#Before=
Thanks
Just wanted to say that this has helped me.
Thank you very much!
Thanks for the tute.
Just a note that user services should be defined in /etc/systemd/user/servicename.service and not be dropped in the system location you have specified. I have just done this and it still functions exactly as you describe.
mannn thanx !!!
This worked for me except there were problems accessing the DISPLAY.
Adding this solved everything after HOURS
User=pi
Group=pi
Great work! its the second tutorial that i used, and THIS worked !!
Thanks
Thank you. This is the first really working solution for Raspberry I found !!