When you run multiple containers — say a web application, a database, and a cache — they need to communicate with each other. Docker networking is what makes this possible. Understanding how Docker handles networking will help you build reliable multi-container systems and troubleshoot connectivity issues when they arise.
Docker supports several networking modes, each with different use cases:
When you run a container without specifying a network, it connects to the default bridge network. Containers on this network can communicate using IP addresses, but not by container names. This is a limitation of the default bridge network.
# List all Docker networks
docker network ls
# Inspect the default bridge network
docker network inspect bridge
# Run two containers and check their IPs
docker run -d --name container1 nginx
docker run -d --name container2 nginx
docker inspect container1 | grep IPAddress
User-defined bridge networks are much better than the default bridge. The key advantage: containers can communicate using container names as hostnames, not just IP addresses. IP addresses change when containers are recreated; names do not.
# Create a custom network
docker network create my-app-network
# Run containers on the custom network
docker run -d --name web --network my-app-network nginx
docker run -d --name db --network my-app-network postgres:15
# Containers can now communicate using names
# The web container can reach the database using hostname "db"
# ping db ← works inside the web container
By default, containers are isolated from the outside world. To access a container's service from your browser or from other machines, you map a host port to a container port.
# Map host port 8080 to container port 80
docker run -d -p 8080:80 nginx
# Map to a specific interface
docker run -d -p 127.0.0.1:8080:80 nginx # localhost only
# Map multiple ports
docker run -d -p 8080:80 -p 443:443 my-web-app
# Publish all exposed ports (random host ports)
docker run -d -P nginx
The format is always host_port:container_port. Remember this order: left side is your machine, right side is inside the container.
Docker has a built-in DNS server that resolves container names to IP addresses within custom networks. This is transparent — you do not need to configure anything. When container A wants to reach container B on the same user-defined network, it simply uses B's name as the hostname.
# Enter the web container
docker exec -it web bash
# Test DNS resolution (inside the container)
ping db
nslookup db
curl http://db:5432 # reach the database by name
# Create network
docker network create webapp-net
# Start database
docker run -d --name postgres-db --network webapp-net -e POSTGRES_PASSWORD=dbpass -e POSTGRES_DB=appdb postgres:15
# Start web application (it connects to postgres-db by name)
docker run -d --name flask-app --network webapp-net -p 5000:5000 -e DATABASE_URL="postgresql://postgres:dbpass@postgres-db:5432/appdb" my-flask-app:1.0
The Flask app connects to the database using postgres-db as the hostname. Docker resolves this name to the container's IP automatically.
# Check container network settings
docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' my-container
# Check which ports a container has open
docker port my-container
# Check network details
docker network inspect my-app-network
In Part 6, we will learn Docker Compose — the tool that lets you define and run multi-container applications with a single file and a single command.
When containers are on the same user-defined network, Docker provides automatic DNS resolution — containers can reach each other by their container names. This is a critical feature for multi-container applications. On the default bridge network, DNS resolution by container name does not work (you must use IP addresses). On user-defined networks, it works automatically:
# Create a network
docker network create myapp_network
# Start backend container on the network
docker run -d --name api_server --network myapp_network my_api_image
# Start frontend — can reach backend by name "api_server"
docker run -d --name frontend --network myapp_network -e API_URL=http://api_server:8080 my_frontend_image
# Verify DNS resolution from inside a container
docker exec frontend ping api_server
This is why Docker Compose and Kubernetes use service names as hostnames — the underlying mechanism is container DNS on isolated networks. Understanding this helps you configure multi-container applications correctly and debug connectivity issues between containers.
Use docker network inspect network_name to see detailed network configuration including which containers are attached and their IP addresses. Use docker exec container_name ip addr to see a container's network interfaces from inside. If containers on the same network cannot communicate, check that they are actually on the same network with docker inspect container_name | grep -A 20 Networks. For debugging network issues inside containers, install tools temporarily: docker exec -it container bash then apt-get install -y curl ping.
Create a two-container application: a Python Flask API that responds to HTTP requests, and a second container that periodically calls the API using curl. Put both on a user-defined network and verify the second container can reach the API using the container name as hostname. Then disconnect one container from the network using docker network disconnect and verify the connection breaks, then reconnect with docker network connect.