skip.link.title

Creating a Linux service with systemd

  • You can find the source code for this video in my GitHub Repo.

Intro

Once in a while, you need to create a script and run it in the background. There are a few approaches that you can take. Probably the simplest one is to just add a & to the end of the command. Also, if you want to close the terminal, append nohup. If your script exists for some reason, you would need to restart it manually.

The better approach is to use the systemd system and service manager for Linux. In this tutorial, I'll show you how to create a simple systemd service that can run your script in the background and restart it in case of failure.

Create Python HTTP Server

First of all, let's create a simple Python HTTP server.

vim my_server.py

We're going to run in on localhost:8080. It will accept only HTTP GET requests and return Hello string back to the client. We can also allow the user to exit the program by hitting Control-C or Delete key on a keyboard.

my_server.py
from http.server import BaseHTTPRequestHandler, HTTPServer

hostName = "localhost"
serverPort = 8080


class MyServer(BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200)
        self.send_header("Content-type", "text/html")
        self.end_headers()
        self.wfile.write(bytes("Hello\n", "utf-8"))


if __name__ == "__main__":
    webServer = HTTPServer((hostName, serverPort), MyServer)
    print("Server started http://%s:%s" % (hostName, serverPort))

    try:
        webServer.serve_forever()
    except KeyboardInterrupt:
        pass

    webServer.server_close()

Let's try to run in the foreground first and make sure that it works.

python3 my_server.py

Then open a new tab and try to access the server with the curl command.

curl localhost:8080/hello

If you get a Hello message from the server, that means it works. Now we want this HTTP server to run all the time, be restarted in case of a failure, and even survive Linux restarts. That’s where systemd comes into play.

Create Systemd Linux Service

Let's go ahead and create a systemd service file.

sudo vim /etc/systemd/system/my-server.service

It's the bare minimum configuration to get you started. Make sure to specify your Linux User and a ExecStart command to start the server. You can also provide Environment variables to your script.

The important parameter is a Restart. You can either set it to always or restart your script only on-failure. By default, when you configure Restart=always as we did, the systemd gives up restarting your service if it fails to start more than 5 times within a 10 seconds interval.

The RestartSec 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 setStartLimitIntervalSec=0. This way, the systemd will attempt to restart your service forever. It's a good idea to set RestartSec to at least 1 second, though, to avoid putting too much stress on your server when things start going wrong.

my-server.service
[Unit]
Description=My Server
After=network.target
StartLimitIntervalSec=0

[Service]
Type=simple
ExecStart=/usr/bin/python3 /home/ubuntu/my_server.py

User=ubuntu

Environment=ENV=production

Restart=always
RestartSec=1

[Install]
WantedBy=multi-user.target

Now let's test the system service. Go ahead and start the server.

sudo systemctl start my-server

To automatically start your service when Linux boots up, enable the service.

sudo systemctl enable my-server

Then check the status.

sudo systemctl status my-server

In case it fails, and you need to find a reason, or you just want to follow the logs, you can use a journactl command.

journalctl -u my-server -f --no-pager

Use curl to test.

curl localhost:8080/hello

The last thing that I want to discuss in this video is that you can manage dependencies for your service. For example, if your server needs a database, you may use After=postgresql.service directive to instruct systemd to start your python server only after the Postgres starts. This will only work when the Linux boots up and both services are enabled.

If you want, you can add another Requires=postgresql.service directive. Even if the Progress is down and you try to start your Python server, the systemd first starts the Postgres and then your program.

Systemd has been the default system and service manager for Linux in RHEL/CentOS, Fedora, Ubuntu, Debian, and others for several years now, and it should become your go-to tool to run services and your scripts in the background.

top.title