Change Docker subnets

Change Docker subnets

Docker is wasteful with IP subnets by default. On hosts with many networks, this sooner or later leads to the following error:

Error response from daemon: could not find an available, non-overlapping IPv4 address pool among the defaults to assign to the network

Looking at the created networks, the following pattern becomes apparent:

The docker0 bridge gets the subnet 172.17.0.0/16 by default. After that Docker assigns 172.18.0.0/16 through 172.31.0.0/16. Then follow 192.168.0.0/20 through 192.168.240.0/20. Once those are exhausted, the error above is thrown on every further attempt to create a network.

Reproduce the problem

If you want to test this yourself on a fresh system, the following Bash one-liner creates the maximum number of networks quickly:

$ for i in {1..256};do docker network create test$i; done

This lists all networks:

$ docker network inspect $(docker network ls | awk '$3 == "bridge" { print $1}') | jq -r '.[] | .Name + " " + .IPAM.Config[0].Subnet' -
bridge 172.17.0.0/16
test1 172.18.0.0/16
test2 172.19.0.0/16
test3 172.20.0.0/16
test4 172.21.0.0/16
test5 172.22.0.0/16
test6 172.23.0.0/16
test7 172.24.0.0/16
test8 172.25.0.0/16
test9 172.26.0.0/16
test10 172.27.0.0/16
test11 172.28.0.0/16
test12 172.29.0.0/16
test13 172.30.0.0/16
test14 172.31.0.0/16
test15 192.168.0.0/20
test16 192.168.16.0/20
test17 192.168.32.0/20
test18 192.168.48.0/20
test19 192.168.64.0/20
test20 192.168.80.0/20
test21 192.168.96.0/20
test22 192.168.112.0/20
test23 192.168.128.0/20
test24 192.168.144.0/20
test25 192.168.160.0/20
test26 192.168.176.0/20
test27 192.168.192.0/20
test28 192.168.208.0/20
test29 192.168.224.0/20
test30 192.168.240.0/20

Networks can be cleaned up with docker network prune.

Handing out a full /16 for every Docker network — who on earth needs more than 65,000 IPs for one network? After that at least /20 subnets are used, but with more than 4,000 addresses these are still vastly oversized. For many home users this also causes a concrete problem: the 192.168.178.0/24 range is typically used by Fritzbox and other home routers.

The fix

Drop the following lines into /etc/docker/daemon.json:

{
  "bip": "10.200.0.1/16",
  "default-address-pools": [{"base":"10.201.0.0/16", "size":24}]
}

I picked 10.200.0.0/16 and 10.201.0.0/16 here, but any private range works (anything in 10.0.0.0/8, 172.16.0.0/12 or 192.168.0.0/16). Just make sure there are no conflicts with existing internal networks or VPNs.

Restart dockerd with sudo systemctl restart docker. The configured ranges will be used for new networks. Existing networks have to be removed and recreated.

Validate the fix

With the same one-liners as above we can verify the change. First, clean up the test server with docker network prune. Then create 256 new networks with for i in {1..256};do docker network create test$i; done.

The following command confirms the networks now match our configuration:

$ docker network inspect $(docker network ls | awk '$3 == "bridge" { print $1}') | jq -r '.[] | .Name + " " + .IPAM.Config[0].Subnet' -
bridge 10.200.0.0/16
test1 10.201.64.0/24
test2 10.201.65.0/24
[...]
test192 10.201.255.0/24
test193 10.201.0.0/24
[...]
test256 10.201.63.0/24

Why Docker begins at 10.201.64.0/24 instead of the start of the pool is a mystery — the earlier ranges only get filled from test193 onwards.