Overview and What You Will Learn
When a payment-api container needs to connect to a postgres container, how does it find it? In production you cannot hardcode IP addresses — they change every time a container restarts. Docker's embedded DNS server solves this by letting containers find each other by name, automatically, without any configuration.
This guide explains exactly how that works, why it only works on user-defined networks (not the default bridge), and how Docker Compose makes service discovery even simpler.
Core Principles
+------------------------------------------+| Default Bridge Network || No DNS — containers use IP addresses || If container restarts, IP changes || Cannot find containers by name |+------------------------------------------+ +------------------------------------------+| User-Defined Bridge Network || Embedded DNS at 127.0.0.11 || Containers resolve each other by name || Uses container name as hostname || Survives container restarts |+------------------------------------------+ +------------------------------------------+| Docker Compose Network || Auto-created user-defined network || Service name = DNS hostname || All services in compose file find || each other by service name |+------------------------------------------+Detailed Step-by-Step Practical Lab
Milestone 1: How Docker DNS Works
# Create a user-defined networkdocker network create app-network # Run two containers on itdocker run -d --name api --network app-network node:20-alpine sleep 3600docker run -d --name db --network app-network postgres:15 \ -e POSTGRES_PASSWORD=secret # Inside the api container, check DNS configdocker exec api cat /etc/resolv.conf# nameserver 127.0.0.11 <- Docker's embedded DNS server# options ndots:0 # Resolve db by namedocker exec api nslookup db# Server: 127.0.0.11# Address: 127.0.0.11:53# Name: db# Address: 172.18.0.3 <- db container's IP # db is restarted — it gets a new IPdocker restart dbdocker exec api nslookup db# Name: db# Address: 172.18.0.4 <- new IP, but DNS still works!# The DNS record updated automatically # Full DNS name inside the networkdocker exec api nslookup db.app-network# Also resolves — full form is: container-name.network-nameMilestone 2: Docker Compose Service Discovery
Docker Compose creates a user-defined network automatically and uses the service name as the DNS hostname:
# docker-compose.ymlversion: "3.8" services: payment-api: # <- This becomes the DNS hostname "payment-api" image: registry.razorpay.in/payment-api:v3.1.0 environment: # Use service name as hostname — no IP addresses needed DB_HOST: postgres REDIS_HOST: redis QUEUE_HOST: rabbitmq postgres: # <- DNS hostname "postgres" image: postgres:15 environment: POSTGRES_PASSWORD: secret redis: # <- DNS hostname "redis" image: redis:7-alpine rabbitmq: # <- DNS hostname "rabbitmq" image: rabbitmq:3-managementdocker compose up -d # payment-api can reach postgres by namedocker compose exec payment-api ping postgres# PING postgres (172.20.0.3): 56 bytes sent docker compose exec payment-api curl http://rabbitmq:15672# Reaches RabbitMQ management UI # Check the auto-created networkdocker network ls# projectname_default <- created by composedocker network inspect projectname_default# Shows all four services connectedMilestone 3: Network Aliases
Network aliases give a container additional hostnames on a network:
# Create container with an aliasdocker run -d \ --name postgres-primary \ --network app-network \ --network-alias db \ --network-alias database \ postgres:15 -e POSTGRES_PASSWORD=secret # Now reachable by three names:# postgres-primary (container name)# db (alias)# database (alias) docker exec api ping postgres-primary # worksdocker exec api ping db # worksdocker exec api ping database # works # Aliases in Docker Compose# docker-compose.ymlservices: postgres-primary: image: postgres:15 networks: app-network: aliases: - db - databaseMilestone 4: Cross-Compose Service Discovery
When you have multiple Compose files and need services to communicate:
# Create a shared external networkdocker network create shared-network # In compose file 1 (payment service)# docker-compose.payment.ymlservices: payment-api: networks: - shared-network networks: shared-network: external: true # Use existing network, don't create # In compose file 2 (database service)# docker-compose.db.ymlservices: postgres: networks: - shared-network networks: shared-network: external: true # Now payment-api can reach postgres by name across compose filesHow This Prepares You for Kubernetes
Docker's DNS model maps directly to Kubernetes:
Docker Kubernetes127.0.0.11 (embedded DNS) -> CoreDNS (kube-dns service)container-name -> pod name (usually not used directly)service-name in compose -> Service object namenetwork-name -> namespaceservice.network -> service.namespace.svc.cluster.localUnderstanding Docker DNS makes Kubernetes CoreDNS immediately intuitive — the concepts are identical, just at a larger scale.
PLACEMENT PRO TIP**Tip:** When container DNS resolution fails, always check `cat /etc/resolv.conf` inside the container first. It should show `nameserver 127.0.0.11`. If it shows `nameserver 8.8.8.8` or your host's DNS server, the container is on the default bridge network which has no Docker DNS.
REMEMBER THIS**Remember:** Container names on user-defined networks resolve as DNS names. Service names in Docker Compose also resolve. But only on user-defined networks — the default bridge network has no DNS and containers can only reach each other by IP.
{ "title": "Debugging Docker Networking — Common Failures and Diagnostic Commands", "slug": "docker-networking-debugging", "cluster": "docker", "description": "Diagnose and fix the most common Docker networking failures — containers that cannot reach each other, port conflicts, DNS failures, and network misconfiguration.", "primaryKeyword": "docker networking debugging"}Debugging Docker Networking — Common Failures and Diagnostic Commands
Overview and What You Will Learn
Docker networking failures have four common root causes: containers on different networks, DNS resolution failures, port conflicts on the host, and iptables rule conflicts. Knowing which diagnostic command reveals which root cause turns a 30-minute debugging session into a 2-minute one.
Core Principles
Networking problem decision tree: Container A cannot reach Container B | Is Container A on the same network as B? docker inspect --format networks | NO: Connect to same network YES: Continue | Does hostname resolve? docker exec A nslookup B | NO: DNS issue — are both on user-defined network? YES: Continue | Is the target port open? docker exec A nc -zv B PORT | NO: Application not listening on that port YES: iptables or firewall blockingDetailed Diagnostic Commands
Cannot Reach Another Container
# Step 1: Check which networks each container is ondocker inspect api --format '{{json .NetworkSettings.Networks}}' | jq 'keys'docker inspect db --format '{{json .NetworkSettings.Networks}}' | jq 'keys'# If different -> connect them: docker network connect networkname db # Step 2: Check if DNS resolves the hostnamedocker exec api nslookup db# If fails -> containers are on default bridge (no DNS)# Fix: create user-defined network and connect both # Step 3: Test port connectivitydocker exec api nc -zv db 5432# Connection to db 5432 port [tcp/postgresql] succeeded!# If fails -> application not listening or firewall blocking # Step 4: Test from inside the containerdocker exec -it api shcurl -v http://db:8080/health# Verbose curl shows exactly where the connection failsPort Already in Use
# Error: "Bind for 0.0.0.0:8080 failed: port is already allocated" # Find what is using port 8080ss -tulpn | grep :8080# tcp LISTEN 0 128 0.0.0.0:8080 users:(("node",pid=1234,fd=18)) # Or check if a stopped Docker container has it reserveddocker ps -a --format "table {{.Names}}\t{{.Ports}}" | grep 8080 # Kill the process or remove the stopped containerdocker rm container-using-8080# Then try againCannot Reach External Internet
# Container cannot reach the internetdocker exec api curl https://api.stripe.com # Check DNS resolution for external hostnamedocker exec api nslookup api.stripe.com# Should resolve to Stripe's IPs # Check if the host can reach the internetcurl https://api.stripe.com # Check Docker's iptables FORWARD rulessudo iptables -L FORWARD | grep REJECT# If there are REJECT rules blocking forward traffic, Docker cannot NAT out # Restart Docker (rebuilds iptables rules)sudo systemctl restart docker# This fixes many iptables corruption issuesInspect Network Configuration
# Full network inspectiondocker network inspect payment-network # Shows:# - Subnet: 172.18.0.0/16# - Gateway: 172.18.0.1# - Containers: {name, IPv4, MAC}# - Options, Labels # Check a container's full network configdocker inspect payment-api --format '{{json .NetworkSettings}}' | jq # Run a debug container on the same network to testdocker run --rm -it \ --network payment-network \ nicolaka/netshoot \ bash# netshoot has: tcpdump, curl, dig, nslookup, netstat, iperf, traceroute# Perfect debugging toolkit for container networking # From netshoot container:dig db # DNS lookuptcpdump -i eth0 port 5432 # Capture database traffictraceroute db # Network path to dbiperf3 -c db # Bandwidth testCommon Networking Failures
| Failure | Symptom | Root Cause | Fix |
|---|---|---|---|
| Container not reachable by name | nslookup fails |
Default bridge network (no DNS) | Create user-defined network |
| Container not reachable at all | Connection refused |
Different networks | docker network connect |
| Port conflict | port already allocated |
Another process using the host port | Kill the process or change the host port |
| No internet from container | curl fails to external URL |
iptables FORWARD rules broken | systemctl restart docker |
| Intermittent connectivity | Sometimes works, sometimes fails | Container restarting (new IP on default bridge) | Move to user-defined network |
PLACEMENT PRO TIP**Tip:** Keep the `nicolaka/netshoot` image pulled on your development machine (`docker pull nicolaka/netshoot`). It is a networking Swiss Army knife container — run it on any Docker network to debug connectivity issues from inside the network, with every networking tool you need already installed.
COMMON MISTAKE / WARNING**Common Mistake:** Restarting containers when networking fails without diagnosing the root cause first. If the problem is that two containers are on different networks, restarting them does not fix it. Use `docker inspect --format networks` to verify network membership before anything else.