How to use systemd for applications start

On the latest Debian distributions is installed by default systemd instead of sysvinit as initialization system. This article illustrates pratically how to use it.

Create a service

To launch a script at startup a proper service file have to be created and added to the systemd.

Let's create a service file for our program. Create as root a file called blink.service in /lib/systemd/system directory.

sudo nano /lib/systemd/system/blink.service

then save inside it this content:

Description=Blinking led

ExecStart=/usr/bin/python /home/pi/


Next systemd has to be configured, enabling the service so at the next reboot it will be started automatically. First off, though, the systemd itself has to be reloaded in order to detect the new service definition:

sudo systemctl daemon-reload

Next, enable the service

sudo systemctl enable blink

Check if it is enabled:

sudo systemctl list-unit-files | grep enabled

Of course, it can be started manually just now:

sudo systemctl start blink

The PID (process id) can be now checked:

ps -ef | grep blink
pi         817     1  0 09:19 ?        00:00:00 /usr/bin/python

it is 817 (the number will be different in your case). Please note that it has been started from systemd process, in fact the PPID (parent process id) is 1.

Test what happens if the program crashes and/or terminates.

In order to simulate a crash, we kill it manually:

sudo kill 817

Now we can check it was restarted looking again to the PID (process id):

ps -ef | grep blink
pi        1037     1  0 09:19 ?        00:00:00 /usr/bin/python

it is now 1037 (PPID always 1), so, as it is different, that means has been restarted automatically after the kill.

Use the systemd logging system

To check what is doing your service launch this command:

journalctl -f blink

It is possible, of course, to add our own messages to this log. In Python, for example, install the python-systemd package:

sudo apt-get update
sudo apt-get install python-systemd

add these lines to your code:

import logging
from systemd.journal import JournalHandler

log = logging.getLogger('blonk')
log.setLevel(logging.INFO)"Blinking led started")

Open another terminal and launc journalctl with follow option (-f):

journalctl -u -f blink
May 13 11:49:24 roadrunner systemd[1]: Started Blinking led.
May 13 11:49:25 roadrunner /home/pi/[18081]: Blinking led started

Create a timer for your service

It is possible to use the systemd timers to launch a service instead of using cron

Example 1: Run a service every hour

Create a systemd service file in /lib/systemd/system/blink.service with this contents:

Description=Blinking led


Create a systemd timer file for this service in /lib/systemd/system/blink.timer with this contents::

Description=Runs every hour

# Time to wait after booting before we run first time
# Time between running each consecutive time


Reload the service definitions:

sudo systemctl daemon-reload

Then is your are using the same file of previous example disable the blink.service and enable just the blink.timer to start it after the bootstrap:

sudo systemctl disable blink.service
sudo systemctl enable blink.timer

Let's try it now:

sudo systemctl start blink.timer

Example 2: Run a service daily

Description=Runs daily at midnight

OnCalendar=*-*-* 00:00:00


Changing the OnCalendar parameter is possible to start the blink.service in a lot of different way:

Minimal form                   Normalized form
Sat,Thu,Mon-Wed,Sat-Sun    ==> Mon-Thu,Sat,Sun *-*-* 00:00:00
Mon,Sun 12-*-* 2,1:23      ==> Mon,Sun 2012-*-* 01,02:23:00
Wed *-1                    ==> Wed *-*-01 00:00:00
Wed-Wed,Wed *-1            ==> Wed *-*-01 00:00:00
Wed, 17:48                 ==> Wed *-*-* 17:48:00
Wed-Sat,Tue 12-10-15 1:2:3 ==> Tue-Sat 2012-10-15 01:02:03
*-*-7 0:0:0                ==> *-*-07 00:00:00
10-15                      ==> *-10-15 00:00:00
monday *-12-* 17:00        ==> Mon *-12-* 17:00:00
Mon,Fri *-*-3,1,2 *:30:45  ==> Mon,Fri *-*-01,02,03 *:30:45
12,14,13,12:20,10,30       ==> *-*-* 12,13,14:10,20,30:00
mon,fri *-1/2-1,3 *:30:45  ==> Mon,Fri *-01/2-01,03 *:30:45
03-05 08:05:40             ==> *-03-05 08:05:40
08:05:40                   ==> *-*-* 08:05:40
05:40                      ==> *-*-* 05:40:00
Sat,Sun 12-05 08:05:40     ==> Sat,Sun *-12-05 08:05:40
Sat,Sun 08:05:40           ==> Sat,Sun *-*-* 08:05:40
2003-03-05 05:40           ==> 2003-03-05 05:40:00
2003-03-05                 ==> 2003-03-05 00:00:00
03-05                      ==> *-03-05 00:00:00
hourly                     ==> *-*-* *:00:00
daily                      ==> *-*-* 00:00:00
monthly                    ==> *-*-01 00:00:00
weekly                     ==> Mon *-*-* 00:00:00
*:20/15                    ==> *-*-* *:20/15:00

Check the list of timers

systemctl list-timers --all
NEXT                          LEFT     LAST                          PASSED       UNIT                         ACTIVATES
Mon 2018-05-14 00:00:00 CEST  10h left Sun 2018-05-13 13:14:44 CEST  2min 17s ago blink.timer                  blink.service
Mon 2018-05-14 10:38:28 CEST  21h left Sun 2018-05-13 10:38:28 CEST  2h 38min ago systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service
n/a                           n/a      n/a                           n/a          apt-daily-upgrade.timer      apt-daily-upgrade.service
n/a                           n/a      n/a                           n/a          apt-daily.timer              apt-daily.service


How to start a script right before shutdown

This is an example on how to start a Python script called at shutdown:

Description=Send a message on the Oled at shutdown



Save is as /lib/systemd/system/oled.service

