Docker networking is a fucking nightmare, and after 6 years of debugging this shit, I can tell you exactly how it's going to break. Same five ways, every goddamn time. Here's what's going to ruin your day:
1. Containers On Different Networks Can't Talk
This is the #1 cause of "it works locally but fails in production" networking issues. By default, Docker creates isolated bridge networks for different projects, and containers can't communicate across networks unless explicitly connected. The Docker networking model is designed for isolation, which bites beginners who expect containers to just work together like processes on a single host.
What you see: curl: (7) Failed to connect
or connection refused
when one container tries to reach another.
How to diagnose: Use Docker network inspection commands to see your network topology.
## Check which network each container is on
docker network ls
docker network inspect bridge
docker network inspect PROJECT_default
## See if containers are on the same network
docker inspect CONTAINER_NAME | grep NetworkMode
The fix: Use Docker Compose networking or create custom bridge networks:
## Create a custom network
docker network create myapp-network
## Connect existing containers
docker network connect myapp-network container1
docker network connect myapp-network container2
Had this exact problem last month - spent like 3 hours debugging why our API couldn't reach the database. Tried everything, checked firewall rules, restarted containers, the works. Turned out they were on different fucking networks. Docker Compose creates a new network for each project by default, so if you have multiple docker-compose files, they can't talk to each other. Stupid default that gets everyone. This network isolation behavior is intentional for security but a nightmare for multi-service architectures.
2. DNS Resolution Completely Broken
Docker provides automatic DNS resolution for container names, but only on custom networks. Containers on the default bridge network can't resolve each other's names.
What you see: ping: cannot resolve hostname 'web': Unknown name
or similar DNS failures.
Quick diagnosis:
## Check DNS inside a container
docker exec -it CONTAINER_NAME cat /etc/resolv.conf
docker exec -it CONTAINER_NAME nslookup other-container-name
## Test if IP connection works when hostname doesn't
docker exec -it CONTAINER_NAME ping 172.18.0.5
If IP addresses work but hostnames don't, it's a DNS problem. The embedded DNS server at 127.0.0.11 only works on user-defined networks.
The fix: Move containers to a custom network or use IP addresses:
## Create network with automatic DNS
docker network create --driver bridge myapp
docker run --network myapp --name web nginx
docker run --network myapp --name db postgres
The DNS server at 127.0.0.11 only works on custom networks. Default bridge network has no DNS, so your containers are fucked. This breaks every beginner's setup because they use docker run
instead of docker-compose
and wonder why nothing can find anything else. The embedded DNS resolver is Docker's attempt at service discovery, but it's hobbled on the default network for legacy compatibility reasons.
3. Published Ports Don't Work From Outside
Your container exposes port 8080, you publish it with -p 8080:8080
, but external clients can't connect. This happens most often with strict firewall rules or when Docker's iptables integration conflicts with system settings.
What you see: Connection timeouts from external clients, but docker exec
works fine inside containers.
Diagnostic steps:
## Verify port is published
docker port CONTAINER_NAME
## Check if it's bound to the right interface
netstat -tlnp | grep :8080
ss -tlnp | grep :8080
## Test locally first (replace 8080 with your actual port)
curl localhost:8080
Common causes and fixes:
- Docker bound to wrong interface: Use
-p 0.0.0.0:8080:8080
instead of-p 8080:8080
- Firewall blocking Docker: Add Docker to firewall rules or disable temporarily for testing
- SELinux/AppArmor interference: Check security policies on RHEL/Ubuntu systems
Docker binds to 127.0.0.1 by default, not 0.0.0.0, so it only accepts local connections. This tripped me up for weeks when I started. Also, UFW firewall will block Docker's ports even if you explicitly allow them - Docker writes its own iptables rules that bypass UFW. The iptables integration is a constant source of confusion because Docker manipulates iptables directly without consulting existing firewall rules.
4. No Internet Access from Containers
Containers can talk to each other but can't reach external websites or APIs. Usually caused by DNS configuration or routing problems on the host system.
Quick test:
## Test DNS resolution to external sites
docker exec CONTAINER_NAME nslookup google.com
## Test direct IP connection (bypasses DNS)
docker exec CONTAINER_NAME curl -I 8.8.8.8
## Check container's routing table
docker exec CONTAINER_NAME ip route
Most common fixes:
- Fix Docker daemon DNS: Edit daemon configuration
/etc/docker/daemon.json
:
{
"dns": ["8.8.8.8", "8.8.4.4"]
}
- Corporate proxy issues: Set HTTP_PROXY environment variables
- MTU size problems: Lower MTU on Docker networks if packets get dropped
If you're using a local DNS server like Pi-hole, containers can't reach it at 127.0.0.1 because container localhost isn't host localhost. Use the Docker bridge IP (usually 172.17.0.1) instead, or just use 8.8.8.8 and be done with it. This localhost confusion hits everyone because container networking creates separate network namespaces.
5. Intermittent Network Failures Under Load
Everything works fine during testing, then containers randomly lose connectivity in production under heavy traffic. This is usually related to connection tracking limits or system resource exhaustion.
Signs of this problem:
- Works perfectly with low traffic
- Random timeouts during peak load
- Connection reset by peer errors
- Containers appear healthy but can't communicate
Investigation commands:
## Check connection tracking table
cat /proc/net/nf_conntrack | wc -l
cat /proc/sys/net/netfilter/nf_conntrack_max
## Monitor network errors
docker exec CONTAINER_NAME netstat -i
This bit us in the ass with a Node.js app that opened database connections like it was going out of style. Hit maybe 1000 concurrent users and containers just started randomly shitting themselves. Took forever to figure out - turns out the connection tracking table was maxed out. Default limit is like 65k or something, which sounds like a lot until you're actually running microservices that each open 50+ connections. The nf_conntrack module tracks every TCP connection for NAT and firewalling, and when it's full, new connections just get dropped silently.
The Nuclear Option: Reset Everything
When Docker networking is completely broken and you can't figure out why:
## Stop all containers
docker stop $(docker ps -q)
## Remove all networks (except defaults)
docker network prune
## Restart Docker daemon
sudo systemctl restart docker
## Verify clean state
docker network ls
This nuclear option has saved my ass more times than I'd like to admit. When everything's broken and you've got angry users breathing down your neck, sometimes you just blow it all up and start over. Usually works, and when it doesn't, you were probably fucked anyway.
Look, Docker networking is just Linux networking with like 5 layers of abstraction that hide what's actually happening. The defaults are hot garbage for anything beyond hello-world demos. In production? You need explicit networks, health checks, monitoring, and a lot of coffee, or you'll be debugging this bullshit every weekend when normal people are doing normal people things.