The VPN is up, the browser reaches internal sites, and the container still cannot resolve the same names. A package install dies with a name-resolution error such as EAI_AGAIN, or an application cannot find a database host that the host machine resolves without complaint. The container is not using the resolver the VPN installed. Docker copied the host's resolver configuration when the container was created, and nothing copied it again afterwards.

Two resolver views on one machine

On Linux, Docker gives each container a copy of the host's resolver configuration at creation time, filtering out loopback addresses, and runs an embedded DNS server at 127.0.0.11 that forwards queries outward. A VPN client that rewrites /etc/resolv.conf after the container started changes nothing inside it. Restarting the container helps only if the host resolver happens to be correct at that moment.

Docker Desktop on macOS or Windows adds a layer, because the daemon lives in a virtual machine. The host's VPN resolver may never be visible to that VM, so restarting changes nothing at all. Compare the two views before choosing a fix: docker exec mycontainer cat /etc/resolv.conf beside the host's resolver list.

A container listing a public resolver while the host lists an internal one will resolve public names and fail internal ones. That split is the signature of this problem.

Option A: pass the tunnel resolver per container

Docker accepts per-container DNS settings, which is the smallest change that works. Read the VPN's resolver addresses first: scutil --dns on macOS, resolvectl status or the client's own status output on Linux. Then start the container with them, for example docker run --dns 192.168.2.1 --dns 192.168.123.1 myimage. In Compose the same setting is a list under the service, where it lives in version control instead of someone's shell history.

A dns array in /etc/docker/daemon.json applies to every new container, which sounds tidier and is not. It also applies when the tunnel is down, and a resolver reachable only through the VPN then breaks ordinary image pulls. Per-container settings are easier to reason about and easier to undo.

Option B: let the container share the host network

Host networking sidesteps the copy entirely, because the container uses the host's resolver and the host's tunnel. It also removes the network isolation you probably wanted, and it does not behave the same way under Docker Desktop. On Linux it is the pragmatic choice for a one-off tool that must resolve internal names.

For builds, neither option applies, since build steps do not inherit run-time flags. Build with host networking where the platform allows it, or keep dependency installation in a stage that does not need the tunnel.

What neither option fixes

A DNS address is useless if the container has no route to it. The VPN resolver usually sits inside the tunnel's address space, so the container needs a path to that prefix. Docker's default bridge normally routes out through the host, which is enough when the host has the tunnel route and forwarding is enabled. Custom bridge networks with their own address pools can collide with the VPN subnet, and then queries go somewhere else entirely.

Check the Docker network subnet against the VPN subnet before you debug the resolver itself. Overlap produces a failure that looks exactly like a broken DNS server.

Verify inside the container

Test with getent hosts db.internal inside the container rather than with a browser or a language runtime, because those apply their own DNS policy and their own caching. If getent resolves the name and the application still fails, the resolver is now correct and the remaining problem is application-level. If it does not resolve, the DNS step is not finished.

Recreate the container after changing its DNS settings. Editing /etc/resolv.conf inside a running container disappears with it and hides the configuration problem for exactly as long as it takes someone to restart it.

ସମ୍ପର୍କିତ ପ୍ରବନ୍ଧ