Docker networking failures are irritating in a specific way: the error message is never the actual problem. “Connection refused” could mean a dozen different things. Your containers are running, ports are published, and nothing works. This guide goes symptom by symptom through the most common Docker networking failures on Linux, with a Windows Server section at the end.
Tested on Ubuntu 24.04 with Docker Engine 27.x.
Table of Contents
- Quick Diagnosis: Start Here
- Issue 1: Containers Can’t Talk to Each Other
- Issue 2: Published Port Not Reachable
- Issue 3: DNS Fails Inside a Container
- Issue 4: Port Binding Conflict
- Issue 5: Docker Networking Breaks After iptables Flush
- Issue 6: Containers Can’t Reach the Host Machine
- Issue 7: Silent Failures: MTU Mismatch
- Windows Server: HNS and the nat Network Driver
- Error Messages Quick Reference
- Configuration Mistakes to Avoid
- Getting Help: Logs and Debug Info
- Prevention Tips
Quick Diagnosis: Start Here
Before diving into a specific issue, run these four commands. They give you a complete picture of your network state in under two minutes.
1. List all Docker networks:
docker network ls

2. Check which network your container is actually on:
docker inspect <container_name> --format '{{json .NetworkSettings.Networks}}'
3. Inspect the network in question, this shows attached containers and their IPs:
docker network inspect <network_name>

4. Check if the container is actually running and what ports are published:
docker ps

If those four commands don’t immediately reveal the problem, find your specific symptom below.
Symptoms and Likely Causes
| Symptom | Likely Cause | Jump To |
|---|---|---|
Connection refused between containers | Different networks, or default bridge (no DNS) | Issue 1 |
| Published port unreachable from host or LAN | Port conflict, wrong interface binding, iptables | Issue 2 |
Name or service not known inside container | Docker DNS not working, bad resolv.conf | Issue 3 |
address already in use on container start | Another process owns that host port | Issue 4 |
| All container networking suddenly broken | iptables chains wiped by flush or firewall tool | Issue 5 |
| Container can’t ping or reach the host | Host firewall blocking Docker subnet | Issue 6 |
| Connections hang or partially work, no errors | MTU mismatch on VPN or cloud network | Issue 7 |
Issue 1: Containers Can’t Talk to Each Other
Symptoms:
curl: (7) Failed to connectwhen one container tries to reach another by nameping: unknown host <container_name>from inside a container- App logs show database connection refused even though both containers are running
Cause: The most common reason is that both containers are on the default bridge network. The default bridge does not provide DNS. Containers can only reach each other by IP, and those IPs change on restart. The fix is a user-defined network, which gives you automatic DNS resolution by container name.
A secondary cause is that the containers are on different networks entirely.
Diagnosis:
# Check which network container A is on
docker inspect app_container --format '{{json .NetworkSettings.Networks}}'
# Check which network container B is on
docker inspect db_container --format '{{json .NetworkSettings.Networks}}'
If the network names don’t match, that’s your problem.
Fix: create a user-defined network and attach both containers:
# Create a named network
docker network create myapp-net
# If containers are already running, connect them to the new network
docker network connect myapp-net app_container
docker network connect myapp-net db_container
# Verify both containers appear in the network
docker network inspect myapp-net
For new deployments, pass --network at run time:
docker run -d --name db --network myapp-net postgres:16
docker run -d --name app --network myapp-net my-app-image
Now app can reach db at the hostname db no IP addresses needed.
Verify:
# Ping by container name from inside the app container
docker exec -it app_container ping db_container
# Or test the actual service port
docker exec -it app_container curl http://db_container:5432
Why user-defined networks? On the default
bridge, Docker doesn’t run a DNS resolver. On any user-defined network, Docker injects an internal DNS server at127.0.0.11that resolves container names automatically. This is also why Docker Compose works out of the box. Compose always creates a user-defined network for each project.
Issue 2: Published Port Not Reachable
Symptoms:
curl: (7) Failed to connect to localhost port 8080from the host- Port shows in
docker psbut times out from another machine - Browser shows “This site can’t be reached”
Cause: Three things commonly cause this: (1) another process already owns the host port, (2) Docker bound to 127.0.0.1 instead of 0.0.0.0 so the port is only reachable locally, or (3) iptables rules are blocking the traffic.
Diagnosis:
# Step 1: Confirm the port mapping in docker ps
docker ps --format "table {{.Names}}\t{{.Ports}}"
Look at the PORTS column. 0.0.0.0:8080->80/tcp means it’s reachable from anywhere. 127.0.0.1:8080->80/tcp means localhost only. That distinction matters.
# Step 2: Test the port locally on the host
curl http://127.0.0.1:8080
# Step 3: Test from another machine (replace 192.168.1.100 with your host IP)
# Install nmap if needed: sudo apt install nmap
nmap -p 8080 192.168.1.100
# Step 4: Check Docker's iptables rules for the DOCKER chain
sudo iptables -L DOCKER -n -v

