systemd Network Readiness - What network-online.target Actually Promises
A service starts during boot, tries to contact a remote endpoint, and fails because the machine has not finished acquiring an address. Adding After=network.target looks like the obvious repair. Yet the same failure returns on the next slow DHCP response. What did the dependency actually promise?
On a systemd-managed Debian server, “the network is up” is not one universal state. It may mean that the network manager has started, an interface has an address, a route exists, DNS works, or one particular remote service answers. Those conditions can happen at different times, and connectivity can disappear again after boot. The useful task is therefore not to find a magical target. It is to state the smallest condition the application really needs and decide whether boot should wait for it.
Three targets describe three different moments
systemd provides network-pre.target, network.target, and network-online.target, but their names are easy to read as a progress bar. The upstream network synchronization guidance gives them narrower meanings.
network-pre.target: before configuration begins
This passive target exists mainly for software that must run before interfaces are configured, such as a firewall setup. A service that needs this ordering pulls the target in and places itself before it. Ordinary network clients and servers usually have no reason to use it.
network.target: the management stack has started
network.target does not promise that a physical interface has appeared, that DHCP has completed, or that an IP address is usable. The Debian systemd.special(7) manual calls its startup meaning weakly defined. Its clearer value is during shutdown: a service ordered after it is stopped before the network stack is taken down, allowing existing connections to close more cleanly.
Consequently, this fragment expresses ordering around the network stack, not address readiness:
[Unit]
After=network.target
If a client absolutely cannot begin without initial network configuration, this is not enough.
network-online.target: the configured startup threshold was reached
network-online.target is an active target intended for units that strictly require a configured network during startup. It can pull a wait service into the boot transaction and delay dependent work until that service finishes. The exact threshold is deliberately left to the network manager and local configuration. The target is therefore a synchronization point, not a vendor-neutral definition of Internet access.
It is also a one-time boot concept. The Debian manual explicitly says that it does not track the system's online state after boot. A cable can be unplugged, a route can disappear, or a remote API can fail after the target is active. Any service that must survive those events still needs runtime error handling.
Wants= and After= solve different problems
For a service that truly needs the boot-time threshold, systemd's documented pattern is:
[Unit]
Wants=network-online.target
After=network-online.target
The two lines are not redundant. According to systemd.unit(5), Wants= weakly pulls the named unit into the same transaction. It does not establish sequence. After= establishes sequence if both units are started, but it does not pull the other unit in. Using only After= can therefore order a service behind a target that nobody requested; using only Wants= allows both jobs to proceed without the intended ordering.
Requires=network-online.target is not simply a “safer” spelling. Requires= creates stronger lifecycle coupling, while the online target does not continuously represent connectivity anyway. The usual Wants= pattern lets the service start even if the wait unit fails or times out, leaving the application to report or retry its real operation. A particular application may need stricter behavior, but that decision should come from its failure contract rather than from the word “online.”
The wait implementation defines “online”
The target itself does not inspect interfaces. A network-manager-specific service runs before it. On common Debian installations, that may be NetworkManager-wait-online.service or systemd-networkd-wait-online.service. Enabling the wrong one does not make the other manager finish sooner.
With systemd-networkd, the default wait service waits for managed links to finish configuration or fail and for at least one link to be online. The Debian systemd-networkd-wait-online(8) manual defines its default online threshold as an operational state of at least degraded. Interface-specific instances and options such as --any, --ipv4, and --ipv6 can change the condition. Network files can also influence whether a link is required for online status.
NetworkManager uses a different model. Its wait-online documentation says it delays the target until NetworkManager reports startup complete over D-Bus. Connection profiles, address-family settings, activation retries, Wi-Fi scanning, and Ethernet carrier waiting all affect that point. For a profile with IPv4 and IPv6 enabled, the default may consider the device activated when one family is ready. These are NetworkManager semantics, not systemd-networkd operational-state rules.
Neither implementation can infer every application's contract. A configured route does not prove that sync.example.net resolves or answers. Conversely, a temporary failure of that endpoint does not necessarily mean the local interface is misconfigured.
Inspect before adding a dependency
Begin by identifying the network manager and the wait unit already present. These commands are read-only:
systemctl is-active NetworkManager.service systemd-networkd.service
systemctl is-enabled NetworkManager-wait-online.service \
systemd-networkd-wait-online.service
systemctl status network-online.target
systemctl list-dependencies --reverse network-online.target
An enabled wait service is only one piece of evidence. It does not show that a particular boot transaction pulled in network-online.target, nor that its definition matches the application. Inspect the consumer and the wait service as well:
systemctl cat example-sync.service
systemctl cat NetworkManager-wait-online.service
systemd-analyze critical-chain network-online.target
journalctl --boot --unit=NetworkManager-wait-online.service
example-sync.service is a placeholder. Substitute the real unit and the wait service used on that host. The critical chain and journal can reveal where boot waited, but they do not prove DNS or remote-application readiness.
Add a boot wait only when the work is truly strict
Suppose a one-shot synchronization job is useless without initial network configuration and can safely report failure if its remote operation still does not work. A local drop-in can express the startup dependency without copying the package's entire unit:
sudo systemctl edit example-sync.service
[Unit]
Wants=network-online.target
After=network-online.target
After saving, inspect the merged unit and verify its syntax before scheduling a real test:
systemctl cat example-sync.service
systemd-analyze verify example-sync.service
Testing the startup path may interrupt or repeat work, so it should be planned around the application's side effects. A successful start proves only that this attempt completed according to the unit's own exit behavior. It does not turn the target into a permanent connectivity monitor.
Do not replace readiness with a fixed sleep
Adding ExecStartPre=/usr/bin/sleep 30 may hide a race on one boot and waste 30 seconds on another. It observes elapsed time rather than the required condition. A loop that pings a public address has a similar mismatch: ICMP reachability to that host does not prove DNS, TLS, credentials, or the intended API works, and some networks block ICMP while useful services remain reachable.
If one endpoint is the real prerequisite, the application is usually the best place to interpret its failures. It can distinguish name-resolution errors, connection refusal, authentication failure, and an invalid response. A service manager can schedule retries or restart a failed client, but retry bounds, delay, idempotency, and alerting still need deliberate design. Waiting forever merely moves an outage into the boot sequence.
Choose behavior by workload
- A network server listening on wildcard or local addresses: usually start it without pulling in
network-online.target. Upstream systemd guidance specifically discourages broad use of the target for server software that can accept local connections before a routable interface exists. - A long-running client or worker: start it and handle temporary failures with bounded retries or dynamic network-change handling. This also covers outages that occur hours after boot.
- A strict one-shot client: using
Wants=andAfter=network-online.targetmay avoid an immediate, predictable boot race. The operation still needs a timeout and an honest failure result. - A remote filesystem mount: let the mount tooling express its network dependency. systemd already arranges online-target dependencies for remote mounts rather than requiring an unrelated application service to reinvent them.
There are exceptions. A server configured to bind only to one address that appears late may fail where a wildcard listener would not. A laptop with optional Wi-Fi should not hold the whole boot path for a nonessential background sync. A multi-interface server may care about one management link but not an unplugged secondary port. These cases are reasons to refine the readiness contract, not to label the whole machine simply online or offline.
Conclusion
network.target marks the presence of the network-management stack, while network-online.target can wait for a configured startup threshold defined by the active network manager. Pairing Wants= with After= both requests that threshold and orders a consumer behind it. None of those statements proves that DNS, the Internet, or a particular service will remain reachable.
The most robust service is usually one that can start before perfect connectivity, explain a failed operation, and recover when the network changes. A boot wait still has a legitimate role for strict clients and remote resources, but it should be a narrow synchronization tool. Before adding it, ask a more precise question than “is the network up?”: which exact condition does this workload need, and what should happen when that condition disappears later?
References
- systemd project, “Network Configuration Synchronization Points”.
- Debian Manpages, systemd.special(7), systemd 257.13.
- Debian Manpages, systemd.unit(5), systemd 257.13.
- Debian Manpages, systemd-networkd-wait-online.service(8), systemd 257.13.
- NetworkManager Reference Manual, NetworkManager-wait-online.service.
