Two fixes get offered for an OpenVPN server that will not start at boot: wait for the address, or let the service retry until the bind succeeds. They address the same symptom with different failure behaviour. Waiting encodes a specific prerequisite and fails loudly when it is missing; retrying hides the prerequisite and keeps trying. Which one belongs in your unit depends on whether that address is a designed dependency or an accident of timing.

Both beat a fixed sleep, which neither waits for anything nor reports what it was waiting for.

What the local Option Actually Requires

local names the address the daemon binds its socket to, and binding to an address that does not exist yet fails at once with an error about the requested address. When the option is absent, the daemon listens on all addresses, which is why the same profile works on a machine whose interface has not come up: it is listening everywhere instead of on the one address you named. That difference is the entire failure, and it stays invisible until a reboot.

The requirement is not that the interface is up in some general sense. The address has to be assigned to the right interface at the moment of the bind, and it has to be the underlay interface rather than a tunnel the same daemon would create later. A check run minutes after login proves nothing about the boot sequence.

Waiting for the Address vs Retrying the Bind

Waiting means an ExecStartPre that polls until the address appears, with a bounded number of attempts and a nonzero exit when time runs out. Retrying means letting the main process fail and asking the service manager to start it again, for example Restart=on-failure with RestartSec=5. The first reports a missing prerequisite as a failed unit, while the second reports it as a run of restarts that eventually stops, which is noisier in the journal but survives an address that arrives late for reasons you cannot predict.

Prefer waiting when the address is a designed dependency, and prefer retrying when the address usually appears but its timing varies. Combining them is reasonable: a bounded wait covers the common case and a restart policy covers the outlier. What neither should do is run forever, because a unit that retries indefinitely hides a misconfiguration instead of reporting it.

Why network-online.target Is Not Enough

network-online.target promises whatever the active network implementation means by online, and nothing beyond it. With systemd-networkd, systemd-networkd-wait-online decides when that target is reached, and its default notion of online can be satisfied before your specific address exists. Under NetworkManager, the wait-online service applies its own rules. Ordering after the target is a sound baseline, not a guarantee that one address, DNS answer or remote peer is ready.

The same reasoning rules out the usual alternatives. A desktop autostart entry waits for a user session that may never open on a server, and a delayed cron command adds elapsed time without naming the resource it hoped would appear. Both can look successful for months and then fail on the one boot that matters.

Check the Binding After a Cold Boot

Verify with a real cold boot rather than a service restart, because a restart inherits an address that the boot sequence had to create. After boot, ss -lntp or ss -lunp shows the local address each listener actually bound, and journalctl -b -u openvpn-server@server shows whether the first attempt failed. Compare the bound address with the one in the profile, then repeat the comparison after a network reconnect.

If the profile never needed a fixed address, removing local is a legitimate simplification, but make it a security decision rather than a troubleshooting habit.

Widening the listener changes who can reach it, so pair that change with the firewall policy that should have been restricting it. A service that starts reliably on every interface has not solved the original problem; it has moved the problem to the network edge.

Ngaahi tokoni fekau'aki