Systemd Units: Types, Structure, and Anatomy

When working with modern Linux systems, you’re almost certainly interacting with systemd, even if you don’t realize it. As the default init system in most major distributions (including Ubuntu, Debian, CentOS Stream, Fedora, and Arch), systemd is responsible for managing everything from services and mounts to timers and devices. At the heart of systemd is the concept of units.

In this article, we’ll dive deep into systemd units, exploring what they are, the different types available, how they’re structured, and how to read and write unit files like a pro.


What is a systemd Unit?

In systemd, a unit is a resource that systemd knows how to manage. Each unit is defined in a plain-text configuration file with a .unit_type suffix. These files define how and when systemd should start, stop, reload, or otherwise handle the associated resource.

Examples of units:

  • A service like nginxnginx.service
  • A mount point like /datadata.mount
  • A timer for periodic tasks → backup.timer

Types of systemd Units

Systemd supports several types of units. Each type corresponds to a specific kind of resource or function. Here are the most common ones you’ll encounter:

Unit TypeSuffixDescription
service.serviceManages background services (daemons).
socket.socketSocket-based activation for services. (xinetd alternative).
timer.timerTime-based activation for services (cron alternative).
mount.mountControls file system mount points.
automount.automountManages on-demand mounting of filesystems.
target.targetGrouping of units. Like a runlevel, but not exactly
device.deviceRepresents kernel devices detected by udev.
path.pathWatches a filesystem path and triggers services.
snapshot.snapshotCaptures the state of units (used internally).
swap.swapManages swap space.
slice.sliceResource control slice, usually for cgroups.
scope.scopeExternal processes started outside systemd.

Anatomy of a systemd Unit File

A systemd unit file is made up of a few clearly defined sections. Let’s break down the key ones using a simple myapp.service as an example.

[Unit]
Description=My Custom App Service
After=network.target

[Service]
Type=simple
ExecStart=/usr/local/bin/myapp
Restart=on-failure

[Install]
WantedBy=multi-user.target

[Unit] Section

Defines metadata and relationships to other units.

  • Description=: A human-readable description of the unit.
  • After= / Before=: Controls ordering with respect to other units.
  • Requires= / Wants=: Defines hard or soft dependencies.

[Service] Section

Used in .service files only. Defines how the service behaves.

  • Type=: Common values are simple, forking, oneshot, notify. It depends on what kind of executable you are going to run. For example: for most Bash scripts(if they should run in a loop, like a daemon) it is enough to use Type=simple. Regular daemons like crond, syslogd, or sshd usually run with Type=forking or Type=notify. To get more information, please take a look at the documentation.
  • ExecStart=: Command to run when the service starts. There are also ExecStartPre/ExecStartPost in case you need to do something before of after the service starting
  • ExecStop=: (Optional) Command to stop the service. There are also ExecSopPre/ExecStopPost in case you need to do something before of after the service stopping
  • Restart=: Defines restart behavior (e.g., on-failure, always).

[Install] Section

Defines how the unit is enabled/linked to targets.

  • WantedBy=: Tells systemd to start this unit when the listed target is reached (e.g., multi-user.target).
  • Alias=: Provides alternative names for the unit.

How Units Work Together

Units don’t operate in isolation. They’re part of a dependency tree. For example:

  • multi-user.target is a runlevel-like target that includes networked services.
  • If you enable myapp.service with WantedBy=multi-user.target, systemd creates a symlink in /etc/systemd/system/multi-user.target.wants/ pointing to your unit.

This is how services get auto-started during boot.


Where to Place Systemd Unit Files

Systemd loads unit files from various locations in this order:

  1. Runtime: /run/systemd/system/
  2. User configuration: /etc/systemd/system/
  3. Vendor defaults: /lib/systemd/system/ or /usr/lib/systemd/system/

To create a custom service, you typically place it in /etc/systemd/system/.


Common systemctl Commands

To interact with units, use systemctl:

# Reload systemd config after editing unit files
sudo systemctl daemon-reexec
sudo systemctl daemon-reload

# Start and stop a unit
sudo systemctl start myapp.service
sudo systemctl stop myapp.service

# Enable at boot and check status
sudo systemctl enable myapp.service
sudo systemctl status myapp.service

# View logs
journalctl -u myapp.service

# Show the configuration(includig the system limits)
systemctl show myapp.service

Best Practices

  • Use absolute paths in ExecStart commands.
  • Avoid overwriting vendor units directly. Use systemctl edit to create overrides in /etc/systemd/system/myapp.service.d/override.conf
  • Use Restart= to make your service resilient to crashes.
  • Use timers instead of cron where appropriate—they integrate better with systemd.

Final Thoughts

Understanding systemd units is essential for Linux system administration today. They offer a unified, extensible, and powerful framework for managing services and system resources. Whether you’re writing custom services or tweaking existing ones, mastering unit files gives you fine-grained control over your system.


Stay tuned for upcoming articles in this series where we’ll explore:

  • Writing robust systemd services with notify and watchdog support
  • Creating and managing systemd timers as cron alternatives
  • Debugging and troubleshooting systemd boots and services

Got questions or want me to cover a specific unit type in more depth? Drop a comment below or reach out!

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top