WG-Quick Routing Policies Are Removed When Networkd Restarts
This goes out to anyone it may save from a few frustrating internet searches.
I recently encountered an issue where a VPS I host in the cloud would occasionally lose its WireGuard VPN connection.
Context
I use a MikroTik Cloud Hosted Router (CHR) with both IPv4 and IPv6 as my gateway for internet-bound traffic. My VPS is configured with a WireGuard connection that routes all IPv4 and IPv6 traffic through the VPN, exiting via the CHR. This setup allows me to avoid assigning a dedicated IPv4 address to the VPS - since it can connect to the CHR using IPv6 - and route all IPv4 traffic through the VPN. As a result, I only need to pay for a single IPv4 address, regardless of how many VPS instances I have.
My WireGuard configuration is fairly simple: I store the config in the default location at /etc/wireguard/wg0.conf
and manage it using systemctl
and wg-quick
.
The Problem
This setup worked fine for a while, but then I started experiencing issues where the VPS would stop responding to IPv4 traffic. The issue seemed to occur consistently in the morning, suggesting an automatic update might be interfering with the VPN connection.
Checking the logs, I noticed that networkd
had been restarted as part of the automatic update process, which appeared to be the root cause of the issue.
After some debugging, I discovered that restarting networkd
resets the routing policy database (viewable via ip rule show
). Since wg-quick
relies on routing policies to ensure all IPv4 traffic goes through the VPN, and its systemd service is a one-shot process (i.e., it doesn’t continuously monitor or reapply routing rules), the VPN would lose its routing policies when networkd
restarted.
Routing Policy Behavior
When the VPN is functioning correctly, the routing policies look like this:
# ip rule show
0: from all lookup local
32764: from all lookup main suppress_prefixlength 0
32765: not from all fwmark 0xca6c lookup 51820
32766: from all lookup main
32767: from all lookup default
After networkd
restarts, they are reset to:
# ip rule show
0: from all lookup local
32766: from all lookup main
32767: from all lookup default
The Fix
To resolve this issue, I needed a way to ensure the routing rules are reapplied. My quick fix was to modify the systemd service to first bring down the WireGuard interface before attempting to bring it back up.
To do this, create the file /etc/systemd/system/[email protected]/pre-down.conf
and add the following content:
[Service]
ExecStartPre=-/usr/bin/wg-quick down %i
This ensures the interface is cleanly removed before wg-quick
attempts to restart the WireGuard service after networkd
has restarted. The -
before the command ensures that any errors (e.g., if wg0
doesn’t exist) are ignored. Once wg-quick up wg0
runs, it will properly reapply the routing policies.