Skip to content
Muhammet Şafak
tr
Asked by: Mert Answered:

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.

Short answer

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. I covered how Docker sets up its own network layer in Docker containerization: installation and basic usage; the clash comes straight out of that auto-assigned bridge network.

Why

  1. A clash is inevitable while Docker picks its pool at random. Automatic network assignment chooses a range on your behalf; the day it lands on your WireGuard subnet, the collision happens on its own.

  2. A broad AllowedIPs hijacks the default route. Give it 0.0.0.0/0 and WireGuard pulls the host’s entire traffic into the tunnel, turning “a collision” into “everything flows through the tunnel”.

What to do

  1. Pin Docker’s pool. With default-address-pools in daemon.json, pin the block Docker auto-assigns networks from to a range that can never collide with the WireGuard subnet.

  2. Give WireGuard a tight AllowedIPs. Let AllowedIPs be only the DB subnet. Narrow it and the kernel only sends DB traffic to wg0.

  3. Make routes persistent. Ad-hoc ip route add commands die on reboot. Define routes via systemd-networkd/netplan so they come back automatically after a restart.

  4. Keep the DB box inbound-only over wg0. Don’t NAT the database to the world; let it accept only connections from the tunnel.

  5. Verify. Use ip route get <db-ip> to confirm the packet really leaves via wg0, and wg show to 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

Share:

Comments

Sign in with your GitHub account to join the discussion. Comments are stored in GitHub Discussions.

More Questions

All questions

Search the site

Start typing to search posts, projects and pages.

Esc to close Powered by Pagefind