Start your application with systemd

Michael Graf
Digital Frontiers — Das Blog
4 min readOct 17, 2022
Photo by Jon Tyson on Unsplash

Most of us have to deploy our new server application on Linux systems.
While docker simplified this process there are still projects that rely on a plain Linux installation. And with the plain installation come the simple but akward problem how to start our application. In our IDE this is quite easy but in production there is no play button. Many people use workarounds like the `nohup`command or `screen`. This works for the moment but there is no auto start, and after a reboot nothing works anymore. So, this blog post explains how to setup your application on plain Linux using systemd.

Systemd replaced in many Linux distributions the old init-V init process. Its main purpose is a “system and service manager” and used to bootstrap the user space processes of the operating system. While it brings many advantages, the way how we define startup scripts changed completely. So, let’s have a look at it.

Define the Systemd startup script

Previously we had to define init.d start-stop shell scripts in /etc/init.d/ and symlink them to the correct runlevel. With systemd, we don’t have to write a complete script that handles the forking and daemon mode of our application. We just create a simple .service unit file under /etc/systemd/system. This file has three sections: Unit, Service, and Install. Let’s have a look at it:

[Unit]
Description=Our super simple application
[Service]
Type=simple
ExecStart=/usr/local/bin/our-application
[Install]
WantedBy=multi-user.target

The Unit and Install sections are pretty self-explaining. The Description= item describes our service. The WantedBy= item defines that our service should be started in the multi-user target. This target is equal to runlevel 5 and should be right for most applications and services we create.

The Service section is more interesting, as it specifies the items related to our service. The most important item is ExecStart=. It defines the command to start our application. In the previous example, we just started a binary, but we can also add arguments. For example, to start a Java main class from a Jar file a .service unit file could look like this:

[Unit]
Description=Starts a jar file
[Service]
Type=simple
ExecStart=java -jar path/to/your/jarfile.jar fully.qualified.package.Application
[Install]
WantedBy=multi-user.target

Let’s have a look at the second item Type=. It defines the startup type of our service. Systemd defines many different types: simple, exec, forking, oneshot, dbus, notify, and idle. The most important types for us are simple, exec, and forking.

Type simple

The type simple is used when our application is running in foreground. You can easily check this by starting your application on the command line. When the executed command blocks and you see the output of your application on the terminal it is running in foreground.

Type forking

If you run the command in the terminal and it finishes but your application still runs in the background use forking. In this case, you also need to define how to stop your application. You can do this with the ExecStop= item.

Type exec

The difference between simple and exec is trickier. In *nix systems we first fork and then execute the child process.
With simple, systemd marks the services as started directly after the fork without checking the result of the exec command. Type exec will check if the call was successful. But it can lead to race conditions in the underlying *nix-privilege system. Normally you want to use exec but keep the possibility of a deadlock in mind.

Start the service

The next step is to register our service in systemd. First, let us check the service file permissions:

$ sudo chmod 644 /etc/systemd/system/ourapplication.service

Now we can start our service. By calling the systemctl command.

$ sudo systemctl start ourapplication

To check if our service is running systemctl provides the status command:

$ sudo systemctl status ourapplication

When everything is working, we can use enable to mark our service for autostart when the system boots.

$ sudo systemctl enable ourapplication

There are some more handy commands that you should know:

$ sudo systemctl stop ourapplication
$ sudo systemctl restart ourapplication
$ sudo systemctl disable ourapplication

stop stops our application, and restart restarts it. While disable removes it from autostart. Our config file still exists, and we can reenabled it with the enable command.

Startup order

Now your application starts on the next boot automatically, the same way docker would autostart our application, if we deploy it with the restart policy. But with docker and docker-compose it is complicated to define a startup order. With Systemd that’s not a problem. You can easily define dependencies between services by adding Wants=, Requires=, Before=, and After= to your service file:

[Unit]
Description=Starts a jar file
Wants=other.service
After=network.target other.service
[Service]
Type=simple
ExecStart=java -jar path/to/your/jarfile.jar fully.qualified.package.Application
[Install]
WantedBy=multi-user.target

The After= and Requires= items are looking identical in the first place. The difference is that After= just defines the order and Requires= models a dependency tree. If our service just defines Requires= but does not add the service to the After= item both services, our own and the required one, will start in parallel at the same time. When our service defines only the After= item it waits until the other service is started by someone else. Therefore, it is quite common to define a service in both items.

As you can see deploying an application to a plain Linux and configuring it’s autostart behavior with systemd is not a problem.

Systemd is a powerful tool, which has many more options and possibilities. Just look at the official manpage of systemd.

If you have any questions, suggestions, or criticism on this topic, please feel free to contact me.

You might be interested in the other posts published in the Digital Frontiers blog, announced on our Twitter account.

--

--