The client connects, fails during TLS negotiation, waits, and tries the same server again with the same result. Under a supervisor that restarts a failed process, those attempts become an endless journal loop, and the first diagnostic line scrolls out of reach. OpenVPN ships an option that turns that negotiation failure into a visible exit instead of another retry. It is a diagnostic and supervision setting, not a repair, so pair it with a policy that actually lets the process stay down.

The cycle that hides the first error

What you see is a repeating block rather than one failure: a connection attempt, a TLS error, a pause, and then the same sequence. Each repetition pushes the original line further up the journal. If the unit restarts on failure, the cycle can continue for hours without anyone reading the cause. The symptom is the loop, even though the underlying fault is a single negotiation problem.

Interactive sessions tolerate this because a transient failure may clear on the next attempt. Automated connections cannot afford the same optimism: the process has to stop so a human or a monitor can see the status.

Before changing anything, capture one full cycle. Note the remote address, the TLS stage named in the error, and whether the process exits at all.

The retry machinery behind the loop

Retrying is the default because several unrelated timeouts share the same machinery. connect-retry sets the gap between connection attempts, connect-retry-max can cap how often each remote entry is tried, and resolv-retry governs repeated name lookups. A TLS negotiation failure reaches that machinery as a transport event, so the client schedules another attempt instead of reporting. The default is helpful for a flaky link and harmful for a fault that cannot heal.

The option --tls-exit changes only the negotiation case. The manual describes it as exiting on TLS negotiation failure, which is narrower than exiting on every error. Timeouts, resolver failures and authentication rejections keep their own behaviour.

Making the client exit on TLS failure

Put tls-exit in the profile the client actually loads, or supply --tls-exit when you start it, and run it once in the foreground. Read the exit status as well as the last log line, because the status is what a supervisor acts on. A non-zero exit after a negotiation failure is the intended result. If the process keeps retrying instead of ending, the option is not in the profile the client actually loaded.

The supervisor decides whether that exit is visible. A systemd unit with Restart=always or Restart=on-failure starts the client again, so the loop continues and the option appears to do nothing. Check the policy before you blame the client:

systemctl show -p Restart -p RestartSec openvpn-client@office
journalctl -u openvpn-client@office -n 40 --no-pager

Restart=no lets the process stay down, and Restart=on-failure still restarts after a non-zero status. If you want the failure to be visible but the service to recover from unrelated crashes, set Restart=on-failure and use StartLimitBurst and StartLimitIntervalSec to stop the restart storm. Then read the unit's status to confirm the last exit code: systemctl status reports it, and the journal keeps the first error above the restarts.

Write the decision into the unit or the profile notes. A profile that exits on TLS failure looks like an accident to the next person unless the reason is recorded. State which failures should stop the process and which should be retried.

Proving it with a throwaway endpoint

To see the behaviour before trusting it, copy the profile and point the copy at an endpoint that accepts a TCP connection but never speaks OpenVPN. A listener that answers and then stays silent is enough. With tls-exit, the client should end when the handshake fails; without it, the same profile should keep cycling. Use the copy, never the working profile.

Compare the two runs by exit status and by the first error line, not by how long each one lasted. The status tells you whether the option fired, and the first line tells you which stage failed.

Remove the test profile when you are done. A copy with a deliberately wrong endpoint is a trap for anyone who later starts every profile in the directory.

Stages this option never reaches

An authentication rejection is a later stage. If the server evaluates the account and refuses it, TLS has already been negotiated, so tls-exit does not fire. The authentication retry settings are the next place to look, and the server's reason matters more than the client's final line. A wrong password and a missing certificate can produce similar client-side messages.

A missing route, a wrong resolver or an unreachable gateway are also outside the option, because they are not negotiation failures. Silent packet loss is the least obvious case: depending on how far the exchange progressed, the client may report a negotiation failure or simply time out.

Keep the distinction in your notes. The setting is for diagnosis and supervision rather than repair, so it does not fix a cipher mismatch, a certificate problem or a wrong port. Repair the cause of the failed negotiation first, then decide whether exiting immediately still suits the deployment.

འབྲེལ་བའི་རྩོམ་ཡིག