Fix pip "Temporary Failure in Name Resolution" During Docker Builds
Fix pip install failing during a Django Docker build with Temporary failure in name resolution or Failed to establish a new connection: diagnose Docker DNS, corporate proxies, VPNs, and IPv6, and configure daemon DNS or build-time proxy arguments.
Problem statement
The pip install step of your Dockerfile retries and then fails:
WARNING: Retrying (Retry(total=4, connect=None, read=None, redirect=None, status=None))
after connection broken by 'NewConnectionError('<pip._vendor.urllib3.connection.HTTPSConnection
object>: Failed to establish a new connection: [Errno -3] Temporary failure in name resolution')':
/simple/django/
ERROR: Could not find a version that satisfies the requirement Django==5.2.6
The "could not find a version" line is misleading: pip never reached PyPI. Inside the build container, DNS lookups fail. The host can resolve pypi.org fine, but build containers use Docker's own networking and DNS configuration, which can break on VPNs, corporate networks, systems with systemd-resolved, or hosts with a broken firewall setup.
- Test DNS in a container:
docker run --rm busybox nslookup pypi.org. - If it fails, set DNS servers for Docker in
/etc/docker/daemon.json:{"dns": ["1.1.1.1", "8.8.8.8"]}and restart Docker. - Behind a corporate proxy, pass
--build-arg HTTPS_PROXY=http://proxy:3128(andHTTP_PROXY,NO_PROXY). - On a VPN, try
docker build --network=hostto use the host's resolver for the build. - Check that the firewall did not drop Docker's NAT rules (common after
iptablesornftableschanges).
Step-by-step solution
1. Reproduce outside the build
docker run --rm busybox nslookup pypi.org
docker run --rm python:3.12-slim python -c "import urllib.request; print(urllib.request.urlopen('https://pypi.org/simple/').status)"
If these fail too, the problem is Docker networking in general, not your Dockerfile. If they work but the build fails, check BuildKit settings and proxy arguments.
2. Fix Docker's DNS servers
Docker copies the host's /etc/resolv.conf into containers, but skips 127.0.0.x entries, because a container cannot reach the host's loopback. On Ubuntu with systemd-resolved, the host file points at 127.0.0.53, so Docker falls back to Google DNS, which some networks block. Set explicit servers that work on your network:
{
"dns": ["10.0.0.2", "1.1.1.1"]
}
Save as /etc/docker/daemon.json and run sudo systemctl restart docker. Use your network's resolver (for example the VPC resolver at .2 on AWS) first.
3. Pass proxy settings to the build
Proxies configured in your shell do not reach the build automatically in all setups. Docker predefines proxy build arguments, so they do not need ARG lines:
docker build \
--build-arg HTTP_PROXY=http://proxy.corp:3128 \
--build-arg HTTPS_PROXY=http://proxy.corp:3128 \
--build-arg NO_PROXY=localhost,127.0.0.1,.corp \
-t myapp .
Alternatively, configure ~/.docker/config.json with a proxies section so every build and container gets them. If the proxy inspects TLS, pip will then fail with certificate errors; add the corporate CA to the image or use PIP_CERT.
4. Use host networking for builds on VPNs
Some VPN clients set up DNS that only the host can reach. Building with the host's network stack avoids it:
docker build --network=host -t myapp .
In Compose: build: { context: ., network: host }. This affects only the build, not the running container.
5. Check firewall and IP forwarding
Docker needs IP forwarding and NAT rules to give containers internet access. Tools like UFW, firewalld, or a custom nftables ruleset can remove them:
sysctl net.ipv4.ip_forward
sudo iptables -t nat -L POSTROUTING -n | grep MASQUERADE
ip_forward = 0 or a missing MASQUERADE rule for the Docker subnet means containers cannot reach the internet. Restarting Docker recreates its rules; make firewall configuration Docker-aware so they survive reloads.
Explanation
Each build step runs in a temporary container attached to Docker's default bridge (or BuildKit's equivalent). Its /etc/resolv.conf comes from the host file with loopback addresses removed, or from the daemon's dns setting. Packets leave through NAT on the bridge. If the resolver is unreachable from the bridge network, or if NAT is broken, getaddrinfo fails with "Temporary failure in name resolution", and pip reports it as a connection problem followed by "no matching distribution".
Edge cases / notes
- Private package indexes. An internal index on a
.corpdomain needs your internal DNS servers indaemon.json, not public ones. - CI runners. Self-hosted runners inside private networks often need the same daemon DNS configuration.
- Cache hides it. If the dependency layer is cached, the build passes until requirements change. Test with
--no-cacheafter fixing.
Next steps
- Avoid network work on most builds with Speed Up Django Docker Builds With Cache.
- Review your Dockerfile with Dockerize Django for Production.
- For TLS errors after the proxy is configured, see Fix Django SSL CERTIFICATE_VERIFY_FAILED.
FAQ
Why does pip say no version satisfies the requirement?
It could not download the package index at all, so it saw no versions. The real error is the earlier DNS failure.
Why does it work on my laptop but not the build server?
The build server may use systemd-resolved, a VPN, or a corporate proxy, each of which affects container DNS differently.
Is --network=host safe for builds?
It gives build steps the host's network access, which is usually fine on a trusted build machine. Avoid it for untrusted Dockerfiles.
Should I hard-code public DNS in daemon.json?
Prefer your network's resolver, with a public one as fallback. Public-only DNS cannot resolve private names.