Creating a Linux service with systemd


What is systemd?Permalink
systemd is a Linux system tool initially developed by the Red Hat Linux team. It includes many features, including a bootstrapping system used to start and manage system processes. It is currently the default initialization system on most Linux distributions. Many commonly used software tools, such as SSH and Apache, ship with a systemd service.
It is simple to create a custom systemd service that will run any script or process you choose. Although there are several ways to run a script or start a process when your Linode boots, a custom systemd service makes it easy to start, stop, or restart your script, as well as configure it to start automatically on boot. systemd offers the advantage of using a standardized interface that is consistent across all Linux distributions that support it.


The program

Let’s create a small server using PHP. We’ll listen to UDP port 10000, and return any message received with



<?php
$sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
socket_bind($sock, '0.0.0.0', 10000);
for (;;) {
     socket_recvfrom($sock, $message, 1024, 0, $ip, $port);
     $reply = str_rot13($message);
     socket_sendto($sock, $reply, strlen($reply), 0, $ip, $port);
}

Let’s start it:
$ php server.php
And test it in another terminal:
$ nc -u 127.0.0.1 10000
Hello, world!
Uryyb, jbeyq!
Cool, it works. Now we want this script to run at all times, be restarted in case of a failure (unexpected exit), and even survive server restarts. That’s where systemd comes into play.

Turning it into a service

Let’s create a file called :
[Unit]
Description=ROT13 demo service
After=network.target
StartLimitIntervalSec=0[Service]
Type=simple
Restart=always
RestartSec=1
User=centos
ExecStart=/usr/bin/env php /path/to/server.php

[Install]
WantedBy=multi-user.target
You’ll need to:
  • set your actual username after 
  • set the proper path to your script in 
That’s it. We can now start the service:
$ systemctl start rot13
And automatically get it to start on boot:
$ systemctl enable rot13

Starting in the right order

You may have wondered what the  directive did. It simply means that your service must be started after the network is ready. If your program expects the MySQL server to be up and running, you should add:
After=mysqld.service

Restarting on exit

By default, systemd does not restart your service if the program exits for whatever reason. This is usually not what you want for a service that must be always available, so we’re instructing it to always restart on exit:
Restart=always
You could also use  to only restart if the exit status is not .
By default, systemd attempts a restart after 100ms. You can specify the number of seconds to wait before attempting a restart, using:
RestartSec=1

Avoiding the trap: the start limit

I personally fell into this one more than once. By default, when you configure  as we did, systemd gives up restarting your service if it fails to start more than 5 times within a 10 seconds interval. Forever.
StartLimitBurst=5
StartLimitIntervalSec=10
The  directive also has an impact on the outcome: if you set it to restart after 3 seconds, then you can never reach 5 failed retries within 10 seconds.
The simple fix that always works is to set . This way, systemd will attempt to restart your service forever.
It’s a good idea to set  to at least 1 second though, to avoid putting too much stress on your server when things start going wrong.
As an alternative, you can leave the default settings, and ask systemd to restart your server if the start limit is reached, using .

Is that really it?

That’s all it takes to create a Linux service with systemd: writing a small configuration file that references your long-running program.
Systemd has been the default init system in RHEL/CentOS, Fedora, Ubuntu, Debian and others for several years now, so chances are that your server is ready to host your homebrew services!

Create a Custom systemd ServicePermalink

  1. Create a script or executable that the service will manage. This guide uses a simple Bash script as an example:
    test_service.sh
    1
    2
    3
    4
    5
    6
    7
    8
    
    DATE=`date '+%Y-%m-%d %H:%M:%S'`
    echo "Example service started at ${DATE}" | systemd-cat -p info
    
    while :
    do
    echo "Looping...";
    sleep 30;
    done
    This script will log the time at which it is initialized, then loop infinitely to keep the service running.
  2. Copy the script to /usr/bin and make it executable:
    sudo cp test_service.sh /usr/bin/test_service.sh
    sudo chmod +x /usr/bin/test_service.sh
    
  3. Create a Unit file to define a systemd service:
    /lib/systemd/system/myservice.service
    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    [Unit]
    Description=Example systemd service.
    
    [Service]
    Type=simple
    ExecStart=/bin/bash /usr/bin/test_service.sh
    
    [Install]
    WantedBy=multi-user.target
    This defines a simple service. The critical part is the ExecStart directive, which specifies the command that will be run to start the service.
  4. Copy the unit file to /etc/systemd/system and give it permissions:
    sudo cp myservice.service /etc/systemd/system/myservice.service
    sudo chmod 644 /etc/systemd/system/myservice.service
    
    For more information about the unit file and its available configuration options, see the systemd documentation.

Start and Enable the ServicePermalink

  1. Once you have a unit file, you are ready to test the service:
    sudo systemctl start myservice
    
  2. Check the status of the service:
    sudo systemctl status myservice
    
    If the service is running correctly, the output should resemble the following:
      
    ● myservice.service - Example systemd service.
       Loaded: loaded (/lib/systemd/system/myservice.service; enabled; vendor preset: enabled)
       Active: active (running) since Tue 2018-05-01 18:17:14 UTC; 4s ago
     Main PID: 16266 (bash)
        Tasks: 2
       Memory: 748.0K
          CPU: 4ms
       CGroup: /system.slice/myservice.service
               ├─16266 /bin/bash /usr/bin/test_service.sh
               └─16270 sleep 30
    
    May 01 18:17:14 localhost systemd[1]: Started Example systemd service..
    May 01 18:17:14 localhost cat[16269]: Example service started at 2018-05-01 18:17:14
    May 01 18:17:14 localhost bash[16266]: Looping...
    
    
  3. The service can be stopped or restarted using standard systemd commands:
    sudo systemctl stop myservice
    sudo systemctl restart myservice
    
  4. Finally, use the enable command to ensure that the service starts whenever the system boots:
    sudo systemctl enable myservice
    
      
    Created symlink from /etc/systemd/system/multi-user.target.wants/myservice.service to /lib/systemd/system/myservice.service.
    
    
  5. Reboot your Linode from the Linode Manager and check the status of the service:
    sudo systemctl status myservice
    
    You should see that the service logged its start time immediately after booting:
      
    ● myservice.service - Example systemd service.
       Loaded: loaded (/usr/lib/systemd/system/myservice.service; enabled; vendor preset: disabled)
       Active: active (running) since Wed 2018-05-02 15:03:07 UTC; 48s ago
     Main PID: 2973 (bash)
       CGroup: /system.slice/myservice.service
               ├─2973 /bin/bash /usr/bin/test_service.sh
               └─3371 sleep 30
    
    May 02 15:03:07 localhost systemd[1]: Started Example systemd service..
    May 02 15:03:07 localhost systemd[1]: Starting Example systemd service....
    May 02 15:03:07 localhost bash[2973]: Looping...
    May 02 15:03:37 localhost bash[2973]: Looping...
    
    

TroubleshootingPermalink

  • “Example service started at …” line does not appear in the output of the status command. The systemd-cat output is not reliable because of a race condition. As a workaround update the test_service.sh file as follows:
    test_service.sh
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    
    info=/tmp/myservice-systemd-cat-pipe-info
    mkfifo "$info"
    trap "exec 3>&-; rm $info" EXIT
    systemd-cat -p info < "$info" &
    exec 3>"$info"
    
    DATE=`date '+%Y-%m-%d %H:%M:%S'`
    echo "Example service started at ${DATE}" | systemd-cat -p info
    
    while :
    do
    echo "Looping...";
    sleep 30;
    done

Post a Comment

Previous Post Next Post