- linux
- l2
- deep-dive
- systemd --- Portal | Level: L2: Operations | Topics: systemd | Domain: Linux
systemd Units, Dependencies, and Ordering¶
Scope¶
This document is the hard part people often fake their way through.
It covers:
- unit search paths
- unit naming
- aliases and templates
- requirement relationships
- ordering relationships
- implicit/default dependencies
- conflicts and isolation
- targets
- how transactions are built
- how to reason about broken graphs
Reference anchors: - https://www.freedesktop.org/software/systemd/man/systemd.unit.html - https://www.freedesktop.org/software/systemd/man/systemd.service.html - https://www.freedesktop.org/software/systemd/man/systemd.special.html - https://www.freedesktop.org/software/systemd/man/systemd-analyze.html
The Core Rule¶
In systemd, requirement and ordering are different dimensions.
That single fact explains a lot of weird behavior.
Requirement dependencies¶
These express "what else must exist/be pulled in."
Examples:
- Requires=
- Wants=
- BindsTo=
- PartOf=
- Conflicts=
Ordering dependencies¶
These express "what must happen before/after what."
Examples:
- Before=
- After=
You often need both.
Why This Matters¶
Suppose unit A depends on unit B.
If you write only:
Then systemd will try to start both, but not necessarily in the order you mentally expect.
If you also need startup order, write:
That is one of the most common systemd mistakes.
Unit Load Paths¶
The manager reads unit files from standard locations, roughly:
- vendor units
- runtime-generated units
- administrator overrides
- transient/runtime state
Admin overrides usually beat vendor defaults.
This is why systemctl edit is so important:
you can layer behavior without mutilating packaged files.
Unit Identity¶
A unit has: - a name - a type suffix - potential aliases - maybe instances if it is a template
Examples:
Template units¶
A template uses @.
Example:
An instance:
Templates are how systemd avoids duplicating similar units.
Requirement Dependencies in Plain English¶
Wants=¶
Soft pull-in. Try to start the other unit, but failure is not necessarily fatal to this one.
Use this more than you think.
Requires=¶
Harder dependency. If the required unit fails to start, the dependent unit is considered failed too.
Requisite=¶
Like "must already be there now." Used less often.
BindsTo=¶
Stronger lifecycle coupling. If the bound unit disappears, this one goes down too.
Often used with device-ish or mount-ish relationships.
PartOf=¶
Propagates stop/restart operations. Useful when you want units to move together administratively without fully changing startup requirements.
Conflicts=¶
Mutual exclusion. Used when states cannot coexist.
Ordering Dependencies¶
After=¶
This unit starts after the listed unit is started. It says nothing about whether the other unit should be pulled in.
Before=¶
Inverse ordering relation.
Again: ordering alone does not pull units in.
Targets Are Not "Runlevels", Except When They Sort Of Are¶
Targets are synchronization points.
They are often used to represent system state milestones:
sysinit.targetbasic.targetmulti-user.targetgraphical.target
You can think of them as graph waypoints.
A target often uses Wants=/Requires= to pull in units and usually implies ordering through target semantics.
But treating them as simple numbered runlevels leaves performance and correctness on the floor.
Default Dependencies¶
Many unit types automatically receive default dependencies unless disabled with:
This is powerful and dangerous.
Default dependencies save you from a lot of boilerplate. Disabling them is mostly for: - early boot units - shutdown-path units - special low-level cases
If you disable defaults without knowing why, enjoy the fire.
Implicit Dependencies¶
Some dependencies are inferred from unit content.
Examples: - mount units from path relationships - service units using sockets or paths - resource-control relationships - target helper dependencies
This is why the graph you run is larger than the file you wrote.
Conditions and Assertions¶
Conditions let a unit be skipped based on runtime state.
Examples: - architecture checks - file existence - path non-emptiness - kernel command line - virtualization/container detection
Assertions are stronger: failing them is treated more severely.
This matters because "didn't run" and "failed" are not the same operational event.
Isolation¶
systemctl isolate X.target asks the system to move toward a target and stop units not part of that target's world.
This is powerful. It can also drop you into a minimal state if you isolate the wrong thing.
Think of isolation as: "Make the system look like target X is the only desired destination."
Instance Relationships and Drop-Ins¶
Use drop-ins for local customization:
That creates override snippets rather than patching vendor unit files.
This is the sane way to add: - environment variables - restart policy - timeouts - dependencies - resource controls
Graph Debugging¶
These commands matter:
systemctl list-dependencies multi-user.target
systemctl show nginx.service
systemctl cat nginx.service
systemd-analyze critical-chain
systemd-analyze dot
When debugging:
1. inspect unit file
2. inspect drop-ins
3. inspect effective properties with show
4. inspect journal
5. inspect graph
Typical Broken Patterns¶
After= without Wants=/Requires=¶
You told systemd about order, not necessity.
Requires= without After=¶
You told systemd about necessity, not order.
Using network.target as if it means "network ready"¶
It mostly means networking stack management has started, not that remote connectivity is truly available.
Using network-online.target everywhere¶
You traded correctness uncertainty for boot slowness and maybe still got it wrong.
Overusing hard dependencies¶
This makes systems fragile. Prefer soft pull-ins unless you truly need hard failure propagation.
Decision Table¶
| Need | Usually use |
|---|---|
| Start B when A starts, but A may still survive if B fails | Wants= + maybe After= |
| A must not run unless B starts successfully | Requires= + After= |
| A must stop if B disappears | BindsTo= + After= |
| Admin stop/restart A should also affect B | PartOf= |
| Two states must not coexist | Conflicts= |
Interview-Level Example¶
A web service needs local mounts and a database socket, but boot should not fail if optional metrics agent is absent.
A decent answer:
- local mounts via appropriate mount dependencies
- database relation with explicit requirement/ordering depending on architecture
- metrics agent via
Wants=, notRequires= - avoid blind
network-online.targetunless actually justified - use
systemd-analyze critical-chainif boot gets slow
That answer shows graph thinking instead of script thinking.
Fast Mental Model¶
Wiki Navigation¶
Prerequisites¶
- Linux Ops (Topic Pack, L0)
Related Content¶
- Case Study: Systemd Service Flapping (Case Study, L1) — systemd
- Cron & Job Scheduling (Topic Pack, L1) — systemd
- Deep Dive: Linux Boot Sequence (deep_dive, L2) — systemd
- Deep Dive: Systemd Architecture (deep_dive, L2) — systemd
- Deep Dive: Systemd Service Design Debugging and Hardening (deep_dive, L2) — systemd
- Deep Dive: Systemd Timers Journald Cgroups and Resource Control (deep_dive, L2) — systemd
- LPIC / LFCS Exam Preparation (Topic Pack, L2) — systemd
- Linux Boot Process (Topic Pack, L1) — systemd
- Linux Logging (Topic Pack, L1) — systemd
- Linux Ops (Topic Pack, L0) — systemd