Networking Basics
Understand Docker's networking model including bridge networks, port publishing, container DNS, and how containers communicate with each other.
Docker Network Types
Docker ships with several built-in network drivers, each suited to a different scenario:
| Driver | What It Does | Best For |
|---|---|---|
bridge | Private internal network on the host | Default for standalone containers |
host | Shares the host's network directly | Maximum performance, no isolation |
none | No networking at all | Air-gapped containers |
overlay | Multi-host networking | Docker Swarm / Kubernetes |
The Default Bridge Network
When you run a container without specifying a network, it automatically joins Docker's built-in bridge network:
docker run -d nginx
docker inspect bridge # See all containers on the default bridge
Containers on the default bridge can reach the internet and each other, but only by IP address. There's no automatic DNS, which means you'd need to hardcode IP addresses to connect containers together. That's fragile and error-prone. That's why user-defined networks exist.
User-Defined Bridge Networks
Creating your own network unlocks automatic DNS between containers, and they can find each other by name instead of IP:
# Create a network
docker network create my-app-network
# Run containers on it
docker run -d --name web --network my-app-network nginx
docker run -d --name db --network my-app-network postgres:16
docker run -d --name cache --network my-app-network redis:7
Now from inside the web container, you can reach the others by their container name:
docker exec -it web curl http://cache:6379 # Redis by name
docker exec -it web psql -h db -U postgres # Postgres by name
Docker's embedded DNS server handles name resolution automatically. No IP addresses needed, no manual config. This is why user-defined networks are always recommended over the default bridge.
Port Publishing
Containers are isolated from your host machine by default. To make a service accessible from your browser, another machine, or a CI pipeline, you need to publish a port. The format is -p HOST_PORT:CONTAINER_PORT.
# Map host port 8080 to container port 80
docker run -d -p 8080:80 nginx
# Bind to localhost only (more secure as it is not accessible from other machines)
docker run -d -p 127.0.0.1:5432:5432 postgres:16
# Publish multiple ports
docker run -d -p 80:80 -p 443:443 nginx
# Let Docker pick a random available host port
docker run -d -p 80 nginx
docker port <container-id> # Check what port was assigned
A key rule: Only publish ports that need to be reached from outside. Container-to-container traffic on the same network works without any port publishing at all.
# db is internal only so no -p needed
docker run -d --name db --network app-net postgres:16
# web is public
docker run -d --name web --network app-net -p 80:3000 myapp
Managing Networks
# List all networks
docker network ls
# Inspect a network (see connected containers, subnet, gateway)
docker network inspect my-app-network
# See which network(s) a container is on
docker inspect my-container --format='{{json .NetworkSettings.Networks}}' | python3 -m json.tool
# Connect a running container to a network (no restart needed)
docker network connect my-app-network my-container
# Disconnect a container from a network
docker network disconnect my-app-network my-container
# Connect with an alias (extra DNS name)
docker network connect --alias api my-app-network backend-v2
A container can belong to multiple networks at the same time, it is useful when you need one container to bridge two separate networks.
EXPOSE vs -p
These two things sound similar but do very different things:
EXPOSE in Dockerfile | -p in docker run | |
|---|---|---|
| What it does | Documents which port the app uses | Actually opens the port to the host |
| Reachable from host? | No | Yes |
| Required for container-to-container? | No | No |
EXPOSE is just metadata, a note to developers (and tools like Docker Compose) saying "this container listens on this port." It doesn't open anything. Only -p actually publishes a port.
Host Networking
With --network host, the container skips network isolation entirely and shares the host's network stack directly. The upside is maximum throughput with no NAT overhead. The downside is zero isolation, the container can bind to any host port, which means port conflicts become your problem. Use it sparingly, typically only for performance-critical tools or network monitoring utilities.
docker run -d --network host nginx
# nginx now listens on host port 80, so no -p needed
Custom DNS Options
Within a user-defined network, DNS works automatically. But you can also customize it:
# Use a specific DNS server
docker run --dns 8.8.8.8 --dns 8.8.4.4 my-app
# Add a custom /etc/hosts entry
docker run --add-host db.internal:192.168.1.50 my-app
# Set a DNS search domain
docker run --dns-search company.internal my-app
Quick Reference
| Command | What It Does |
|---|---|
docker network create <name> | Create a new bridge network |
docker network ls | List all networks |
docker network inspect <name> | See containers and config |
docker network connect <net> <c> | Add a container to a network |
docker network disconnect <net> <c> | Remove a container from a network |
docker run -p 8080:80 | Publish port 80 to host port 8080 |
docker run --network host | Use host networking (no isolation) |
Key Takeaways
- Default bridge network has no DNS: containers can only reach each other by IP, which is fragile; use user-defined networks instead
- User-defined bridge networks give you automatic DNS: containers find each other by name, no IP addresses needed
- Only publish ports that need external access: container-to-container traffic on the same network is already routable without
-p EXPOSEin a Dockerfile is documentation only: it doesn't open any ports;-pindocker runis what actually publishes--network hostremoves all isolation: useful for performance-critical tools, but avoid it for regular application containers- One container can be on multiple networks: handy when you need a service to bridge two isolated environments
Frequently Asked Questions
Why can't my two containers talk to each other even though they're both running?
They're probably both on the default bridge network, where containers can only communicate by IP address, not by name. Create a user-defined network with docker network create, add both containers to it with --network, and they'll be able to reach each other by container name automatically.
What's the difference between -p 8080:80 and -p 80?
-p 8080:80 maps host port 8080 to container port 80, and you choose the host port. -p 80 tells Docker to map container port 80 to a random available port on the host. Use docker port <container> to see what port was assigned.
Do I need to publish a port for containers on the same network to communicate?
No. Containers on the same user-defined network can reach each other freely without any port publishing. Port publishing is only needed to make a service accessible from outside Docker which means from your browser, another machine, or the host itself.
What does EXPOSE actually do if it doesn't open ports?
It's documentation. It tells anyone reading the Dockerfile what port the application listens on. Some tools (like Docker Compose and Docker Desktop) use it to suggest port mappings. But it has zero effect on actual network accessibility, only -p does that.
Can a container be on more than one network?
Yes. You can connect a running container to additional networks with docker network connect. This is useful when you have a gateway or proxy container that needs to reach services on two separate isolated networks.
What's the difference between bridge and host networking?
Bridge networking gives each container its own isolated network namespace. It has its own IP, and you control what's exposed via port publishing. Host networking removes that isolation completely; the container shares the host's network interfaces and ports directly. Bridge is safer and more flexible; host is faster but requires careful port management.