Three conditions decide whether a watchdog helps or hurts: what the probe can distinguish, how many consecutive failures justify action, and whether the action is a reconnect or a failover. A cron line that pings a public address answers none of them well. It reconnects tunnels that are fine and leaves tunnels that are broken, and it does both without a record. The design worth keeping makes those three choices explicit.

The probe is the part that decides everything else.

What a Ping to the Internet Actually Proves

Pinging a public resolver shows that the local internet path is alive. It does not prove the tunnel is carrying traffic, and on a full-tunnel profile the ping itself travels through the tunnel, so a healthy reply and a broken tunnel can coexist while the client still forwards packets. If the profile sends name lookups through the tunnel, a DNS failure reads as a dead link and starts a reconnect that cannot fix anything.

Neither direction of the error appears in the exit code. A watchdog built on that signal is a coin flip with a cron schedule.

Probe Something That Lives Behind the Tunnel

ip -brief addr show tun0
ip route get 10.100.20.10
nc -z -w3 10.100.20.10 443

Probe inside, cheapest check first. Whether the interface exists tells you the tunnel is up; a route lookup for a known internal address tells you traffic would enter it; a TCP probe to a host on the far side tells you something actually answers. Echo requests alone mislead: many internal hosts answer TCP happily while dropping ICMP, so a watchdog built on ping keeps reconnecting a tunnel that never stopped working.

Keep the probe bounded in time and give it a distinct exit status. A probe that hangs for thirty seconds turns a watchdog into an outage detector with a delay built in.

Decide Between a One-Liner and a Loop

* * * * * nc -z -w5 10.100.20.10 443 || myvpn reconnect

A cron entry is a single line with no memory, which suits a laptop where an extra reconnect costs nothing. A supervised loop keeps a counter and acts only after several misses, which stops a flaky link from triggering a storm of reconnects. Either way, run it under a supervisor so it survives a crash and writes to the journal. Choose on whether a spurious reconnect matters, not on which version is shorter.

failures=0
while sleep 30; do
  if nc -z -w5 10.100.20.10 443; then failures=0
  else failures=$((failures+1)); fi
  if [ $failures -ge 3 ]; then myvpn reconnect; failures=0; fi
done

That loop counts consecutive failures rather than a fraction over a window. A window needs a clock and a memory of samples, and most of the bugs in hand-rolled watchdogs live in that arithmetic.

Failover Is a Different Decision

Switching servers differs from reconnecting. Tear the old link down first, because a second client starting while the first still holds the address will collide. Count consecutive failures before switching, cap the attempts, and probe again once the new link is up rather than trusting the connect command's exit status. A wrong configuration should not rotate through every server in a loop.

Do not probe the VPN server's own public address from inside a full tunnel. That check depends on the thing it is testing, and it reports success while the path it was meant to validate is gone.

Record which probe ran, what it returned and what action followed. That record turns a mystery reconnect into a diagnosable event.

Ebitabo ebifaananyi