Container Communication
Building multi-container applications requires understanding how containers communicate. This lesson covers service discovery, networking patterns, and troubleshooting communication issues.
Service Discovery
Containers need to find and connect to each other. Docker provides built-in service discovery on custom networks.
DNS-Based Discovery
# Create network
docker network create myapp
# Start services
docker run -d --name postgres --network myapp postgres:15
docker run -d --name redis --network myapp redis:alpine
docker run -d --name api --network myapp myapi
# Containers can reach each other by name
docker exec api ping postgres
# PING postgres (172.18.0.2): 56 data bytes
docker exec api ping redis
# PING redis (172.18.0.3): 56 data bytes
How DNS Works
┌────────────────────────────────────────────────────────────────┐
│ myapp network │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Docker DNS Server │ │
│ │ postgres → 172.18.0.2 │ │
│ │ redis → 172.18.0.3 │ │
│ │ api → 172.18.0.4 │ │
│ └──────────────────────────────────────────────────────────┘ │
│ ▲ ▲ ▲ │
│ │ │ │ │
│ ┌────┴────┐ ┌─────┴────┐ ┌────┴────┐ │
│ │postgres │ │ redis │ │ api │ │
│ │172.18.0.2│ │172.18.0.3│ │172.18.0.4│ │
│ └─────────┘ └──────────┘ └─────────┘ │
└────────────────────────────────────────────────────────────────┘
Network Aliases
Assign additional DNS names to containers:
# Single alias
docker run -d \
--name postgres \
--network myapp \
--network-alias db \
postgres:15
# Multiple aliases
docker run -d \
--name postgres \
--network myapp \
--network-alias db \
--network-alias database \
--network-alias primary \
postgres:15
# All names resolve to the container
docker exec api nslookup db
docker exec api nslookup database
docker exec api nslookup primary
Load Balancing with Aliases
Multiple containers can share an alias:
# Start multiple API instances with same alias
docker run -d --name api1 --network myapp --network-alias api myapi
docker run -d --name api2 --network myapp --network-alias api myapi
docker run -d --name api3 --network myapp --network-alias api myapi
# DNS returns all IPs (round-robin)
docker exec web nslookup api
# Name: api
# Address 1: 172.18.0.4
# Address 2: 172.18.0.5
# Address 3: 172.18.0.6
Connection Strings
Use container names in connection strings:
# PostgreSQL
docker run -d \
--name api \
--network myapp \
-e DATABASE_URL="postgresql://user:pass@postgres:5432/mydb" \
myapi
# Redis
docker run -d \
--name api \
--network myapp \
-e REDIS_URL="redis://redis:6379" \
myapi
# MongoDB
docker run -d \
--name api \
--network myapp \
-e MONGO_URL="mongodb://mongo:27017/mydb" \
myapi
Multi-Tier Architecture
Implement network isolation for security:
# Create tier networks
docker network create frontend
docker network create backend
docker network create database
# Database tier - most isolated
docker run -d \
--name postgres \
--network database \
postgres:15
# Backend tier - connects to database
docker run -d \
--name api \
--network backend \
--network database \
myapi
# API can reach postgres, but postgres can't initiate to API
# Frontend tier - connects to backend
docker run -d \
--name web \
--network frontend \
--network backend \
-p 80:80 \
nginx
# Web can reach API, but not database directly
┌──────────────────────────────────────────────────────────────────┐
│ │
│ Internet │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Frontend Network │ │
│ │ ┌──────────┐ │ │
│ │ │ Web │ ──────────────┐ │ │
│ │ │ :80 │ │ │ │
│ │ └──────────┘ │ │ │
│ └─────────────────────────────│───────────────────────────────┘ │
│ │ │
│ ┌─────────────────────────────│───────────────────────────────┐ │
│ │ Backend Network ▼ │ │
│ │ ┌──────────┐ │ │
│ │ │ API │ ────────┐ │ │
│ │ │ :3000 │ │ │ │
│ │ └──────────┘ │ │ │
│ └────────────────────────────────────────────│────────────────┘ │
│ │ │
│ ┌────────────────────────────────────────────│────────────────┐ │
│ │ Database Network ▼ │ │
│ │ ┌──────────┐ │ │
│ │ │ Postgres │ │ │
│ │ │ :5432 │ │ │
│ │ └──────────┘ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────┘
Link (Legacy)
The --link flag is deprecated but you may see it in older documentation:
# Legacy approach (don't use)
docker run -d --name db postgres
docker run -d --name api --link db:database myapi
# Modern approach (use this)
docker network create myapp
docker run -d --name db --network myapp postgres
docker run -d --name api --network myapp myapi
Container-to-Host Communication
Sometimes containers need to reach services on the host:
Using host.docker.internal
# Available on Docker Desktop (Mac/Windows)
docker run -d \
-e API_URL="http://host.docker.internal:3000" \
myapp
# From inside container
curl http://host.docker.internal:3000
Using Host Network Mode
# Linux: container shares host network
docker run --network host myapp
# localhost inside container = host's localhost
Using Host IP
# Get host IP
HOST_IP=$(hostname -I | awk '{print $1}')
# Pass to container
docker run -e HOST_IP=$HOST_IP myapp
Health Checks for Dependencies
Wait for dependencies to be ready:
# Wait for database to be ready
docker run -d \
--name api \
--network myapp \
myapi sh -c '
while ! nc -z postgres 5432; do
echo "Waiting for database..."
sleep 1
done
node server.js
'
Using wait-for-it
# In Dockerfile
COPY wait-for-it.sh /wait-for-it.sh
RUN chmod +x /wait-for-it.sh
CMD ["/wait-for-it.sh", "postgres:5432", "--", "node", "server.js"]
Debugging Network Issues
Check DNS Resolution
# From inside container
docker exec api nslookup postgres
# Should return IP address
# If fails, check network connection
Test Connectivity
# Ping (if installed)
docker exec api ping -c 3 postgres
# TCP connection test
docker exec api nc -zv postgres 5432
# HTTP request
docker exec api curl http://web:80
View Network Configuration
# Container's network settings
docker inspect api --format '{{json .NetworkSettings.Networks}}' | jq
# All containers on a network
docker network inspect myapp --format '{{range .Containers}}{{.Name}}: {{.IPv4Address}}{{"\n"}}{{end}}'
Common Issues
Container not on same network:
# Check container's networks
docker inspect api --format '{{range $net, $conf := .NetworkSettings.Networks}}{{$net}} {{end}}'
# Add to correct network
docker network connect myapp api
DNS not resolving:
# Default bridge doesn't have DNS
# Move to custom network
docker network create myapp
docker network connect myapp api
docker network connect myapp postgres
Port not accessible:
# Check if service is running
docker exec postgres pg_isready
# Check if listening on correct interface
docker exec postgres netstat -tlnp
Key Takeaways
- Custom networks provide automatic DNS resolution
- Use container names as hostnames in connection strings
- Network aliases enable multiple DNS names and simple load balancing
- Multi-network architecture provides security isolation
- Use
host.docker.internalfor container-to-host communication - Implement health checks for reliable startup ordering
- Debug with nslookup, ping, nc, and curl from inside containers
- Prefer custom networks over the default bridge

