
Basics
If you’ve never created your own services before, let’s start with the basics. Systemd operates with abstract units, which come in different types, can provide various resources (such as processes, sockets, and abstract “targets”), and may require other resources to start.
The most common type of resource is a service. Files describing services and other related components are located in the /
directory. To allow systemd to recognize a new service, you simply need to place your file in this directory. If the service did not previously exist, systemd will read the file and load it into memory. However, if you edit the file of an already running service, remember to have systemd re-read the files using the command sudo
!
Oneshot Services: Farewell to rc.local
At one time, the standard way to add command execution during system startup was to append them to /etc/rc.local. The obvious drawback of this method is the lack of ways to track their execution success. With systemd, it’s easy to create a custom oneshot service for this purpose, which can be managed through systemctl like any other service. In this case, systemd will execute the command and consider the service startup successful if it exits with a zero code.
Save the following file to /
:
[Unit]
Description=Dumb test[Service]
ExecStart=/bin/true Type=oneshot[Install]
WantedBy=multiuser.target
No additional actions are needed, and now you can manage it just like system services: start it with sudo
, enable it to run at boot with sudo
, and so on.
Turning Any Program into a Service
Any long-running process can be easily turned into a service using the Type=idle
option. In this case, systemd will intercept standard input/output streams and monitor the process’s lifecycle.
To demonstrate, let’s write a Python program that simply outputs a message in an infinite loop. Save the following script in /
:
import timewhile True: print("Test service is alive") time.sleep(5)
Next, create a service file for it at /
:
[Unit]
Description=Smart test[Service]
ExecStart=/usr/bin/python3 -u /usr/local/bin/test.py Type=idle KillMode=process SyslogIdentifier=smart-test SyslogFacility=daemon Restart=on-failure[Install]
WantedBy=multiuser.target
Now you can start our service and verify that it’s working:
$ sudo systemctl status smart-test.service
smart-test.service - Smart test Loaded: loaded (/usr/lib/systemd/system/smart-test.service; static; vendor preset: disabled) Active: active (running) since Fri 2019-10-25 16:25:18 +07; 1s ago
Main PID: 19893 (python3) Tasks: 1 (limit: 4915) Memory: 3.5M
CGroup: /system.slice/smart-test.service
└─19893 /usr/bin/python3 -u /usr/local/bin/test.py
The program’s standard output is written to journald, and you can view it using journalctl
. Out of curiosity, let’s take a look at how the Restart=on-failure
option works. We’ll stop our process using kill
and check the logs:
systemd[1]: Started Smart test.
Test service is alive
Test service is alive
smart-test.service: Main process exited, code=killed, status=9/KILL
smart-test.service: Failed with result ‘signal’.
smart-test.service: Service RestartSec=100ms expired, scheduling restart.
smart-test.service: Scheduled restart job, restart counter is at 1.
Stopped Smart test.
Started Smart test.
Test service is alive
For true demons, you should use the forking
type, but we won’t go into details here — the authors of such packages likely already know what to do.
Dependencies and Startup Order
There are numerous options for configuring dependencies in systemd. It’s important to highlight that it has two separate mechanisms for specifying the order of service startup and the dependencies between them.
Service Startup Order
The order in which services are started is determined by the Before
and After
options. If the configuration for the service foo
specifies After=bar.
and both services need to be started, systemd will first attempt to start bar
and then foo
.
However, the option After=bar.
by itself will not enable the service to start. Moreover, it will not influence the decision to run foo, even if the start of bar fails.
The reason for these options is systemd’s ability to start services in parallel.
Let’s consider a typical web server setup with an FCGI web application, a database management system, and a reverse proxy. The order in which you start the FCGI process and the reverse proxy is not crucial. Requests will function only when both are running, but an “incorrect order” won’t prevent them from starting properly.
If a web application requires data from a database for initialization, it’s not enough to just ensure that both the FCGI process and the database management system (DBMS) are running. You must start the application only after the DBMS has fully launched. This is where the Before/
options come into play.
Dependencies
Dependencies come in two types: soft and hard. If both services start successfully, there is no difference between them. The distinction becomes relevant if one of the services fails to start: with a soft dependency, the dependent services will still be launched, whereas with a hard dependency, systemd won’t even attempt to start them.
Soft dependencies are specified using the Wants=
option in the [
section. Here’s an example from sshd.
:
[Unit]
Description=OpenSSH server daemonDocumentation=man:sshd(8) man:sshd_config(5)
After=network.target sshd-keygen.targetWants=sshd-keygen.target
The sshd-keygen.
evidently serves to generate a host key if it is missing. Technically, sshd cannot start without a host key, so why the authors decided to make this a non-mandatory dependency is anyone’s guess. Perhaps they assumed that in most cases, the key already exists and that an outdated key is better than a non-functional SSH.
info
When you copy settings from distribution packages, be cautious. Distribution maintainers are only human, and they can ship sub‑optimal—or outright incorrect—configurations. Plus, what works for one service won’t necessarily work for another, so cross‑check with the documentation and run tests before you ship.
You can specify strict dependencies using the Requires
option. For example, in the systemd-journal-flush.
, there’s an option Requires=systemd-journald.
— it’s evident that sending a journald command is impossible until it’s running.
There are variations of this option, for example, RequiresMountsFor
. Let’s take a look at the logrotate.
file:
[Unit]
Description=Rotate log filesDocumentation=man:logrotate(8) man:logrotate.conf(5)
RequiresMountsFor=/var/log
To function properly, logrotate only needs access to the directory containing the logs, and nothing else. The RequiresMountsFor=/
option ensures this by allowing the service to start as soon as the directory containing the /
path is mounted, even if it isn’t located in the root partition.
Integrating into Dependencies of External Services
In systems using System V init, adding dependencies to someone else’s service required editing its script. Such changes, obviously, wouldn’t survive a system update, so the only way to make them permanent would be to rebuild the package.
In systemd, there are a few ways to tackle this issue. If you specifically need to create a dependency on another service, you might consider using reverse dependency options: WantedBy
and RequiredBy
. These should be placed in the [
section, not the [
section. There are two caveats: these options are only processed when a service is installed with systemctl
and, as with everything in systemd, they may not function consistently across all versions.
The second option, which allows you to change any settings, is to copy the service file to /
. If a file exists in both directories, the file from /
takes precedence.
A third, less radical option is to create a file like /
and include only the necessary configurations there.
As an example, let’s pretend that our smart-test service isn’t actually ours, and let’s add a dependency on sshd.service using the third method. Create a file at /
with the following content:
[Unit]
Requires=sshd.service
Now, the sshd.
will start alongside the smart-test.
, even if it was previously turned off.
If the order of launching the two services is important in addition to the fact that they’re both running, make sure to specify this using the Before
or After
options.
Default Dependencies
It’s important to note that systemd automatically creates dependencies for each service by default. In most cases, this is beneficial because user services typically require a fully initialized system to function properly. However, if you want to introduce a new step in the system boot process, you’ll need to remove these dependencies. This can be achieved by using the DefaultDependencies=no
option.
This template works well for services that need to start as early as possible:
[Unit]
Description=My early boot stepDefaultDependencies=noAfter=systemd-remount-fs.service
To review a service’s dependencies and ensure there are no unnecessary ones, you can use the command systemctl
.
Conclusion
Systemd is something you can love or hate, but you can’t ignore it—you need to know how to work with it. Hopefully, this knowledge will help you turn systemd to your advantage.

2023.04.19 — Kung fu enumeration. Data collection in attacked systems
In penetration testing, there's a world of difference between reconnaissance (recon) and data collection (enum). Recon involves passive actions; while enum, active ones. During recon,…
Full article →
2022.06.01 — Log4HELL! Everything you must know about Log4Shell
Up until recently, just a few people (aside from specialists) were aware of the Log4j logging utility. However, a vulnerability found in this library attracted to it…
Full article →
2023.07.07 — VERY bad flash drive. BadUSB attack in detail
BadUSB attacks are efficient and deadly. This article explains how to deliver such an attack, describes in detail the preparation of a malicious flash drive required for it,…
Full article →
2023.02.21 — Herpaderping and Ghosting. Two new ways to hide processes from antiviruses
The primary objective of virus writers (as well as pentesters and Red Team members) is to hide their payloads from antiviruses and avoid their detection. Various…
Full article →
2023.06.08 — Cold boot attack. Dumping RAM with a USB flash drive
Even if you take efforts to protect the safety of your data, don't attach sheets with passwords to the monitor, encrypt your hard drive, and always lock your…
Full article →
2022.02.09 — Dangerous developments: An overview of vulnerabilities in coding services
Development and workflow management tools represent an entire class of programs whose vulnerabilities and misconfigs can turn into a real trouble for a company using such software. For…
Full article →
2023.02.21 — SIGMAlarity jump. How to use Sigma rules in Timesketch
Information security specialists use multiple tools to detect and track system events. In 2016, a new utility called Sigma appeared in their arsenal. Its numerous functions will…
Full article →
2022.06.01 — First contact. Attacks on chip-based cards
Virtually all modern bank cards are equipped with a special chip that stores data required to make payments. This article discusses fraud techniques used…
Full article →
2023.02.21 — Pivoting District: GRE Pivoting over network equipment
Too bad, security admins often don't pay due attention to network equipment, which enables malefactors to hack such devices and gain control over them. What…
Full article →
2023.03.26 — Attacks on the DHCP protocol: DHCP starvation, DHCP spoofing, and protection against these techniques
Chances are high that you had dealt with DHCP when configuring a router. But are you aware of risks arising if this protocol is misconfigured on a…
Full article →