Ever wondered how to create a Linux service to manage your executables?
This article was originally written in my personal blog abhinand5.github.io
Sometimes you might build an application and by nature, it might be important to keep it running forever. For example, a server program where downtimes are simply unacceptable or an agent that has to always run in the background without requiring any manual intervention to start, stop or restart. There are multiple ways of doing this — keeping it running forever. But in this article, I’ll cover a standard Linux way of doing this — this can also be used in production. Of course, I am not talking about (almost) self-managed environments like docker, AWS Lambda, Heroku etc… This is going to be only about plain old Linux servers/desktops.
What is systemd?
Systemd is a system manager and initialization tool that has become widely popular in recent years.
Systemd is also the default init system in most of the well known Linux distributions. So knowing how to create a service in
systemd might not be all that bad after all. Some of the popular distros that run on systemd by default include,
- Arch Linux
- Debian Jesse or newer
- CentOS 7 / RHEL 7 or newer
- Ubuntu 16.04 or newer
- Fedora 15 or newer
The main purpose of an init system like
systemd is to initialize the components that need attention after the Linux kernel itself boots up. But it also offers a mechanism to manage system services and daemons as long as the system is running, in form of
systemctl which is what we are interested in. However, I am not going to delve too much into the details of how systemd works, that in itself needs several articles.
There are many services that the OS runs at any moment, for example, the keyboard setup service which enables the use of keyboard with your preferred layout in the console. There are several of these services which always run in the background to bring you a more seamless experience.
You can see a list of all services using the command below
$ systemctl list-units --type=service
As you can see from the image above, Postgres or any other database management system also runs as a service in Linux. You can check the status of any service like this,
$ systemctl status postgresql
You may have noticed that the system services end with
.service extension, these are nothing but files that are used to define a service in
systemctl is smart enough to understand that you’re looking for a service and correctly display the status, however, you can add in the
.service extension as a good practice.
You can view the contents of the service file like this,
$ systemctl cat postgresql
This file is formally known as a Unit file which is used to describe a system service. We will talk about this in the next section.
Creating our own systemd service
The magical file that lets us create a systemd service is called a Unit file. I say magical because it is much easier to write and setup. For a functioning service, this file must be present only in the following directories
$ man systemd.unit
These are the directories from which
systemd will load the information from. The one that we are interested in mainly is
/etc/systemd/system where the admin created system units can reside.
For this article, I’ll use a simple echo server written in Python which I binarized with
pyinstaller to show you how to run an executable as a service. Alternatively, we will also see how to directly run the python file from a shell script and turning that into systemd service.
Here is the simple python server I’m using,
For now, you can start creating a unit file here and then finally when it is done move it to
/etc/systemd/system the directory.
Creating a unit file with the name of your service
$ touch echo-server.service
You can edit it with any editor of your choice, I’ll be using the inbuilt
nano editor to keep it simple.
We can start by defining the description of the service we are creating.
Description=Service that keeps running the echo-server from startup.
Next, we should define when the service actually should start (run level)
multi-user.target normally defines a system state where all network services are started up and the system will accept logins, but a local GUI is not started. This is the typical default system state for server systems, which might be rack-mounted headless systems in a remote server room. This is just another way of telling the service should start at run-levels 3,4 and 5.
If your service needs an internet connection to even start, you should wait till the basic network functionalities are established. This can be achieved by including the following line under the
[Unit] section of the file.
Now it is time to define the service itself, this is done under the
[Service] section of the unit file.
Now let's see what each line in this section is doing.
This means we are specifying how to run the service using
ExecStart in the next line, where we specify the path to the binary executable file which
systemd will use to start up the service. There are also other types of services like
forking — which is used when the service forks a child process, exiting the parent process almost immediately. This tells
systemd that the process is still running even though the parent exited. These are the most commonly used ones, for more types, you can verify the documentation of
You can also mention the working directory of the service which is optional.
This means that the service will be restarted automatically in 5 seconds if the service fails or aborts at any time. This can be particularly useful for servers and agents running in the background.
The above lines help in automatically redirecting the output from the service to a log file.
Running the Service and monitoring it
Now since the unit is created, we can move it to the
In the screenshot above I am moving the unit file to the right directory and verifying it by piping ls command in the systemd directory with the file name of the unit file we created.
Next, let us reload
systemctl to make our new unit file visible.
$ sudo systemctl daemon-reload
Now enable our service using the command below,
$ sudo systemctl enable echo-server.service
If everything is set up correctly, you should get an output like this saying created symlink.
Now finally you can start your service using this command,
$ sudo systemctl start echo-server.service
This won’t print any output on the terminal.
Next, try to check the status of the service using this command,
$ sudo systemctl status echo-server.service
If everything works correctly, you’ll be able to see the service status as active and running. Congratulations! You’ve created your first Linux service.
If you want to see the logs of the service,
$ journalctl -f -u echo-server.service
You can now reboot to verify if the service is starting automatically.
As seen in this image, the system has booted up less than a minute ago and we can see that the service has started automatically just 39 seconds ago which is impressive.
Run a shell script as service (Alternative)
This method can be useful when you do not have a standalone binary executable for your application. First, create a shell script that invokes your program. In this case, we should write the python command that executes our program.
Create a shell script with the name of your choice.
$ touch start-echo-server.sh
Now edit the shell script and add the command that invokes your program
PYTHON_PATH=/home/abhi/.local/bin/.virtualenvs/main/bin/python$PYTHON_PATH $SCRIPT_PATHexit 1
You might know the script path, but you may not know the python path. This shell script will work even if you’re using a python virtual environment (venv). If you’re not using a venv, just execute the command below, otherwise activate the venv and then execute the command below, add the output as
PYTHON_PATH in the shell script.
$ which python
Do not forget to give executable permission for the script, otherwise, this will not work.
$ chmod +x start-echo-server.sh
Now save this shell script and move it to
/usr/sbin the directory. Why there? Because that is where we should put the general system-wide binaries that need admin privileges to run.
$ sudo mv start-echo-server.sh /usr/sbin
Now you only need to change one line in the unit file —
The rest of the procedure is exactly the same. If you have succeeded in creating the service you should get a status like this.
Removing the systemd service
Now let's see how to remove the service completely from the system.
- Stop the service if it is running
$ systemctl stop echo-server.service
2. Disable the service
$ systemctl disable echo-server.service
3. Delete the Unit file
$ sudo rm /etc/systemd/system/echo-server.service
4. Reload systemctl
$ systemctl daemon-reload
5. Delete the shell script (optional — if you’ve used a shell script)
$ sudo rm /usr/sbin/start-echo-server.sh
6. Clear out the units that have failed (Optional)
$ systemctl reset-failed
Being able to create these services can be really helpful in many cases, this is just one example where I’ve shown you how to do this but this method can be adopted for several problems where
systemd services can do the job elegantly most of the times without involving any third-party tools.
Thanks a ton for reading all the way. If you like the article feel free to hit that clap icon which motivates me to write more good articles.