Problem: Containers Can't Talk to Each Other
Your Docker containers work individually but can't communicate. You see connection refused, DNS resolution fails, or services timeout when calling each other.
You'll learn:
- Why Docker networking breaks between containers
- How to diagnose network connectivity issues
- When to use bridge vs custom networks
Time: 12 min | Level: Intermediate
Why This Happens
Docker's default bridge network doesn't provide automatic DNS resolution between containers. When you reference containers by name, Docker can't resolve them unless they're on a custom network.
Common symptoms:
curl: (6) Could not resolve host: api-containerconnection refusedwhen using container names- Works with IP addresses but not names
- Port mappings conflict between containers
Solution
Step 1: Verify Current Network Setup
# List all networks
docker network ls
# Inspect your containers' networks
docker inspect <container-name> | grep NetworkMode
Expected: You'll likely see "NetworkMode": "default" or "NetworkMode": "bridge"
Why this matters: The default bridge doesn't support container name DNS resolution.
Step 2: Check Container Connectivity
# Get container IP addresses
docker inspect -f '{{.Name}} - {{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $(docker ps -q)
# Test connection from one container to another
docker exec <source-container> ping <target-container>
If ping fails with "Name or service not known": DNS resolution is broken (expected on default bridge).
If ping fails with "Destination Host Unreachable": Network isolation issue.
Step 3: Create a Custom Network
# Create a user-defined bridge network
docker network create app-network
# Connect existing containers
docker network connect app-network <container-name>
# OR restart containers on the new network
docker run -d --name api --network app-network your-api-image
docker run -d --name web --network app-network your-web-image
Why this works: Custom networks provide automatic DNS resolution. Containers can now reach each other using their names.
Step 4: Update Service References
# docker-compose.yml
version: '3.8'
services:
api:
image: your-api
networks:
- app-network
ports:
- "3000:3000"
web:
image: your-web
environment:
# Use service name, not localhost
API_URL: http://api:3000
networks:
- app-network
depends_on:
- api
networks:
app-network:
driver: bridge
Critical change: Use service names (api:3000) not localhost:3000 when containers communicate.
Step 5: Handle Port Conflicts
# Find what's using a port
sudo lsof -i :3000
# or on Linux
sudo netstat -tulpn | grep 3000
# Change host port mapping (keep container port same)
docker run -p 3001:3000 your-image # Host:3001 → Container:3000
If services still can't connect:
- Error: "bind: address already in use" → Change the host port (first number in
-p) - Connection refused inside containers → Don't use published ports internally, use container ports directly
Verification
# Test DNS resolution between containers
docker exec web ping api
# Test HTTP connectivity
docker exec web curl http://api:3000/health
# Check network members
docker network inspect app-network
You should see:
- Successful pings between containers
- HTTP responses without connection errors
- All your services listed in network members
Common Troubleshooting Patterns
Pattern 1: "Connection Refused" on localhost
# ⌠Wrong: Using localhost inside containers
curl http://localhost:3000
# ✅ Right: Using service name
curl http://api:3000
Why: localhost inside a container refers to that container, not the host or other containers.
Pattern 2: Works in Development, Breaks in Production
# ⌠Wrong: Hardcoded localhost
environment:
DATABASE_URL: postgresql://localhost:5432/db
# ✅ Right: Service name
environment:
DATABASE_URL: postgresql://postgres:5432/db
Why: Production containers are isolated. Use service names for portability.
Pattern 3: Port Conflicts on Host
# ⌠Wrong: Same host port for multiple services
services:
api-v1:
ports: ["3000:3000"]
api-v2:
ports: ["3000:3000"] # Conflict!
# ✅ Right: Different host ports
services:
api-v1:
ports: ["3001:3000"]
api-v2:
ports: ["3002:3000"]
Inter-container communication: Containers on the same network still use port 3000 internally.
Advanced Debugging
Check Network Traffic
# Install network tools in container
docker exec -it <container> sh
apk add --no-cache tcpdump curl bind-tools
# Test DNS resolution
nslookup api
# Trace route to another container
traceroute api
# Capture packets
tcpdump -i eth0 port 3000
Inspect Network Isolation
# See which networks a container is on
docker inspect <container> -f '{{range $k, $v := .NetworkSettings.Networks}}{{$k}} {{end}}'
# Check network driver
docker network inspect app-network -f '{{.Driver}}'
If containers are on different networks: They can't communicate unless you connect both to a shared network.
What You Learned
- Default bridge networks don't support DNS between containers
- Custom networks provide automatic service discovery
- Use service names, not localhost, for inter-container communication
- Host port mappings are for external access, not internal communication
Limitations:
- This applies to bridge networks. Host network mode bypasses isolation entirely
- Swarm mode and Kubernetes have different networking models
Quick Reference
# Create network
docker network create my-network
# Run container on network
docker run --network my-network --name api my-image
# Connect existing container
docker network connect my-network existing-container
# Debug DNS
docker exec container nslookup other-container
# Check connectivity
docker exec container curl http://other-container:port
Tested on Docker 25.x, Docker Compose v2.24, Ubuntu 24.04 & macOS Sonoma