Fix, if Docker bound to 127.0.0.1 only:
Stop and re-run the container with an explicit 0.0.0.0 binding:
docker run -d -p 0.0.0.0:8080:80 my-image
Or set this globally in /etc/docker/daemon.json so all published ports bind to all interfaces by default. Only do this if your host firewall controls external access:
{
"ip": "0.0.0.0"
}
Then sudo systemctl restart docker.
Fix, if iptables is blocking traffic:
Rather than disabling your firewall, add a targeted rule to the DOCKER-USER chain. Docker reads this chain before its own rules. It’s the safe place for custom firewall logic:
# Allow traffic from your LAN to the container port
sudo iptables -I DOCKER-USER -p tcp --dport 8080 -s 192.168.1.0/24 -j ACCEPT
Verify:
curl http://127.0.0.1:8080
# Expected: HTTP response from your container, not a connection error
Issue 3: DNS Fails Inside a Container
Symptoms:
curl: (6) Could not resolve host: google.comfrom inside a containernslookup: can't resolveinside a container- App can’t reach external APIs by hostname, but can reach them by IP
Cause: Docker injects 127.0.0.11 as the DNS resolver inside containers on user-defined networks. If that resolver can’t forward queries upstream, because the host’s systemd-resolved is conflicting, because /etc/resolv.conf inside the container is empty, or because iptables is blocking UDP port 53, name resolution fails entirely.
Diagnosis:
# Step 1: Check what DNS server the container is configured to use
docker exec -it <container_name> cat /etc/resolv.conf

You should see nameserver 127.0.0.11. If you see nameserver 127.0.0.53 or nothing useful, Docker is inheriting a broken resolver from the host.
# Step 2: Test DNS resolution from inside the container
docker exec -it <container_name> nslookup google.com
# Step 3: Check if the host's systemd-resolved is running and what port it's on
sudo ss -ulnp | grep 53
# Step 4: Check iptables isn't blocking Docker's DNS proxy on port 53
sudo iptables -L DOCKER -n | grep 53
Fix, override DNS for a single container:
docker run --dns 1.1.1.1 --dns 8.8.8.8 my-image
Fix, override DNS globally for all containers (edit /etc/docker/daemon.json):
{
"dns": ["1.1.1.1", "8.8.8.8"]
}
sudo systemctl restart docker
Fix, if systemd-resolved is conflicting on Ubuntu:
Ubuntu 22.04+ runs systemd-resolved, which binds to 127.0.0.53. This can interfere with Docker’s DNS proxy. Point Docker at the real upstream resolver instead:
# Find your actual upstream DNS server
resolvectl status | grep "DNS Servers"
# Then add that IP to daemon.json instead of 127.0.0.53
Verify:
docker exec -it <container_name> nslookup google.com
# Expected output: Server: 127.0.0.11 (or your override), followed by a resolved address
Issue 4: Port Binding Conflict
Symptoms:
docker: Error response from daemon: driver failed programming external connectivity: Bind for 0.0.0.0:80 failed: port is already allocated- Container fails to start, exits immediately with a port error
Cause: Something else on the host, whether another container, nginx, Apache, or any other service, is already listening on the host port you’re trying to publish. Docker can’t bind to a port that’s already taken.
Diagnosis:
# Find what's using port 80 (replace 80 with your port)
sudo ss -tlnp | grep :80

