Running nc -uz against an OpenVPN server measures almost nothing, and the OpenBSD manual says so in its caveats: UDP scans in that mode report success whether or not anything is listening. The exit status reflects the local send, not the remote state, so a supervisor that trusts it keeps a dead server marked healthy for as long as the process table looks plausible. Replacing it starts with deciding which question the check should answer, because process, listener, authentication and application reachability are four different observations. A probe that names one of them is useful; a probe that implies all four is a liability.

What a UDP probe can and cannot observe

nc -uz sends a datagram and exits, and the status it returns describes the local send. A closed port may answer with ICMP port unreachable, but the documented behaviour of this scan mode does not convert that into a failure, and a filtered port produces no answer at all. Silence and success therefore mean the same thing to the caller.

That makes the probe a test of the local stack, not of the server. It does not establish that the host is reachable, that the port is bound, that the OpenVPN process is running, or that a client could authenticate. A TCP connect test is stronger because the handshake gives a definite answer, but only when the deployment actually listens on TCP. Switching the probe to TCP on the same number tests a listener that may not exist, and against a UDP-only server it will fail a healthy service.

Matching the probe to the configured transport comes before trusting either result. A UDP deployment needs a different signal, not a different protocol.

Three callers, three different questions

Ready depends on who is asking. A service manager restarting a crashed process needs to know that the instance started and bound its listener. A load balancer needs to know that a client arriving now would be accepted. A monitoring system needs to know that the path from a real network still carries a session to a permitted resource.

Those are not the same check, and one probe cannot honestly cover all three. Pick the caller first, then pick the evidence.

Build the probe from the current instance

OpenVPN writes a startup line when an instance finishes initializing, and that line is useful evidence about local startup. Read it from the current start rather than from the tail of an appended file, because a success from last week reads exactly like a success from a minute ago. A server can also finish initializing before any client has ever connected, so the line says nothing about authentication.

For a startup check, pair process state with current log evidence and a listener that matches the configured transport. The check should fail when the process is running but has not finished initializing, which means the log query has to be scoped to this boot and this unit.

systemctl is-active openvpn@server
sudo journalctl -u openvpn@server -b --no-pager | grep "Initialization Sequence Completed"
ss -lunp | grep 1194

Three outputs, three claims: the unit is active, this boot produced the initialization line, and something holds the UDP port. None of them says a client can connect. Keep them separate in the status page so an incident does not collapse into a single green dot.

Break it on purpose and watch the probe

A probe is only trustworthy once you have watched it fail. Stop the server, run the probe, and confirm the result you expect. Then start it again and confirm the probe recovers without any other change.

sudo systemctl stop openvpn@server
nc -uz 203.0.113.10 1194; echo "exit=$?"
ss -lunp | grep 1194
sudo systemctl start openvpn@server
ss -lunp | grep 1194

Read the block line by line. The netcat command prints exit=0 while nothing is bound, which is the whole problem in one line. The first ss prints nothing, the restart succeeds, and the second ss shows the listener again. If your probe passes against the stopped server, it is measuring the local stack and nothing else.

The opposite failure matters too. A probe that fails against a healthy server causes restarts that were never needed, and a restart loop is its own outage. Test both directions before you wire the probe into anything that can act on it.

What the status line should say

Record which boundary each check covers: process, listener, authentication, or application. One check rarely covers more than one, and a status page that compresses them into "up" hides the difference that matters during an incident. For the network path, use a real client with a dedicated identity, let it complete the handshake and one harmless permitted operation, then disconnect. That exercises the control channel, the authentication policy and the listener in one step.

Keep that identity separate from real users and give it the minimum permissions the check needs. A readiness probe with broad access is a new attack surface added for convenience. Rotate its credentials like any other account, because a probe that stops authenticating looks exactly like a server that stopped accepting clients.

Name the boundary in the alert text, so the first responder knows whether to look at the unit, the socket, the certificate or the route. A probe that cannot say what it proved will be trusted for the wrong reason, which is how the netcat check survived in the first place.

Allakkat attuumassuteqartut