Units and state
Dependencies and targets
Read requirement and ordering relationships without assuming that After also starts another unit.
systemd keeps two questions apart. Does this unit need that other unit? And when should this unit start relative to it? Once you see the split, a lot of unit files make sense. Miss it and you write units that look right and behave wrong.
Requirement vs ordering
Requires= and Wants= express a requirement: start that other unit too.
Wants= is the soft version. Pull the other unit in, but carry on if it fails. Requires= is hard. If the required unit fails to start, this unit fails with it.
My advice is to use Wants= unless the service cannot exist without the dependency. Hard requirements make small failures spread.
After= and Before= express ordering only. After=postgresql.service means “if both of us are starting, let PostgreSQL go first”. It does not start PostgreSQL. If nothing else pulls PostgreSQL in, your service starts alone.
That gives us the classic bug: a unit with After=network-online.target and no Wants=. Nobody asked for the target to be reached, so the ordering is meaningless. The two directives travel in pairs:
[Unit]
Description=Demo API server
Wants=network-online.target
After=network-online.target postgresql.service
This unit pulls in network-online.target and waits for it. It also orders itself after PostgreSQL, without demanding that PostgreSQL exists on this machine.
Targets group units
A target is a unit with no process of its own. It exists to group other units around a system state.
multi-user.target is the normal server boot state. When you write WantedBy=multi-user.target in an [Install] section, systemctl enable creates a symlink in that target’s .wants/ directory. That symlink is the whole “start at boot” mechanism. Nothing magic.
Read the graph
You can walk the relationships in both directions. The --reverse flag answers “who depends on this?” instead of “what does this depend on?”:
systemctl list-dependencies --reverse network-online.target
network-online.target
● ├─demo-api.service
● └─nginx.service
To see ordering as systemd resolved it, ask for the unit’s effective properties:
systemctl show demo-api.service -p Wants -p After
Wants=network-online.target
After=network-online.target postgresql.service systemd-journald.socket basic.target
Notice the extra entries. systemd adds implicit ordering, like basic.target, to every service. You only wrote two names, but the effective list is longer.
Run the list-dependencies command on your server. Find one unit that wants the target, then confirm one ordering relationship in that unit’s effective configuration. If you spot an After= with no matching Wants= or Requires= anywhere, you found a latent bug. It works today only because something else happens to start that unit.
Lesson completed