Skip to main content

Docker DNS and Service Discovery — How Containers Find Each Other

Understand Docker's embedded DNS server, how containers resolve each other by name inside user-defined networks, and how this maps to Kubernetes CoreDNS.

44 Terms

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

◈ DIAGRAM
+------------------------------------------+
| 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
Bash
docker network create app-network
# Run two containers on it
docker run -d --name api --network app-network node:20-alpine sleep 3600
docker run -d --name db --network app-network postgres:15 \
-e POSTGRES_PASSWORD=secret
# Inside the api container, check DNS config
docker exec api cat /etc/resolv.conf
# nameserver 127.0.0.11 <- Docker's embedded DNS server
# options ndots:0
# Resolve db by name
docker 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 IP
docker restart db
docker 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 network
docker exec api nslookup db.app-network
# Also resolves — full form is: container-name.network-name
Milestone 2: Docker Compose Service Discovery

Docker Compose creates a user-defined network automatically and uses the service name as the DNS hostname:

YAML
# docker-compose.yml
version: "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-management
Bash
docker compose up -d
# payment-api can reach postgres by name
docker 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 network
docker network ls
# projectname_default <- created by compose
docker network inspect projectname_default
# Shows all four services connected
Milestone 3: Network Aliases

Network aliases give a container additional hostnames on a network:

Bash
# Create container with an alias
docker 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 # works
docker exec api ping db # works
docker exec api ping database # works
# Aliases in Docker Compose
# docker-compose.yml
services:
postgres-primary:
image: postgres:15
networks:
app-network:
aliases:
- db
- database
Milestone 4: Cross-Compose Service Discovery

When you have multiple Compose files and need services to communicate:

Bash
# Create a shared external network
docker network create shared-network
# In compose file 1 (payment service)
# docker-compose.payment.yml
services:
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.yml
services:
postgres:
networks:
- shared-network
networks:
shared-network:
external: true
# Now payment-api can reach postgres by name across compose files

How This Prepares You for Kubernetes

Docker's DNS model maps directly to Kubernetes:

◈ DIAGRAM
Docker Kubernetes
127.0.0.11 (embedded DNS) -> CoreDNS (kube-dns service)
container-name -> pod name (usually not used directly)
service-name in compose -> Service object name
network-name -> namespace
service.network -> service.namespace.svc.cluster.local

Understanding Docker DNS makes Kubernetes CoreDNS immediately intuitive — the concepts are identical, just at a larger scale.

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

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.


JSON
{
"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

Bash
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 blocking

Detailed Diagnostic Commands

Cannot Reach Another Container
Bash
# Step 1: Check which networks each container is on
docker 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 hostname
docker 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 connectivity
docker 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 container
docker exec -it api sh
curl -v http://db:8080/health
# Verbose curl shows exactly where the connection fails
Port Already in Use
Bash
# Error: "Bind for 0.0.0.0:8080 failed: port is already allocated"
# Find what is using port 8080
ss -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 reserved
docker ps -a --format "table {{.Names}}\t{{.Ports}}" | grep 8080
# Kill the process or remove the stopped container
docker rm container-using-8080
# Then try again
Cannot Reach External Internet
Bash
# Container cannot reach the internet
docker exec api curl https://api.stripe.com
# Check DNS resolution for external hostname
docker exec api nslookup api.stripe.com
# Should resolve to Stripe's IPs
# Check if the host can reach the internet
curl https://api.stripe.com
# Check Docker's iptables FORWARD rules
sudo 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 issues
Inspect Network Configuration
Bash
# Full network inspection
docker 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 config
docker inspect payment-api --format '{{json .NetworkSettings}}' | jq
# Run a debug container on the same network to test
docker 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 lookup
tcpdump -i eth0 port 5432 # Capture database traffic
traceroute db # Network path to db
iperf3 -c db # Bandwidth test

Common 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
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

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.

Resources

Explore More in Docker Networking and Storage

All 3 Topics

Frequently Asked Questions

Is Docker DNS and Service Discovery — How Containers Find Each Other free to learn on DevOps Network?

Yes - this topic, like everything on DevOps Network, is 100% free with no paywall or sign-up gate.

What does the Docker DNS and Service Discovery — How Containers Find Each Other topic cover?

Understand Docker's embedded DNS server, how containers resolve each other by name inside user-defined networks, and how this maps to Kubernetes CoreDNS.