Keep a VPN alive when the home router flips between DHCP and static
I’ve been on a laptop that plugs into a home router that sometimes hands out a DHCP lease and other times uses a static IP you set in the admin UI. The VPN client (OpenVPN 3‑CLI on Ubuntu 24.04) would drop the tunnel as soon as the interface lost its address, forcing me to reconnect manually. Below is a minimal, reproducible setup that keeps the tunnel up regardless of the router’s mode.
1. Detect the interface state
systemd-networkd is the default on most modern distros. It already emits network-online.target when an address is assigned, but it doesn’t distinguish DHCP vs static. We can hook into the networkd-dispatcher service to run a script whenever the link changes.
sudo apt install networkd-dispatcher
Create /etc/networkd-dispatcher/routable.d/99-vpn.sh:
#!/usr/bin/env bash
# Run only when the interface becomes routable
if [ "$NETWORKD_DISPATCHER_ACTION" = "routable" ]; then
# Check if the VPN is down
if ! systemctl is-active --quiet [email protected]; then
systemctl start [email protected]
fi
fi
Make it executable:
sudo chmod +x /etc/networkd-dispatcher/routable.d/99-vpn.sh
Now the script runs every time the interface gets an IP, regardless of DHCP or static.
2. Make the VPN resilient
OpenVPN’s --ping and --ping-restart options keep the connection alive. In /etc/openvpn/client/myvpn.conf add:
ping 10
ping-restart 30
The client will send a keep‑alive ping every 10 s and restart if no reply for 30 s. This prevents the tunnel from timing out when the router re‑boots or changes its IP.
3. Avoid DNS leaks
When the router switches to static, the DNS server may change. The VPN client should override the system resolver. Add to the same config:
script-security 2
up /etc/openvpn/update-resolv-conf
down /etc/openvpn/update-resolv-conf
update-resolv-conf is provided by openvpn-systemd-resolved on Ubuntu. It writes the VPN’s DNS to /etc/resolv.conf and restores the original on disconnect.
4. Keep the routing table stable
If the router’s gateway changes, the default route can be lost. A small systemd unit ensures the route is re‑added after the interface comes up.
Create /etc/systemd/system/vpn-route.service:
[Unit]
Description=Add VPN default route
After=network-online.target
Requires=[email protected]
[Service]
Type=oneshot
ExecStart=/usr/sbin/ip route add default dev tun0 table vpn
ExecStop=/usr/sbin/ip route del default dev tun0 table vpn
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
Enable it:
sudo systemctl enable vpn-route.service
The VPN’s default route is now isolated in its own table, so a change in the LAN gateway won’t affect it.
5. Security note
Running a VPN on a laptop that sometimes connects to a public Wi‑Fi or a corporate network can expose you to man‑in‑the‑middle attacks if the router’s firmware is outdated. Keep the router’s firmware up to date and use WPA3 where available. The VPN itself uses TLS 1.3 by default; verify the server’s certificate chain with openssl s_client -connect vpn.example.com:1194.
6. Quick sanity check
# Verify the VPN is up
systemctl status [email protected]
# Confirm the route
ip route show table vpn
# Test DNS
dig @127.0.0.53 example.com
If the VPN drops, the dispatcher script will restart it automatically. The ping-restart option keeps the tunnel alive across brief network hiccups.
See also
- Testing Restic Incremental Backups on an Active MySQL Server Without Downtime
- Fixing the 2‑second DNS lookup lag on my Pi by tweaking resolvectl and systemd‑resolved cache
- Diagnosing a 30‑second Nginx start‑up lag on an 8‑core ARM home‑server
- When `ip link` shows no IP but you can ping: diagnosing an absent IPv4 on a bridge interface
- When Wayland Won’t Run My Games: Tweaking a Laptop for Better Battery Life