Reload the new service definition:

sudo systemctl daemon-reload

Enable the new service:

sudo systemctl enable oled.service

Then shutdown:

sudo halt


How to setup root autologin on roadrunner and CM3 serial console

During intensive board test it is convenient to temporarily disable the stamndard login procedure on the physical console, usually found on all systems on the ttyS0.

In any case, the physical serial associated at the console can be found reading a kernel file:

# Raspbian on CM3
cat /sys/devices/virtual/tty/console/active
tty1 ttyS0 ttyAMA0

# Debian 8.0 on RoadRunner
cat /sys/devices/virtual/tty/console/active

In Unix the consoles are managed using the program getty (agetty in modern distributions). getty opens the serial line file and, when it detects a connection, it prompts for a username and runs the login program to authenticate the user.

In the old systems, where the first process is the infamous init , the getty processes were started from the /etc/inittab with the respawn option active so that when it should terminate, it will be restarted.

In order to bypass the password request the a otpion has to be added to the command lineof getty.

For inittab it is enough to edit it and force the reading with

init q

next the getty have to be killed, init will restart it automatically.

For recent systems using systemd, follow the procecure below.

Edit the service file

nano /lib/systemd/system/serial-getty@.service

the row starting with ExecStart has to be changed from

ExecStart=-/sbin/agetty --keep-baud 115200,38400,9600 %I $TERM


ExecStart=-/sbin/agetty <b>-a root</b> --keep-baud 115200,38400,9600 %I $TERM

after that the systemd have to be forced to re-read the files

systemctl daemon-reload

lastly the service has to be restarted

systemctl restart serial-getty@ttyS0.service

For CM3 sudo has to be preponed to all commands and the user should be changed from root to pi.


SystemV the old way

A few pieces of old way are still present and working as for example /etc/rc.local where all the applications that implement the system functionality are started.

A typical rc.local contains:

cat /etc/rc.local

#!/bin/sh -e
# rc.local
# This script is executed at the end of each multiuser runlevel.
# Make sure that the script will "exit 0" on success or any other
# value on error.
# In order to enable or disable this script just change the execution
# bits.
# By default this script does nothing.

cd /home/acmesystems
/usr/bin/nohup /usr/sbin/python &
exit 0

In this example a Python program is started from application's home directory, and left in background (using the trailing ampersand). That works passably well till our python application never crashes: in such unfortunate case, our system only chance has, in order to regain the lost functionality, is to be manually restarted by user (probably cycling the power...).

Usually, the quick and dirty solution suggested is to add our application to the /etc/inittab file, specifyng a respawn in the line:

id:2345:respawn:/bin/sh /path/to/application/startup

in such case startup must be a proper script able to start out application (and probably it could be convenient to reuse this script as is even in /etc/rc.local).

Can systemd use as is a classic System file startup script ?

How it can be seen using mosquitto server, systemd is able to keep in account all System V entire set of scripts. Infact, if one take a look to mosquitto Debian package, the systemd service files cannot be found. There is instead teh usual file for the rc (System V):


this is the file that is referred using a soft link, in the several directories under /etc/rc.d. Really, even on Debian Stretch the rc subsystem seems to be there, at least lookning to tits configuration files.

That means systemd is able in some way to interpret the old files: but, as will be shown below, the story is more complex.

So, looking to a wlive working system, there is really a systemd service file active and running for mosquitto

pi@raspberrypi:~ $ systemctl list-unit-files --type=service | grep mosqu
mosquitto.service                            generated

excpet it has a differnet status than usual, it is generated instead of running .

therefore the System V files are present but not natively used, rather it are used by systemd in order to generate its .service files automatically and on the fly.

cat  /run/systemd/generator.late/mosquitto.service 
# Automatically generated by systemd-sysv-generator

Description=LSB: mosquitto MQTT v3.1 message broker

SuccessExitStatus=5 6
ExecStart=/etc/init.d/mosquitto start
ExecStop=/etc/init.d/mosquitto stop
ExecReload=/etc/init.d/mosquitto reload
pi@raspberrypi:~ $ systemctl status mosquitto
● mosquitto.service - LSB: mosquitto MQTT v3.1 message broker
   Loaded: loaded (/etc/init.d/mosquitto; generated; vendor preset: enabled)
   Active: active (running) since Wed 2018-04-18 21:01:34 CEST; 1 day 1h ago
     Docs: man:systemd-sysv-generator(8)
   CGroup: /system.slice/mosquitto.service
           └─408 /usr/sbin/mosquitto -c /etc/mosquitto/mosquitto.conf

Apr 18 21:01:33 raspberrypi systemd[1]: Starting LSB: mosquitto MQTT v3.1 message broker...
Apr 18 21:01:34 raspberrypi mosquitto[328]: Starting network daemon:: mosquitto.
Apr 18 21:01:34 raspberrypi systemd[1]: Started LSB: mosquitto MQTT v3.1 message broker.

That means it is not advisable to make any change to that .service as it would be overwritten on the next run.


Andrea Montefusco
Currently employed as network architect, always Internet working man, real C/C++ programmer in the past, network and Unix system engineer as needed, HAM Radio enthusiast (former IW0RDI, now IW0HDV), aeromodeller (a person who builds and flies model airplanes) since 1976 (ex FAI10655). - - -
Sergio Tanzilli (Autore - Progettista hw & sw - Imprenditore - Musicista dilettante, classe 1964)
Fondatore di Area SX srl, Acme Systems srl e del TanzoLab, si occupa dal 1974 di elettronica digitale, dal 1978 di microprocessori, dal 1992 di microcontroller, dal 1995 di Linux e pubblicazioni su Web, dal 2005 di progettazione, produzione e commercializzazione di schede Linux Embedded. - - -