Every container networking question — "why can't these two containers talk," "why is this port not reachable" — traces back to which network driver is actually in play.
Bridge: the default, and the one you'll use most
Bridge networking creates a private, isolated virtual network that containers attach to — each container gets its own IP on that network, and Docker handles routing between them and out to the internet.
docker network create my-app-network
docker run -d --name db --network my-app-network postgres:16
docker run -d --name app --network my-app-network -p 3000:3000 my-appContainers on the same user-defined bridge network can reach each other by container name (db, app) via Docker's built-in DNS — this is exactly the mechanism Docker Compose relies on automatically, without you creating the network by hand.
Host: no isolation, direct access
Host mode removes network isolation entirely — the container shares the host machine's network stack directly, with no virtual network or port mapping in between.
docker run --network host nginx
# nginx binds directly to the host's port 80 — no -p needed or possibleThe tradeoff: no port mapping is needed (whatever the container binds to is directly reachable on the host), but there's also no isolation — a container in host mode can bind to any port the host itself could, and two containers using host mode can't both bind the same port. This mode is Linux-specific in practice — Docker Desktop's macOS/Windows VM changes what "host" actually refers to.
The default bridge network: works, but missing DNS
Docker creates a default bridge network automatically, and any container run without --network lands on it — but unlike a user-defined bridge network, the default one doesn't provide automatic DNS resolution by container name, only by (unstable) IP address:
docker run -d --name db postgres:16 # default bridge
docker run -d --name app my-app # default bridge
# app can't reliably reach "db" by name here — no DNS on the default networkThis is the single most common source of "my containers can't find each other" confusion — the fix is almost always creating and using a real user-defined network instead of relying on the default one.
Overlay: networking across multiple hosts
Bridge networking only works between containers on the same Docker host. Overlay networks extend that same idea across multiple hosts — the networking layer behind Docker Swarm and a foundational concept Kubernetes' own networking model builds on:
docker network create --driver overlay --attachable my-overlayBest for: multi-host container orchestration, where services need to discover and reach each other regardless of which physical (or virtual) machine they're actually scheduled on. Most teams encounter this concept through Kubernetes' own CNI networking rather than raw Docker overlay networks directly, but the underlying problem — routing between containers that aren't on the same machine — is the same one both are solving.
Publishing ports: the bridge/host boundary
-p (publish) is what makes a bridged container's port reachable from outside Docker's internal network at all:
docker run -p 8080:80 nginx
# host:8080 → container:80Without -p, a container on a bridge network is reachable from other containers on the same network (by name, via Docker DNS) but not from the host machine or the outside world — a frequent point of confusion for anyone expecting a bridged container to be reachable by default the way host-mode containers are.
Inspecting a network directly
When container-to-container communication isn't working as expected, inspecting the actual network is the fastest way to confirm what's really connected to it, rather than guessing from configuration alone:
docker network ls # list all networks
docker network inspect my-app-network # see connected containers, IPs, subnet
docker network connect my-app-network some-container # attach an already-running container
docker network disconnect my-app-network some-containerdocker network inspect shows the actual current state — which containers are attached, their assigned IPs, and the subnet in use — which is worth checking directly before assuming a networking problem is a DNS or firewall issue elsewhere.
None network: full isolation
A fourth built-in option worth knowing exists: --network none gives a container no networking at all, not even a loopback to the outside — useful for a genuinely sandboxed process (a batch job processing local files with no legitimate reason to make any network call) where you want to guarantee no network access is possible, not just unlikely.
docker run --network none my-batch-jobCommon mistakes
- Relying on the default
bridgenetwork and expecting container-name DNS resolution to work. It doesn't — create a user-defined bridge network explicitly (or use Compose, which does this automatically) for that behavior. - Forgetting
-pand then being confused why a container's service isn't reachable from the host machine. Being on a bridge network makes a container reachable to other containers on that network, not to the host, without an explicit port publish. - Using host networking mode as a default "just to avoid networking issues." It removes isolation entirely and behaves inconsistently across platforms (Docker Desktop's VM changes what "host" means) — reserve it for genuine cases needing direct host network access, not as a general troubleshooting shortcut.
- Assuming overlay networking is only relevant to Kubernetes. It's a real, usable Docker networking driver on its own (via Swarm) — worth knowing as a distinct concept from bridge/host, not just something Kubernetes does internally.
Related reading
- Docker Compose for Local Development Environments — shares tags: docker, devops, cloud (same category).
- Docker Multi-Stage Builds: A Step-by-Step Tutorial — shares tags: docker, devops, cloud.
- Karpenter: Faster, Simpler Kubernetes Autoscaling on AWS — shares tags: cloud, devops.
- Kubernetes ConfigMaps and Secrets: A Practical Guide — shares tags: devops, cloud.
- Infrastructure as Code: Why Terraform Won — shares tags: devops, cloud.