How to run a script on boot

That is the Canonical (haha) way of doing it now.

To start, create a limited user to run your service as by running the following commands:

sudo addgroup --system robot
sudo adduser --system --no-create-home --disabled-login --disabled-password --ingroup robot robot

if your script user needs to be in extra groups you can add them with

adduser robot GROUP

…where GROUP is the group you want to add the user to (video, for example, to access the GPU). You can see the groups a user such as your own is in with the groups USER command where USER is the user you want to check the groups for. If your script needs gpio, follow the instructions at the following link to create a gpio group if it doesn’t already exist rather than running your script as root (and then add robot to the gpio group with “sudo adduser robot gpio”).

Next, create a service unit file in /lib/systemd/system by doing:

sudo touch /lib/systemd/system/robot.service
sudo your_favorite_editor_here /lib/systemd/system/robot.service

The touch may not be necessary if your editor creates the file automatically. Then paste the below into the file and save it, modifying the ExecStart line with the path to your script. I recommended to sudo cp your script to /usr/local/bin and use that path so the script copy can only be changed by root. It’s mode should be 755, but chances are it already is if you’re executing it. You may wish to chmod 755 /usr/bin/your_script.whatever anyway after you copy it just to make sure it’s not something like 777.

[Unit]
After=network.target
Description="Robot Service"
[Service]
ExecStart=/usr/local/bin/your_script.whatever
User=robot
[Install]
WantedBy=multi-user.target

The “After” line is only needed if your robot needs network access. Otherwise it can be omitted. Other targets or specific services for “After” or “WantedBy” may be more appropriate depending on your needs.

Once that’s done you need do:

sudo systemctl daemon-reload
sudo systemctl start robot

so systemd sees the new .service file you’ve created and starts the service. “stop” instead of start will stop the service with SIGINT, and then (iirc) SIGKILL. To check on the status of the service and it’s output, if your script sends things to stderr/stdout, do:

sudo systemctl status robot

If there is an error in your script and the service fails, that’s how you will know.

Once you have tested that it works, to make the change peristent and load it every boot, simply do:

sudo systemctl enable robot

and every time your system boots to any runlevel past multi-user.target, your script will be run. Use “disable” instead to disable the service.

The final (optional) icing on the cake is for your script to handle SIGINT and cleanly shut down. This may or may not be necessary depending on what your script does. You may need to, for example, free some system resource. Otherwise you may have problems starting your script a second time after stopping it. In Python you can do this by catching KeyboardInterrupt (in a try block, for example) or with the signals module. In bash, it’s like this. Systemd can send other signals, like SIGHUP when a “reload” is requested with “systemctl reload robot”, so you can, for example, reload a configuration file without restarting your service. You can catch that in Python with the signal module. For bash, it’s the same link as above.

8 Likes