Systemd Targets and Dependencies

Originally published on 2026-01-25

tl;dr: If you know the background details and just need some quick syntax reminders, checkout the summary at the bottom.


I modify systemd unit files maybe a couple of times per year. That’s often enough that it’s useful for me to understand them well, but not often enough for those details to stick in my brain.

So I wrote this for myself or for anyone else who might be in a similar situation. It’s for anyone who needs a quick refresher on systemd targets, Wants=, Requires=, WantedBy=, RequiredBy=, Before=, and After=.

Before I get into the real content, it’s useful to have an example systemd unit file in your head for context. I chose Tailscale (copy / pasted from my laptop running Archlinux):

# /usr/lib/systemd/system/tailscaled.service
[Unit]
# Details omitted for brevity
Wants=network-pre.target
After=network-pre.target NetworkManager.service systemd-resolved.service

[Service]
# Details omitted for brevity

[Install]
WantedBy=multi-user.target

The Dependency Graph

Systemd maintains a dependency graph of units.[1] These units can include, for example, generic “targets” (which describe overall system states) such as network-online.target, or specific background services like tailscaled.service. There are other kinds of systemd units, but these are the most common types we interact with.

This dependency graph is important because as your system changes state (online vs. offline, starting up vs. shutting down, etc.), certain things must happen in a specific order. For instance, it doesn’t make sense to start the Tailscale service before your DNS resolver is ready.

A key strength of systemd’s design is that you can precisely add entries to this graph without performing invasive surgery on your operating system.

Most of what follows applies regardless of whether you’re working with targets, services, or other types of systemd units — the concepts are largely consistent across unit types.

Activation States

When a unit is activated, it eventually reaches a final activation state: either “active” or “failed.” If a unit was never intended to be activated, it remains “inactive.”

Understanding these states is important for understanding the difference between “wanting” and “requiring” another unit.

Establishing a Basic Dependency Relationship

When writing a unit file, the most basic way to establish a dependency on another unit is using Wants= and Requires=.

In both cases, Wants= and Requires= cause systemd to include the dependent unit in the same transaction. The difference lies in how failure is handled.

Reverse Dependencies

The WantedBy= and RequiredBy= directives let you define a dependency from another unit back to yours — effectively reversing the dependency direction.

These directives cause symbolic links to be created. For example, if your unit specifies:

WantedBy=multi-user.target

systemd creates a symlink:

/etc/systemd/system/multi-user.target.wants/my-service.service → /usr/lib/systemd/system/my-service.service

This ensures your unit starts during the activation of multi-user.target.

What’s up with the [Install] section?

You may have noticed that Wants= and Requires= appear in the [Unit] section of a unit file, while WantedBy= and RequiredBy= belong in the [Install] section. Why? And what does “install” even mean here?

The [Install] section defines what happens when you enable a unit (i.e., configure it to auto-start at boot). So when you run:

systemctl enable tailscaled.service

systemd reads the [Install] section, finds the WantedBy=multi-user.target, and adds the Tailscale service to the dependency list of the multi-user.target unit.

This is how auto-starting works: systemd already plans to activate multi-user.target during every boot process, and since Tailscale is now included as a dependency, the service starts along with it.

Note: The [Install] section has no effect until you enable the service. Before enabling, the unit exists, but the dependency relationship isn’t set up.

Why You Need Before= and After=

Important: Wants=, Requires=, and similar directives do not control startup order. They ensure that certain units are started together in the same transaction, but not necessarily in any particular sequence. systemd may still start your unit in parallel with its dependencies.

If you need your unit to start only after another unit has reached an active (or failed) state, you must explicitly use After=. Similarly, if your unit must start before another, use Before=.

Also note: Before= and After= do not imply a dependency. If systemd wasn’t already planning to start the referenced unit, these directives have no effect. They only matter when the other unit is part of the current activation transaction.

That’s why Wants= or Requires= are often paired with After=. In most real-world cases, you need both:

The Tailscale Example

In the Tailscale example, you see this:

[Unit]
Wants=network-pre.target
After=network-pre.target NetworkManager.service systemd-resolved.service

Here’s what this configuration does:

Notice that NetworkManager.service and systemd-resolved.service are not listed in Wants=. That’s because Tailscale doesn’t want to depend on them — Tailscale can move mountains to handle all kinds of Linux DNS and networking configurations. The unit file only ensures the Tailscale service starts after those services, if they are part of the current activation sequence.

You’ll also see this:

[Install]
WantedBy=multi-user.target

This means that when multi-user.target is activated (e.g., at boot), systemd will also start tailscaled.service — but only if you’ve previously run systemctl enable tailscaled.service.

Summary

Directive Section Purpose
Wants= [Unit] Soft dependency: start together, ignore failure
Requires= [Unit] Hard dependency: start together, fail if dependency fails
Before= [Unit] Start this unit before the listed ones
After= [Unit] Start this unit after the listed ones
WantedBy= [Install] Enable auto-start by adding to another unit’s wants
RequiredBy= [Install] Same, but with hard dependency

Rules of thumb:

For most cases…

Footnotes

  1. Tools like systemd-analyze and systemctl list-dependencies let you inspect various parts of the dependency graph. On a typical desktop Linux system, the full dependency graph is huge. ↩︎

Other Posts