The output shows the listening address, port, and the process name with PID in brackets. For example: users:(("nginx",pid=1234,fd=6)).
# Alternative using lsof (install if needed: sudo apt install lsof)
sudo lsof -i :80
# If it's another Docker container
docker ps | grep "0.0.0.0:80"
Fix, stop the conflicting process:
# If it's a system service
sudo systemctl stop nginx
# If it's another Docker container
docker stop <container_id>
Fix, use a different host port instead:
If you can’t stop the conflicting service, map Docker to a different host port:
# Map host port 8080 to container port 80
docker run -d -p 8080:80 my-image
In a Docker Compose file:
ports:
- "8080:80"
Verify:
docker ps
# Confirm the container is now running with the new port mapping in the PORTS column
curl http://localhost:8080
Issue 5: Docker Networking Breaks After iptables Flush
Symptoms:
- All container networking suddenly stops working
- Containers can’t reach the internet or each other
- Problem started right after running
iptables -F, enablingufw, or running a security hardening script
Cause: Docker manages a set of iptables chains, DOCKER, DOCKER-USER, DOCKER-ISOLATION-STAGE-1, and DOCKER-ISOLATION-STAGE-2 that it creates when the daemon starts. These chains route traffic to and from containers. When you run iptables -F or a tool like ufw resets the firewall, Docker’s chains get wiped. Container traffic has nowhere to go.
Think of iptables chains as a sorted list of traffic rules. Docker adds its own rules to route packets into containers. Flush the list, and Docker’s routing disappears.
Diagnosis:
# Check if Docker's chains are present — if this returns nothing, they're gone
sudo iptables -L | grep DOCKER
No output means Docker’s firewall rules have been wiped.
Fix, restart the Docker daemon to regenerate all rules:
sudo systemctl restart docker
Containers keep running through a daemon restart on Linux; their processes aren’t killed. But verify:
docker ps
# Confirm containers are still in "Up" state
Fix, if you’re using ufw, configure it to work with Docker instead of against it:
Don’t disable ufw. Instead, allow Docker’s bridge subnet through the FORWARD chain:
# Edit /etc/default/ufw
sudo nano /etc/default/ufw
# Change: DEFAULT_FORWARD_POLICY="DROP"
# To: DEFAULT_FORWARD_POLICY="ACCEPT"
sudo ufw reload
sudo systemctl restart docker
Verify:
# Confirm Docker's chains are back
sudo iptables -L DOCKER -n -v
# Test container connectivity
docker exec -it <container_name> curl http://google.com
Rule of thumb: Never run
iptables -Fon a Docker host without immediately restarting the Docker daemon. Better yet, use theDOCKER-USERchain for your custom rules. Docker never touches that chain’s contents, so your rules survive a daemon restart.
Issue 6: Containers Can’t Reach the Host Machine
Symptoms:
- A container can reach the internet but can’t connect to a service on the host (like a local database)
ping 172.17.0.1from inside a container fails- App inside a container can’t connect to
localhostbecause inside a container,localhostmeans the container itself, not the host
Diagnosis:
# Find the Docker bridge IP — this is the host's address as seen from containers
ip addr show docker0

The inet line shows the host’s Docker bridge IP, typically 172.17.0.1. Containers on the default bridge can reach the host at this address.
# Test from inside the container
docker exec -it <container_name> ping 172.17.0.1
If ping fails, the host firewall is likely blocking the Docker subnet.
Fix, allow the Docker bridge subnet to reach the host:
# Add a targeted INPUT rule for the docker0 interface
sudo iptables -I INPUT -i docker0 -j ACCEPT
For user-defined networks, the bridge interface has a generated name. Find it:
docker network inspect myapp-net --format '{{.Options}}'
# Look for com.docker.network.bridge.name, or use:
ip link show | grep br-
Then apply the same rule with that interface name.
Fix, use host-gateway for a portable solution (Docker 20.10+):
Instead of hardcoding the bridge IP, use Docker’s built-in host-gateway target. It always resolves to the correct host IP regardless of which bridge network you’re on:
docker run --add-host=host.docker.internal:host-gateway my-image
In Docker Compose:
extra_hosts:
- "host.docker.internal:host-gateway"
Verify:
docker exec -it <container_name> ping host.docker.internal
# Should resolve and respond from the host
Issue 7: Silent Failures: MTU Mismatch
Symptoms:
- Connections appear to work but hang partway through, especially for large responses
- Small requests succeed (like a health check ping) but large file transfers or API responses never complete
- Problem only appears inside Docker, not on the host directly
- Common in AWS, GCP, or VPN-connected environments
Cause: Docker’s default MTU is 1500 bytes. MTU is the largest packet size the network can carry. In VPN tunnels or cloud environments, the real MTU is often lower: 1450 in many VPN setups, 1480 in some cloud environments. Large packets get silently dropped. Small packets get through fine. That’s why the problem looks intermittent, even though it isn’t.
Diagnosis:
# Check the host's primary interface MTU
ip link show eth0
# Look for 'mtu XXXX' in the output — if it's less than 1500, you may have a mismatch
# Use tcpdump to watch for retransmissions on the Docker bridge
sudo tcpdump -i docker0 -n

