Docker Container Can't Reach the Internet: 5 Fixes That Actually Work
You pull a fresh container, run it, and immediately hit a wall: curl times out, apt-get update fails, your app can't call an API. The host machine is fine. The container is isolated. What went wrong?
This happened to me last week while setting up a staging environment. Three containers, three different networking symptoms, one root cause I'd seen too many times. Here's the systematic checklist that got everything working again — no guesswork, just the five fixes that cover 95% of cases.
The symptom first: what exactly is broken?
Before you touch any config, confirm the failure mode:
# Test from inside the container
docker exec <container> ping -c 3 8.8.8.8 # ICMP to public DNS IP
docker exec <container> nslookup google.com # DNS resolution only
docker exec <container> curl -v https://example.com # Full HTTP to external
| Result | Likely cause |
|---|---|
ping 8.8.8.8 works, nslookup fails |
DNS resolver misconfiguration |
ping fails, nslookup fails |
Network stack / bridge / NAT issue |
ping works, curl times out on HTTPS |
Firewall or MTU issue |
| Only some containers fail | Container-specific network config |
Fix each layer in order. Don't jump to iptables before checking DNS.
Fix 1: Set explicit DNS servers (most common fix)
Docker's built-in DNS resolver sometimes can't reach your upstream DNS, especially on Linux hosts with systemd-resolved or cloud VPS providers that use private DNS ranges.
Symptom: containers can ping IPs but can't resolve hostnames.
Fix: add DNS servers explicitly in docker-compose.yml:
services:
web:
image: nginx:alpine
dns:
- 8.8.8.8
- 1.1.1.1
# or use the host's resolved DNS
dns_search: local.domain
Or set it globally in /etc/docker/daemon.json:
{
"dns": ["8.8.8.8", "1.1.1.1"]
}
Then restart: sudo systemctl restart docker.
Why this works: Docker defaults to the host's resolv.conf, which on many systems points to 127.0.0.53 (systemd-resolved). Containers can't always reach that loopback address. Pointing directly to public DNS bypasses the issue entirely.
Fix 2: Check the bridge network MTU
If ping works but curl hangs on large responses, you're likely hitting an MTU mismatch. Docker creates a bridge (docker0) with MTU 1500 by default, but many cloud providers and VPNs use jumbo frames or tunnel encapsulation that reduces the effective MTU.
Test:
# From host
ip link show docker0
# From container (try pinging with small packets)
docker exec <container> ping -c 3 -s 1472 8.8.8.8 # 1500 - 28 header
docker exec <container> ping -c 3 -s 1400 8.8.8.8 # conservative
Fix: set MTU on the daemon:
// /etc/docker/daemon.json
{
"mtu": 1400
}
Or per-network at creation:
docker network create -o com.docker.network.driver.mtu=1400 mynet
Rule of thumb: if you're on AWS, GCP, Azure, or using WireGuard/Cloudflare Tunnel, start with MTU 1400. It's the safe default for tunneled environments.
Fix 3: Verify iptables / firewalld isn't blocking outbound
Linux firewalls sometimes drop forwarded traffic from Docker's bridge subnet. This is rare but painful because the symptom looks identical to a DNS problem.
Check:
# See if Docker's rules are present
sudo iptables -L DOCKER -n -v
sudo iptables -L FORWARD -n -v
# Check for explicit DROP/REJECT rules
sudo iptables -L FORWARD -n -v | grep -i drop
Fix: ensure the FORWARD chain allows established connections and Docker traffic:
sudo iptables -A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
sudo iptables -A FORWARD -s 172.16.0.0/12 ! -d 172.16.0.0/12 -j ACCEPT
If you're using firewalld:
sudo firewall-cmd --zone=trusted --add-interface=docker0
sudo firewall-cmd --runtime-to-permanent
Fix 4: Disable IPv6 if it's causing silent failures
Some hosting providers and home routers have broken IPv6 routing. Docker enables IPv6 by default on the bridge, and containers may try IPv6 first — silently failing instead of falling back to IPv4.
Check:
docker exec <container> cat /etc/gai.conf 2>/dev/null
docker exec <container> ip -6 addr show
Fix: disable IPv6 in Docker:
// /etc/docker/daemon.json
{
"ipv6": false
}
Or force IPv4-only at runtime:
docker run --platform linux/amd64 <image>
# or add to docker-compose
network_mode: bridge
ip_stack: ipv4
Fix 5: The --network=host escape hatch (and when to use it)
If none of the above work and you need something running now, --network=host bypasses the bridge entirely — the container shares the host's network stack directly.
docker run --network=host <image>
# or in compose
services:
web:
network_mode: host
Pros: instant connectivity, no NAT overhead, no MTU/DNS worries.
Cons: port conflicts (container binds directly to host ports), reduced isolation, some security-sensitive workloads shouldn't use it.
Use this for: batch jobs, one-off tools, debugging. Don't use it for: production web servers, databases exposed to the internet, or anything handling sensitive data.
My actual debugging story (the one that brought all five together)
Last Tuesday I spun up a Python scraper container on a Linode VPS. curl google.com from inside returned curl: (6) Could not resolve host. Ping to 8.8.8.8 worked. So it was DNS.
Fix 1 didn't work — I'd already set dns: [8.8.8.8] in compose and it was still failing. Checked the host's systemd-resolved status: the DNS proxy at 127.0.0.53 was running but refusing external queries (expected). The explicit 8.8.8.8 in compose should have worked though.
Turns out Fix 4 was the real culprit: the VPS had IPv6 enabled globally, and Docker was creating an IPv6-capable bridge. The container preferred IPv6 for google.com (AAAA record), got a routable IPv6 address, but the VPS provider's IPv6 router was dropping packets silently. Forcing ip_stack: ipv4 in the network config made it use IPv4 exclusively, and everything worked.
Lesson: when multiple fixes seem to apply, check the one that causes "silent" failures (IPv6 routing) before the noisy ones (DNS).
Quick reference: what to try first
- Can it ping an IP but not a hostname? → Fix 1 (DNS)
-
Can it ping small packets but
curlhangs? → Fix 2 (MTU) - Only specific containers fail? → Fix 4 (IPv6 per-container)
- Everything fails after a host firewall change? → Fix 3 (iptables)
- Need it working in 30 seconds and isolation doesn't matter? → Fix 5 (host network)
Full guide with screenshots and edge cases: https://jiunotes.top/en/articles/docker-container-no-internet-fixes
More DevOps debugging notes: ops.jiunotes.top
Top comments (0)