Overview
Every Docker container runs in its own network namespace by default, connected to a virtual network managed by Docker. The driver you choose for that network, bridge, host, overlay, or none, determines how containers reach each other, how they reach the outside world, and how the outside world reaches them. Most confusion comes from conflating two different questions: "can containers talk to each other" (governed by which Docker network they share) and "can the host or outside world reach a container" (governed by published ports).
Quick Reference
| Driver | Scope | Use case |
|---|---|---|
| bridge (user-defined) | Single host | Default for most multi-container apps; gives DNS-based container name resolution |
| bridge (default) | Single host | Legacy default network; no automatic DNS between containers |
| host | Single host | Shares the host's network stack directly; no isolation, no port mapping needed |
| overlay | Multi-host (Swarm/K8s-adjacent) | Container-to-container networking across multiple Docker hosts |
| none | N/A | Fully isolates a container from all networking |
Syntax
# Create a user-defined bridge network
docker network create app-net
# Run containers attached to it; they can resolve each other by name
docker run -d --name api --network app-net my-api-image
docker run -d --name db --network app-net postgres:16
# Publish a port so the host (and outside world) can reach the container
docker run -d --name api -p 8080:80 --network app-net my-api-imageExamples
# docker-compose.yml - services on the same compose network resolve
# each other by service name automatically; no manual network needed.
services:
api:
build: .
ports:
- '8080:80' # host:container - only needed for host/external access
depends_on:
- db
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: example// Inside the `api` container, `db` resolves via Docker's embedded DNS -
// no hardcoded IP address needed.
const client = new Client({ host: 'db', port: 5432 })Visual Diagram
Common Mistakes
- Using the legacy default bridge network and expecting containers to resolve each other by name, only user-defined bridge networks get Docker's embedded DNS.
- Publishing a port with
-ppurely to let sibling containers talk to each other, unnecessary; they only need to share a network. - Assuming
hostnetworking is "just faster" without weighing the loss of network isolation and the risk of port collisions with the host itself. - Forgetting that containers on different Docker networks cannot reach each other at all unless explicitly connected to a shared network.
Performance
- Bridge networking adds a small amount of NAT/virtual-interface overhead per packet compared to host networking, usually negligible for typical web workloads, but measurable for very high-throughput, low-latency services.
- Overlay networks (multi-host) add encapsulation overhead (VXLAN by default) on top of bridge networking, since traffic has to be tunneled between hosts.
- The
nonedriver has zero networking overhead by definition, useful for batch/offline jobs that need no network access at all.
Best Practices
- Always use a user-defined bridge network (or Compose's default per-project network) instead of the legacy default bridge, to get name-based service discovery.
- Only publish ports that genuinely need to be reachable from the host or the internet, internal service-to-service traffic does not need it.
- Reach for
hostnetworking only when its specific performance characteristics are required and the isolation trade-off is acceptable. - Use overlay networks (or a Kubernetes CNI) when containers need to communicate across multiple physical or virtual hosts.