Skip to main content
RunBook Academy

LinuxVII · systemd and Service ManagementTransactions

Targets, dependencies, and ordering

Intermediate⏱ ~10 minbashsystemctlsystemd-analyze

What you'll learn

  • Trace the start order systemd computes on boot
  • Distinguish Requires, Wants, Requisite, and BindsTo
  • Understand the difference between After/Before and the dependency types
  • Diagnose "why is my service starting before X" issues

Prerequisites

Verified against Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL 9.x · Rocky Linux 9.x · AlmaLinux 9.x · Linux kernel 6.1 LTS / 6.6 LTS · systemd 255+ · OpenSSH 8.7p1 (RHEL 9) / 9.6p1 (Ubuntu 24.04) · nftables 1.0.x · chrony 4.x · Pacemaker 2.1.x · Corosync 3.1.x · 2026-08-09

Not yet marked complete on this device.

systemd does not start services one at a time. On every state change — boot, runlevel change, manual systemctl start — it computes a transaction: a list of jobs that bring the system to the desired state.

How systemd computes the transaction

flowchart LR
  STATE["Current state"]
  TARGET["Desired state"]
  GRAPH["Dependency graph"]
  TX["Transaction<br/>(ordered jobs)"]
  APPLY["Apply jobs"]

  STATE --> GRAPH
  TARGET --> GRAPH
  GRAPH --> TX
  TX --> APPLY

For each unit, systemd asks:

  1. Is this unit supposed to be active in the desired state?
  2. Is it currently active?
  3. Does the dependency graph allow activation?

If yes to 1 and 3 and no to 2, the unit is queued for start. If yes to 1 but the dependency graph says a required unit is inactive, the start is deferred until the dependency is active.

Requires, Wants, Requisite, BindsTo

DirectiveOn startOn stop
Requires=Start together; if dependency fails, this unit failsStop together
Wants=Soft preference; ignore failure of dependency(no effect)
Requisite=Like Requires, but dependency must already be active when this unit starts(no effect)
BindsTo=Like Requires, but stronger — if dependency is stopped, this unit is also stoppedAlways stopped together
PartOf=(no effect on start)When the LISTED unit is stopped or restarted, this unit is stopped/restarted too. One-way: changes to this unit do not affect the listed unit
Read-only / Safedependency types
$ grep -E '^(Requires|Wants|Requisite|BindsTo|PartOf)' /usr/lib/systemd/system/nginx.service /usr/lib/systemd/system/sshd.service 2>/dev/null
/usr/lib/systemd/system/sshd.service:Wants=network-online.target
/usr/lib/systemd/system/sshd.service:After=network-online.target

Illustrative output

After and Before — ordering, not dependency

After= and Before= are NOT dependencies. They are ordering constraints: “this unit starts AFTER the named unit”, or “this unit starts BEFORE the named unit”. The named unit is not pulled in if it is not already wanted by something else.

[Unit]
After=network-online.target
Wants=network-online.target

This pair is common: “after” for ordering, “wants” to pull in the dependency. Either alone is incomplete:

  • After= alone: ordering only — if network-online.target is not already going to start, sshd may start before the network.
  • Wants= alone: dependency without ordering — sshd may race the network.

Reading the actual order

Read-only / Safesystemd-analyze
$ systemd-analyze dot sshd.service 2>/dev/null | head -20; systemd-analyze
Bootup finished in 4.234s (kernel) + 12.345s (userspace) = 16.579s
graphical.target reached after 12.001s in userspace

Illustrative output

Conflicts

Conflicts= means two units cannot be active at the same time. Starting one stops the other. Used for exclusive resources:

[Unit]
Conflicts=umount.target

Conflicts produces interesting behaviour: if unit A is running and you systemctl start B, systemd stops A and starts B. Useful when services claim exclusive resources (a network interface, a serial port).

Targets

Targets group units for collective state changes. Boot into multi-user.target means “every unit that multi-user.target Wants or Requires must be active”. Targets themselves do nothing — they are just collection points.

Read-only / Safemulti-user.target deps
$ systemctl list-dependencies multi-user.target | head
multi-user.target
● ├─accounts-daemon.service
● ├─chrony.service
● ├─cron.service
● ├─dbus.service
● ├─getty.target
● ├─nginx.service
● ├─sshd.service
● └─systemd-update-utmp.service
...

Illustrative output

Common dependency bugs

  • My service starts before its dependency: missing After=.
  • My service starts but the dependency does not: missing Wants= or Requires=.
  • My service refuses to start: required dependency is in failed state; fix the dependency first.
  • Cycle detected: two units depend on each other. systemd refuses to start either until the cycle is broken.
Read-only / Safefailed state
$ systemctl status myapp 2>&1 | head -10
● myapp.service - MyApp daemon
Loaded: loaded (/etc/systemd/system/myapp.service; enabled; vendor preset: disabled)
Active: failed (Result: start-limit-hit) since ...
Main PID: 12345 (code=exited, status=2)
...

Illustrative output

Knowledge check

Knowledge check · 3 questions

  1. Q1. What is the difference between `Requires=` and `Wants=`?

  2. Q2. A unit with only After=postgresql.service will start without postgresql.service if nothing else pulled that unit into the transaction.

  3. Q3. Which of the following are correct dependency declarations? Select all that apply.

Passing score: 75%. Answers are checked in this browser.