While tcpdump is running, reproduce the failing connection from another terminal. Repeated retransmissions of the same sequence numbers point to packet loss, not a flaky service.
# Check the current MTU on Docker's bridge network
docker network inspect bridge --format '{{json .Options}}'
Fix, set Docker’s MTU to match or be slightly below the host’s effective MTU:
Edit /etc/docker/daemon.json:
{
"mtu": 1450
}
sudo systemctl restart docker
# Recreate any custom networks to pick up the new MTU
docker network rm myapp-net
docker network create myapp-net
Verify:
docker network inspect bridge --format '{{json .Options}}'
# Should show: {"com.docker.network.driver.mtu":"1450"}
Test the previously failing large transfer, it should now complete successfully.
Windows Server: HNS and the nat Network Driver
Docker networking on Windows Server works differently at every layer. Instead of iptables and Linux bridges, Windows uses the Host Networking Service (HNS), a Windows component that manages virtual switches and network endpoints for containers. The default network driver is nat (not bridge), and the bridge driver doesn’t exist on Windows.
Common Windows-specific symptoms:
- Containers fail to start with HNS-related errors after a Windows update or unexpected reboot
docker network lsshows thenatnetwork but containers can’t reach the internet- Network endpoints get stuck in a bad state after Docker restarts
Diagnosis in PowerShell:
# Check Docker service status
Get-Service docker
# List Docker networks
docker network ls
# Inspect the default nat network
docker network inspect nat
# View recent Docker events for networking errors
docker events --since 1h
Fix, reset HNS state (the standard fix for corrupted Windows container networking):
Warning: This drops all container network connections. Stop your containers first.
# Stop all running containers
docker stop $(docker ps -q)
# Stop Docker and the HNS service
Stop-Service docker
Stop-Service hns
# Clear HNS policy state
Remove-Item -Recurse -Force "C:\ProgramData\Microsoft\Windows\HNS\Policy"
# Restart both services
Start-Service hns
Start-Service docker
Verify on Windows:
# Test internet connectivity from a Windows container
docker run --rm mcr.microsoft.com/windows/nanoserver:ltsc2022 cmd /c ping google.com
Key differences to remember:
| Aspect | Linux Docker | Windows Server Docker |
|---|---|---|
| Default network driver | bridge | nat |
| Firewall management | iptables | HNS / Windows Firewall |
| DNS resolver in containers | 127.0.0.11 | Windows DNS client |
| Host networking mode | --network host (full) | --network host (limited support) |
| Bridge driver | Available | Not available |
| Diagnostic tool | iptables, tcpdump | Get-HNSNetwork, Event Viewer |
For a full walkthrough of Docker on Windows Server setup, see the official Windows container networking architecture docs.
Error Messages Quick Reference
| Error Message | Cause | Fix |
|---|---|---|
port is already allocated | Host port taken by another process | Find with ss -tlnp, stop process or change host port |
address already in use | Same as above | Same as above |
Name or service not known | DNS resolution failure inside container | Check resolv.conf, override DNS with --dns flag |
No such container: <name> | Container not running or wrong name | Run docker ps -a to see all containers including stopped |
network <name> not found | Network was removed or never created | Run docker network create <name> |
Failed to create endpoint | HNS error (Windows) or iptables issue | Restart Docker daemon; on Windows, reset HNS |
connection refused | Service not listening, wrong port, or wrong network | Check container is on same network; verify app binds to 0.0.0.0 not 127.0.0.1 |
i/o timeout | Firewall blocking, MTU mismatch, or wrong IP | Check iptables DOCKER-USER chain; test MTU |
driver failed programming external connectivity | iptables rules missing after flush | Restart Docker daemon |
Configuration Mistakes to Avoid
Mistake 1: Relying on container IPs instead of names
Container IPs change every restart. Don’t hardcode them.
# Wrong — this IP will change
docker run --env DB_HOST=172.17.0.3 my-app
# Right — use container names on a user-defined network
docker run --network myapp-net --env DB_HOST=db my-app
Mistake 2: App listening on 127.0.0.1 inside the container
If your app binds to 127.0.0.1 inside the container, Docker can publish the port but nothing from outside, including other containers, can reach it. This accounts for a surprising number of “Docker networking is broken” reports.
# Check what address your app is listening on inside the container
docker exec -it <container_name> ss -tlnp
# Look for 127.0.0.1:<port> vs 0.0.0.0:<port>
# The app itself needs to bind to 0.0.0.0 — this is an app config issue, not a Docker issue
Mistake 3: Setting "iptables": false in daemon.json without managing rules yourself
This disables Docker’s automatic firewall management. If you set it, you are responsible for all routing rules. Most users should leave this at the default true.
Mistake 4: Using --network host without understanding the implications
--network host removes container network isolation. The container shares the host’s network stack directly. It solves connectivity problems but bypasses Docker’s port publishing and exposes all container ports on the host. Use it deliberately, not as a debugging shortcut.
Correct daemon.json for a typical setup with DNS override and MTU adjustment:
{
"dns": ["1.1.1.1", "8.8.8.8"],
"mtu": 1450,
"log-driver": "journald"
}
After any change to daemon.json:
sudo systemctl restart docker
Getting Help: Logs and Debug Info
When you need to dig deeper or report a problem, here’s where to look:
Container logs:
docker logs <container_name>
docker logs --tail 50 --follow <container_name>
Docker daemon logs (systemd):
sudo journalctl -u docker.service --since "1 hour ago"
Full container network configuration:
docker inspect <container_name>
All iptables rules Docker has created:
sudo iptables -L -n -v
sudo iptables -t nat -L -n -v
Live packet capture on the Docker bridge:
# Capture all traffic on docker0 (install tcpdump: sudo apt install tcpdump)
sudo tcpdump -i docker0 -n
# Filter to a specific port
sudo tcpdump -i docker0 -n port 80
# Save to file for later analysis
sudo tcpdump -i docker0 -n -w /tmp/docker-capture.pcap
Community and official resources:
- Docker Networking Docs
- Docker Community Forums
- Moby GitHub Issues for suspected Docker Engine bugs
- Docker Subreddit, good for homelab and general questions
Prevention Tips
- Always use user-defined networks. Never rely on the default
bridgefor multi-container apps. Create a named network in your Compose file or run command from day one.
- Pin your port bindings explicitly. Use
0.0.0.0:<port>:<port>or127.0.0.1:<port>:<port>intentionally. Don’t let Docker decide which interface to bind to.
- Set MTU in daemon.json proactively on cloud and VPN hosts. If you’re on AWS, GCP, Azure, or any VPN-connected server, set
"mtu": 1450before you see problems.
- Use the
DOCKER-USERchain for custom firewall rules. Rules you add here survive Docker daemon restarts. Rules you add to theDOCKERchain get overwritten.
- Keep a
daemon.jsonin version control. A documented/etc/docker/daemon.jsonmeans you can reproduce your network configuration on a new host in under a minute.
- Don’t run
iptables -Fon a Docker host. If you must reset iptables, immediately follow withsudo systemctl restart docker.
- Check app bind addresses, not just Docker config. Half of “Docker networking is broken” reports turn out to be an app listening on
127.0.0.1inside the container. Verify withdocker exec -it <container> ss -tlnp.
Wrapping Up
Most Docker networking failures come down to three root causes: containers on the wrong network, iptables rules getting wiped, or an app binding to the wrong address. The diagnostic workflow is the same every time. docker network ls, docker network inspect, ss -tlnp, and tcpdump when packets disappear silently.
Make docker network inspect your first move when a container connection fails. It shows which containers are on which network and what IPs they have. That alone eliminates half the possible causes before you’ve changed anything.
Last updated: June 2026 | Applies to Docker Engine 27.x on Ubuntu 24.04 / Debian 12, with Windows Server 2019/2022 callout