# WireGuard collides with Docker networks — how do I stabilize routing?

> This is an addressing-plan problem, not a routing bug: pin Docker's pool in daemon.json, cut AllowedIPs down to the DB subnet, make routes persistent.

- Asked: 2026-05-04
- Answered: 2026-05-08
- Asked by: Mert
- Tags: altyapi, networking, docker
- Source: https://www.muhammetsafak.com.tr/en/just-ask/wireguard-and-docker-network-ip-conflicts-and-routing/
- Language: en-US
- Author: Muhammet Şafak

---
**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?


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.

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. As long as Docker picks random subnets, a clash is inevitable; decide it by hand.
2. **Give WireGuard a TIGHT `AllowedIPs`.** Let `AllowedIPs` be only the DB subnet — **not** `0.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 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. That's the only durable cure for "it worked, then a reboot broke it".
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. That's the whole point of a closed cluster — access through one door, and that door is WireGuard.
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

- [Docker containerization: installation and basic usage](/en/blog/docker-containerization-installation-and-basic-usage/) — Blog
- [How do I shrink Docker images with multi-stage builds and distroless?](https://www.muhammetsafak.com.tr/en/just-ask/shrinking-docker-images-with-multi-stage-builds-and-distroless/) — Just Ask
- [How do HTTP/2 and HTTP/3 (QUIC) improve API performance?](https://www.muhammetsafak.com.tr/en/just-ask/how-http-2-and-http-3-improve-api-performance/) — Just Ask
- [My non-root container can't write to the mounted storage directory—how do I fix the permissions?](https://www.muhammetsafak.com.tr/en/just-ask/my-non-root-container-cant-write-to-the-mounted-storage-directory/) — Just Ask
