How does the default bridge network provide container-to-container communication, and why is it usually not the best choice?
Quick Answer
The default bridge network (docker0) lets containers reach each other by IP address, and provides outbound internet access via NAT — but it does not provide automatic DNS-based service discovery by container name, unlike a user-defined bridge network. This means containers on the default bridge must be hardcoded to reach each other by IP (which can change on restart) rather than by a stable, memorable name, which is exactly why Docker's own documentation recommends always creating a custom, user-defined bridge network instead of relying on the default one.
Detailed Answer
What happens without specifying a network
docker run -d --name web nginx
docker run -d --name api myapi:1.0
Without an explicit --network flag, both containers attach to Docker's default bridge network (visible as the docker0 interface on the host). Each gets its own private IP address on this virtual network, and can reach the outside internet through NAT via the host.
The critical limitation: no built-in DNS resolution by name
docker exec web ping api
# ping: api: Name or service not known
On the default bridge network specifically, containers cannot resolve each other by container name. You'd have to look up api's current IP address manually (docker inspect api) and hardcode that IP into web's configuration. This is fragile, since a container's IP on the default bridge can change if it's stopped and restarted.
The fix: a user-defined bridge network
docker network create my-app-network
docker run -d --network my-app-network --name web nginx
docker run -d --network my-app-network --name api myapi:1.0
docker exec web ping api
# PING api (172.20.0.3): 56 data bytes <- resolves correctly by name!
A user-defined bridge network (created explicitly via docker network create) includes Docker's embedded DNS server automatically. This lets containers on that same network resolve each other by container name, or by any --network-alias assigned. This is the single biggest practical reason the default bridge is discouraged for real multi-container applications.
Additional benefits of user-defined networks over the default
- Better isolation. You can create multiple separate user-defined networks. Containers only see or reach others explicitly attached to the same network, giving you a deliberate segmentation tool — for example, a "frontend" network and a "backend" network, with only specific containers bridging both — that the single, shared default bridge doesn't provide.
- Dynamic reconnection — a running container can be connected to or disconnected from a user-defined network on the fly (
docker network connect/disconnect), without needing to restart it; the default bridge is more rigid about this. - This is exactly what Docker Compose does automatically. Every
docker-compose.ymlproject automatically gets its own user-defined bridge network by default. This is precisely why Compose services can reference each other by their service name out of the box (see the Compose topic), without any manual network setup at all.
The default bridge is really only still relevant for the simplest, single-container, no-inter-container-communication case, or for historical/legacy setups. Anything with more than one container talking to each other should get a user-defined network instead.