# 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://muhammetsafak.com/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.

## 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](/blog/docker-containerization-installation-and-basic-usage/); the clash comes straight out of that auto-assigned bridge network.

## Why

1. **A clash stays possible while Docker picks from its built-in pools.** Automatic network assignment takes a range from predefined pools (`172.17.0.0/16` up to `192.168.0.0/16`) on your behalf. Docker tries to avoid prefixes already in use on the host, but nothing guarantees that it will steer clear of a subnet you plan to bring up later.

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

- [Docker containerization: installation and basic usage](/blog/docker-containerization-installation-and-basic-usage/) — Blog
- [How do I shrink Docker images with multi-stage builds and distroless?](https://muhammetsafak.com/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://muhammetsafak.com/just-ask/how-http-2-and-http-3-improve-api-performance/) — Just Ask
- [My Go worker in a container never receives SIGTERM and won't shut down gracefully—is this a PID 1 problem?](https://muhammetsafak.com/just-ask/go-worker-never-receives-sigterm-pid-1-problem/) — Just Ask
