The failure is specific: Docker cannot find an acceptable automatic subnet for a new bridge network. It is not a statement that the machine is out of addresses, and it is not a statement that the VPN is broken. Two questions decide the diagnosis: which candidate blocks the daemon can see, and which of them its allocator excludes in this environment. Work through them in that order, because the second question is meaningless until the first is answered.

Confirm what failed before you change anything

Run the steps below from the host that runs the daemon. A shell talking to a remote context is describing a different machine, and that mistake alone can waste an hour.

Note the Engine version in the ticket at the same time. Every heuristic in the rest of this runbook is version-scoped, and a claim without a version is a rumour.

Count the allocations you already have

List the networks, then inspect the ones that matter: docker network ls followed by docker network inspect NETWORK_NAME. Read their IPAM subnets and the containers attached to each. Several Compose projects consume more blocks than their container counts suggest, because every project tends to create its own default network. A stopped container does not release its network's allocation.

Check the pool configuration as well, since a default pool has a base range and a per-network prefix size. Large per-network allocations exhaust the available blocks faster, and that is a capacity fact rather than a routing problem.

Write the count down. It is the baseline that tells you whether the next change helped.

Compare the daemon's view of routes

A VPN can influence candidate selection, but the exact implementation decides how. In Moby 28.2.2 on Linux, InferReservedNetworks collects nameserver addresses and certain on-link IPv4 routes, and it does not simply reserve every route from every table. Collected link routes also exclude an unspecified destination address, so a 0.0.0.0/1 route alone does not reserve every address. A different broad on-link prefix, such as 128.0.0.0/1, can intersect the familiar 172 and 192 candidate ranges.

Compare ip -4 route show table all before and after the VPN connects, on the daemon's host. The delta is the evidence, and the version is the scope of the claim.

Do not generalize one release's heuristics to every release. That is how a forum answer becomes a policy change.

Pick the smallest fix that works

If a confirmed unused network holds a block you need, plan its targeted retirement with the owner instead of pruning broadly. For a new network, an explicitly reviewed unused subnet avoids depending on automatic selection at all. That subnet still has to avoid real Docker, LAN and required VPN destinations, because private does not mean safe. Routing a real remote prefix would trade the allocation error for broken access.

Changing default-address-pools is a broader policy about future allocations. Choose a non-conflicting base and a suitable allocated size, merge it with the existing daemon settings, and plan the documented restart impact. Existing networks are not silently renumbered.

Do not disable company route pushes to make allocation succeed. An explicit address plan usually solves the local problem without weakening a mandatory policy.

Accept allocation and routing separately

After the change, inspect the subnet Docker actually created and test the application's VPN and local dependencies. Successful allocation and correct runtime routing are two different acceptance checks, and passing the first is not evidence for the second. If the app cannot reach a resource behind the tunnel, you have a routing problem that the pool error was hiding. Record both results in the ticket.

Keep the route delta and the pool count with the change record, because the next VPN client update can alter the first one without touching Docker.

The runbook is complete when someone else can repeat those two checks and get the same answer.

Erthyglow kevrennek