Guide
How do you stop Docker networks clashing with your LAN or VPN subnet?
Docker hands every new network a subnet from a fixed list that runs out after 31 and finishes inside 192.168.0.0/16. It only avoids subnets your host is directly attached to at that moment, so remote LANs and VPN routes get swallowed. One `default-address-pools` entry fixes both problems.
How do you stop Docker networks clashing with your LAN or VPN subnet?
Set default-address-pools in /etc/docker/daemon.json to a private range nothing else on your network uses, for example {"base": "10.201.0.0/16", "size": 24}, which gives 256 networks of 254 addresses. Restart the Docker daemon, then recreate existing networks with docker compose down and up -d, because networks keep the subnet they were created with. Docker's own check only skips subnets on the host's directly attached interfaces, so it cannot protect a VPN or remote LAN.
Put a default-address-pools entry in /etc/docker/daemon.json that hands out subnets from a private range nothing else on your network uses, restart the daemon, and recreate your networks. That one change fixes both symptoms that bring people here: a new stack that cuts off a VPN or a remote LAN, and the error all predefined address pools have been fully subnetted. Both come from the same built-in list in Docker Engine Engine, which holds 31 networks and spends its last sixteen inside 192.168.0.0/16.
Two answers circulate, and both are half right#
The first answer is "Docker uses 172.17.0.0/16, so just move it with bip". bip moves only the default bridge, docker0. Every compose project creates its own _default network, and those come from a separate list, the default address pools. Changing bip leaves all of them where they were.
The second answer is "Docker already avoids subnets that are in use, so a clash cannot be Docker's fault". Docker's networking overview says it "attempts to avoid address prefixes already in use on the host", and the source shows exactly how far that attempt goes. Before taking a subnet automatically, the daemon reads two things: the nameservers in /etc/resolv.conf, and the on-link routes in the host's main routing table. That protects the LAN your server is plugged into. It does not protect anything the host reaches through a gateway, anything in another routing table, or anything that appears after the network was created. Those are exactly the places a VPN puts its routes.
The default pools, in numbers#
The list is hardcoded in Moby's ipamutils package, and it is identical in Engine 28.1.1 and in the current release, docker-v29.9.1 (14 October 2026):
| Base | Size of each network | Networks | Usable addresses each |
|---|---|---|---|
| 172.17.0.0/16 to 172.19.0.0/16 | /16 | 3 | 65,534 |
| 172.20.0.0/14, 172.24.0.0/14, 172.28.0.0/14 | /16 | 12 | 65,534 |
| 192.168.0.0/16 | /20 | 16 | 4,094 |
| Total | 31 |
Docker takes the first free subnet in that order. docker0 normally takes 172.17.0.0/16, so your first fourteen compose projects land on 172.18 to 172.31. The fifteenth project gets 192.168.0.0/20, which covers 192.168.0.0 to 192.168.15.255, the range most home routers use. The thirty-first project fails with all predefined address pools have been fully subnetted.
Each network is far larger than it needs to be. A typical self-hosted stack has two to five containers on a network built for 65,534. That waste is why the pool runs out at 31 networks and not at a few thousand.
We saw the whole sequence on one server running 39 compose projects. Thirty of its networks filled the built-in list exactly, 172.18 to 172.31 and all sixteen 192.168 /20 blocks. The other nine, and docker0, sat in 10.201.x.0/24, the pool its daemon.json now sets. A network keeps the subnet it was created with, which is why the two ranges sit side by side on one host.
What Docker checks, measured#
We tested each case on Docker Engine 28.1.1 by adding a route for the next free subnet, then creating a network and reading the subnet it got. Every route and test network was removed afterwards.
| Situation on the host | What Docker did |
|---|---|
| Subnet on a local interface (on-link route) | Skipped it, took the next subnet |
| Same subnet routed through a gateway, as a site-to-site VPN or a second LAN does | Took it anyway |
| Same subnet as an on-link route in table 52, where Tailscale's Linux client keeps its routes | Took it anyway |
Network created with an explicit --subnet that overlaps a local interface | Accepted it without a warning |
Explicit --subnet that overlaps another Docker network | Refused: Pool overlaps with other one on this address space |
Then we tried both orders, because a VPN that connects after boot is the common case:
- Docker network first, VPN route second. Adding the gateway route failed with
RTNETLINK answers: File exists, andip route getsent the VPN's address range into the Docker bridge. The tunnel shows as connected, and nothing behind it answers. - VPN route first, Docker network second. Docker created the network on the same subnet. The bridge route was added beside the existing one, but the host kept sending that range to the gateway, so the containers' own subnet was the part that stopped working from the host.
Either way, the first route wins and the other side breaks quietly. No error appears in docker compose up.
Who actually gets hurt#
On a server whose LAN is directly attached, the on-link check means Docker skips the LAN itself. The clashes come from networks one hop further away:
- A site-to-site VPN or a VPN server whose remote LAN, or whose tunnel subnet, falls inside a Docker network. If the tunnel comes up after the stack, its route is refused with
File existsand replies to those clients go into the bridge. Headscale vs wg-easy covers the two common VPN servers. - Tailscale subnet routes. Routes in table 52 are invisible to Docker's check, so a stack started later can claim an advertised subnet.
- A second VLAN or office LAN reached through your router, such as a camera network on 192.168.10.0/24.
- A laptop running Docker, where a work VPN that routes 192.168 ranges connects long after the Docker networks were allocated.
If you reach the server through an overlay network, Remote access without port forwarding covers the setups this applies to.
The fix: one pool, sized on purpose#
Choose a block inside 10.0.0.0/8 that none of your LANs, VPNs or cloud networks use, and write it to /etc/docker/daemon.json:
{
"default-address-pools": [
{"base": "10.201.0.0/16", "size": 24}
]
}base is the block Docker allocates from. size is the prefix length of each network it creates, not the size of the pool, and this is where tutorials go wrong. The number of networks is 2 to the power of (size minus the base prefix length):
| Pool | Networks | Usable addresses each | Fits |
|---|---|---|---|
| 10.201.0.0/16, size 24 | 256 | 254 | Almost any home server |
| 10.201.0.0/16, size 26 | 1,024 | 62 | Many small stacks |
| 10.201.0.0/20, size 24 | 16 | 254 | A tight corner of 10/8 |
| 192.168.91.0/24, size 24 | 1 | 254 | Nothing. The second network fails |
The last row is a configuration that circulates in tutorials, paired with "bip": "192.168.90.1/24". The bridge works, the first compose project works, and the second one fails with the same "fully subnetted" error the change was meant to prevent.
Avoid two other ranges. 172.240.0.0/16, which another widely shared fix recommends, is outside 172.16.0.0/12, so it is not private address space under RFC 1918. While a Docker network holds it, traffic to the real hosts in that range goes into the bridge. And if you use Swarm, its overlay networks have their own default pool, 10.0.0.0/8 split into /24 networks, so carve your local pool from a part of 10/8 you are certain Swarm is not using.
With no bip set, docker0 takes the first subnet of the new pool. On our test server it came up as 10.201.0.1/24. Anything that hardcodes 172.17.0.1, such as a host service bound to that address, has to change with it. Reaching a host service from a container covers the host-gateway name that follows the bridge for you.
Applying it: restart, then recreate#
default-address-pools is not one of the options the daemon reloads on SIGHUP, so it needs a full restart:
sudo systemctl restart dockerDocker's live restore documentation is plain about the cost: "By default, when the Docker daemon terminates, it shuts down running containers." Containers with a restart policy come back afterwards. Plan it as downtime.
The restart does not move anything. Every existing network keeps its old subnet until it is deleted and created again. For each compose project:
cd /srv/stacks/immich
docker compose down # removes the containers and the project's networks
docker compose up -d # recreates them from the new pooldocker compose down removes networks defined in the compose file and "the default network, if one is used", and leaves named volumes alone unless you add -v. "Networks and volumes defined as external are never removed", so the shared proxy network in Docker Compose conventions survives every project's down. Stop every stack attached to it, run docker network rm proxy and docker network create proxy, then bring the stacks back.
Check the result with:
for n in $(docker network ls -q --filter driver=bridge); do
docker network inspect -f '{{.Name}} {{range .IPAM.Config}}{{.Subnet}}{{end}}' "$n"
doneIf you hit "fully subnetted" right now#
Free networks first, then fix the pool. docker network prune removes networks "not referenced by any containers". On a self-hosting box that usually means networks left by renamed or deleted projects. It also removes the networks of stacks that are currently stopped, which is harmless because docker compose up recreates them, and an unused external network, which is not harmless if your stacks expect it. Look at docker network ls before you prune. Pruning buys a few slots. The pool change is what makes the error go away for good.
Pinning a subnet for one stack#
Compose can set a network's subnet directly:
networks:
default:
ipam:
config:
- subnet: 10.250.3.0/24Use this only when something needs to know the subnet in advance, such as a trusted-proxies setting in an app behind a reverse proxy (Reverse proxy and TLS covers why that matters). Our test showed that an explicit subnet skips Docker's route check entirely, so a pinned subnet that overlaps your LAN is accepted without a warning. Keep pinned subnets outside the automatic pool, or the stack fails with Pool overlaps with other one on this address space on any host where Docker has already handed that subnet to another network.
When this answer stops applying#
- IPv6. Docker's route check only reads on-link routes for IPv4. If you enable IPv6 on your networks, choose the IPv6 pool yourself.
- Docker Desktop on macOS and Windows. Containers run inside a virtual machine with its own networking. Nothing here was tested there.
- Podman and rootless Docker. Both use different network back ends. Nothing here was measured on either, and Docker vs Podman covers how they differ.
- Swarm overlay networks. They use the global pool described above, not the local one this guide changes.
What to do next#
Write the pool into daemon.json before you need it. It is far easier on a new server than after forty networks exist. List the subnets your VPN and remote LANs use, alongside the plan in DNS for self-hosters, and pick a Docker range that misses all of them. If you are building the server now, Your first self-hosted server lists the other decisions that are hard to undo later.
Questions#
What does 'all predefined address pools have been fully subnetted' mean?
Docker has used every subnet in its address pools. The built-in pools hold 31 networks: fifteen /16 networks from 172.17.0.0 to 172.31.0.0 and sixteen /20 networks in 192.168.0.0/16. The default bridge takes one, and every compose project creates its own _default network. Run docker network prune to free networks no container uses, then set default-address-pools so you do not hit the wall again.
Does Docker avoid subnets that are already in use?
Partly. Before it picks a subnet automatically, Docker skips any that overlap an on-link route in the host's main routing table, plus the nameservers in /etc/resolv.conf. Your directly attached LAN is safe. A network reached through a gateway, a route in another routing table such as Tailscale's table 52, or a VPN that comes up later is not checked.
What does the size field in default-address-pools mean?
It is the prefix length of each network Docker creates, not the size of the pool. A base of 10.201.0.0/16 with size 24 gives 256 networks of 254 usable addresses each. A base of 192.168.91.0/24 with size 24, an example that circulates in tutorials, gives exactly one network, so the second network you create fails.
Do I need to set bip as well as default-address-pools?
Usually not. With no bip set, the default bridge docker0 takes the first subnet from the pool. On our test server, daemon.json set only default-address-pools with base 10.201.0.0/16 and size 24, and docker0 came up as 10.201.0.1/24. Set bip only if you want docker0 at a specific address.
Why do my old Docker networks still use 172.x after I changed daemon.json?
A network keeps the subnet it was created with. The pool setting applies only to networks created after the daemon restarts. Recreate each compose project's network with docker compose down followed by docker compose up -d, and remove and recreate any external network you made by hand once nothing is attached to it.
Can I use 172.240.0.0/16 for Docker networks?
You can, but you should not. RFC 1918 reserves 172.16.0.0 to 172.31.255.255 for private use, so 172.240.0.0/16 is public address space. While a Docker network holds that range, containers and the host send traffic for those real internet addresses into the bridge instead. Pick a block inside 10.0.0.0/8, which is large enough to find an unused corner.
Sources#
- Moby source, default local and global address pools (ipamutils, v28.1.1 and docker-v29.9.1)
- Moby source, InferReservedNetworks: on-link routes and nameservers excluded
- Docker docs, networking overview (subnet allocation, default address pools)
- Docker docs, dockerd reference (default-address-pools, configuration reload behavior)
- Docker docs, live restore
- Docker CLI reference, docker network prune
- Docker CLI reference, docker compose down
- Compose file reference, networks (ipam)
- RFC 1918, Address Allocation for Private Internets
- Tailscale source, Linux router (routing table 52)
Published . Last reviewed . Found something out of date? Tell us and we will fix it and log the change.