SystemD

The SystemD is a application startup manager designed in 2010. It is an answer to the previous manager SysV, which has it’s downsides. The main issue that was targeted to be fixed is long booting times due to the sequentially executed programs. The order of the application startups were controlled in etc/init.d directory by manually manipulating the priorities. This was tedious solution and hard to maintain in a longer time span. The SystemD resolves the issue by creating a centralized tool for maintaining dependencies and run levels - the systemctl.

The new way of Linux startup has it’s fans and opponents due to the additional complexity it brings. Therfore on some distributions such Linux Alpine you will not found SystemD solution, instead they are shipping OpenRC which is flavored version of SysV. ## The “unit”

The most atomic structure is unit which describes the program that suppose to be run. The file can be stored in few places in the system:

Creating the services file will be automatically visible by the systemctl tool in the auto-completion list.

The unit file are following some standardized naming convention (but are not mandatory). The most important are listed below:

Service unit .service A system service.
Target unit .target A group of systemd units.
Device unit .device A device file recognized by the kernel.
Mount unit .mount A file system mount point.
Swap unit .swap A swap device or a swap file.
Timer unit .timer A systemd timer.
System service .service A system service

More of the examples can be found on RedHat Docs1. Registered paths can be also displayed with:

systemd-analyze unit-paths

Structure of the unit

The unit file is a text based file which contains three main sections:

Example service created for custom program that overwrites the keyboard behavior:

[Unit]
Description=Thinkpad keyboard backlight service

[Service]
User=root
Group=root
ExecStart=/usr/bin/thinkpad-keybacklight
Type=exec
Restart=always

[Install]
WantedBy=default.target

The unit file has description of “Thinkpad keyboard backlight service”, and is executing /usr/bin/thinkpad-keybacklight. The program is suppose to be run by the root and in case of any error the program should be automatically restarted. The unit file must be run on the defualt synchronization points - what is after the text or graphical environment is loaded.

Targets

Targets are the synchronization points, it is alternative to the run levels which were used in the SysV. By targeting the unit file on a certain target we can loosely say after which booting stage our program suppose to be launched. Targets allow to create dependency tree much easier, what results in faster system booting times, because not all programs are not strictly chained together. We can try to imagine targets as a group of programs that must be run together to achieve the synchronization point. In case we need some dependency for certain unit within the group we can use dedicated settings in the unit file like WantedBy=sample.service.

You can check what kind of targets are defined in your system with:

systemctl list-units --type=target

The most important are usually:

  1. poweroff.target - When the device is power off
  2. rescue.target - The device is in rescue mode
  3. multi-user.target - Text mode with network interfaces running up
  4. graphical.target - The graphical environment works
  5. reboot.target - Reboot

When creating simple services we can use default.target which is usually pointing at the multi-user or graphical target. The service will be reached when almost all other services are up and running, so we can take advantage of those in our programs.

Unit file settings

There if a lot of other settings that might be useful when creating own unit files. Everything is accessible in the man systemd.services or on freedesktop3.

systemctl

The systemctl is the main application which allows us to control the systemD environment. With the command we can instruct which units should be enabled/disabled, restart them, check logs, statuses etc.

To see the list of currently running services use:

systemctl list

To see details about the service use:

systemctl status <service>
● thinkpad-keybacklight.service - Thinkpad keyboard backlight service
     Loaded: loaded (/etc/systemd/system/thinkpad-keybacklight.service; enabled; preset: enabled)
     Active: active (running) since Wed 2026-03-18 16:00:59 CET; 1 month 1 day ago
   Main PID: 1355 (thinkpad-keybac)
      Tasks: 1 (limit: 37887)
     Memory: 712.0K (peak: 2.1M)
        CPU: 15min 2.869s
     CGroup: /system.slice/thinkpad-keybacklight.service
             └─1355 /usr/bin/thinkpad-keybacklight

Mar 18 16:00:59 username systemd[1]: Starting thinkpad-keybacklight.service - Thinkpad keyboard backlight service...
Mar 18 16:00:59 username systemd[1]: Started thinkpad-keybacklight.service - Thinkpad keyboard backlight service.

The command will show not only the current status of the service, but also statistics regarding the CPU and memory consumption, and logs this program sent.

You will see the whole tree of services. Each slice means a cgroup, which has its own resource limits. This might be useful when we would like to create service that has limited accesses/resources. Also, you will be able to see what kind of other forks the process did.

When the new unit file is created first we need to enable it with:

systemctl enable <service>

then when it is enabled we must start it manually, or restart the device:

systemctl start <service>

The opposite actions are:

systemctl stop <service>
systemctl disable <service>.

Service stopped but not disabled will be restarted automatically after the device reboot. You can simply restart the unit by calling:

systemctl restart <service>

Overwriting services

During the system update default services will be overwritten with new versions. In case you need to edit the default services you should use drop-in file, which will be preserved and used instead of the default one. You can use:

systemctl edit <service>

or do it manually by creating in the services folder, subdirectory named <service>.service.d/override.conf

Refreshing unit files

When experimenting and modifying unit-files sometimes the new changes might not be consumed by the systemd. In that case the systemd should be reloaded with:

systemctl reload

Timers

The systemD is suppose to be the alternative to the CRON for executing periodic tasks. The “timer” type of units gives ability to schedule execution of units based on two types of clocks:

Units of timer type have .timer extension.

The unit itself can execute the program directly after the time elapses, or can trigger other unit. Triggering other unit is cleaner approach as this gives clear distinction between program execution, and trigger type. Keep in mind that the official manual suggest keeping the names of unit files the same (despite of extension) for easier tracing 4.

Monotonic timer

Example timer for running backup shell script

[Unit]
Description=Hourly Backup Timer
Requires=backup.service

[Timer]
OnBootSec=5min
OnUnitActiveSec=1h
AccuracySec=1min
Persistent=true

[Install]
WantedBy=timers.target

This will create a timer with description “Hourly Backup Timer”. That will run backup.service unit (notice it is using Requires not Wants what create much stronger dependency - the unit file cannot fail to end successfully). The timer is scheduled to be run for the first time 5 min after the device bootup, and repeated every 1h from the last activation. The Persistent forces the unit to be triggered even if it the timer was not activated fro the first 5 min from the start. This flag allow you to fix missed triggers. By specifying the timers target be ensure we run the unit after the timer is actually initialized in our system.

Calendar timer

You can adjust the timer schedule using options like OnCalendar= for specific times (e.g., OnCalendar=daily or OnCalendar=*-*-* 02:00:00 for 2 AM daily).

For the common patterns for e.g. to run every 1 min use:

man systemd.time

The following special expressions may be used as shorthands for longer normalized forms:

journalctl

For further debugging using logs we can take advantage of the journalctl command to see all logs coming from our unit test file:

journalctl -u <service>
Category: Linux