WireGuard collides with Docker networks — how do I stabilize routing?
Question
On my Ubuntu VPS I have a database cluster closed to the outside; the main interface is `ens192`. I set up a WireGuard tunnel for secure access. When the VPN comes up, IP conflicts and routing errors appear between the local Docker networks (`docker0`) and the WireGuard subnets. How do I permanently stabilize network isolation and the routing table at the server level?
Answer
Short answer: the root cause is overlapping RFC1918 ranges. The fix isn’t a runtime trick, it’s a deterministic addressing plan: pin Docker’s pool, narrow WireGuard’s AllowedIPs, make routes persistent.
This isn’t a “route bug”, it’s a planning problem: once WireGuard comes up there are two networks sitting in the same 172.x/10.x range, and the kernel can’t decide which one to route.
- Pin Docker’s pool. With
default-address-poolsindaemon.json, pin the block Docker auto-assigns networks from to a range that can never collide with the WireGuard subnet. As long as Docker picks random subnets, a clash is inevitable; decide it by hand. - Give WireGuard a TIGHT
AllowedIPs. LetAllowedIPsbe only the DB subnet — not0.0.0.0/0. Keep it broad and WireGuard hijacks the host’s entire default route, and everything tries to flow through the tunnel. Narrow it and the kernel only sends DB traffic towg0. - Make routes persistent. Ad-hoc
ip route addcommands die on reboot. Define routes viasystemd-networkd/netplan so they come back automatically after a restart. That’s the only durable cure for “it worked, then a reboot broke it”. - Keep the DB box inbound-only over
wg0. Don’t NAT the database to the world; let it accept only connections from the tunnel. That’s the whole point of a closed cluster — access through one door, and that door is WireGuard. - Verify. Use
ip route get <db-ip>to confirm the packet really leaves viawg0, andwg showto confirm the handshake is live. Confirm with these two commands, not by guessing.
Bottom line: stop chasing collisions at runtime; this is a planning problem. I’d write a documented, non-overlapping subnet plan per interface (ens192 / docker0 / wg0). Pin the Docker pool, narrow AllowedIPs, make routes persistent via systemd, reach the DB only through the tunnel. With a clean addressing plan, routing stabilizes on its own.
Related Reading
Comments
Sign in with your GitHub account to join the discussion. Comments are stored in GitHub Discussions.