Docker network modes
Docker offers several network drivers, each suited to specific use cases.
Understanding these drivers avoids most of the connectivity problems you run into with containers: an application that cannot find its database, a port that cannot be reached from outside, or, on the contrary, a database exposed to the Internet without anyone intending it. The most widely used drivers are bridge, host and overlay; there is also none, which cuts off all network access, and the rarer macvlan. The basic commands to explore what already exists:
docker network ls
docker network inspect bridge
docker network inspect mon-reseau --format '{{range .Containers}}{{.Name}} {{.IPv4Address}}{{"\n"}}{{end}}'
Bridge (default)
Bridge mode creates an isolated network for containers on the same host:
docker network create --driver bridge mon-reseau
docker run --network mon-reseau --name app myapp
docker run --network mon-reseau --name db mysql
Containers on the same bridge network can communicate using their container name as the hostname.
An important nuance: this name resolution only works on user-defined bridge networks. The default bridge network, to which containers started without a --network option are attached, provides no DNS between containers. That alone is reason enough to always create your own networks, which Docker Compose does automatically: each project gets a network named <project>_default.
On a bridge network, containers have private addresses and cannot be reached from outside. To make a service reachable, you publish a port: Docker then adds a NAT rule from the host to the container. By default, the port is opened on every host interface; specify an address to restrict it:
# Reachable from outside on host port 8080
docker run -d -p 8080:80 --name web nginx:alpine
# Reachable only from the host itself
docker run -d -p 127.0.0.1:8081:80 --name admin nginx:alpine
# Attach an existing container to a second network
docker network connect mon-reseau web
Be careful: the iptables rules Docker creates for published ports apply before those of a firewall such as UFW. A port published on 0.0.0.0 is therefore reachable even if UFW seems to block it.
Host
Host mode removes network isolation. The container uses the host's network directly:
docker run --network host nginx
Useful for maximum performance, but without isolation.
There is no NAT and no port to publish: Nginx listens directly on the host's port 80, and -p options are ignored. On the other hand, two containers cannot use the same port, and the container sees every network interface of the machine. This mode makes sense for tools that need to observe the host's network (monitoring agents, some VPN tools) or for workloads where every bit of latency counts. It is fully supported on Linux; with Docker Desktop on macOS and Windows, containers run inside a VM and the behaviour is not the same.
None
With --network none, the container only has a loopback interface. This is useful for processing that needs no network access at all, for example converting untrusted files: even if compromised, the process cannot exfiltrate anything.
docker run --rm --network none -v "$PWD/input:/data:ro" alpine ls -l /data
Overlay
For Docker Swarm clusters, the overlay network enables communication between containers on different nodes:
docker network create --driver overlay --attachable mon-overlay
An overlay network requires the host to be a Swarm node (docker swarm init). Traffic between nodes is encapsulated in VXLAN: ports 2377/tcp (cluster management), 7946/tcp and udp (node discovery) and 4789/udp (data) must be open between the machines. The --attachable option lets containers started with docker run, not just Swarm services, join the network. Management traffic is encrypted by default; to also encrypt application traffic between nodes, add --opt encrypted.
Macvlan
The macvlan driver gives the container its own MAC address and an IP address on the physical network, as if it were a separate machine. It is mainly used to integrate legacy applications that expect a dedicated IP. One quirk to know: by default, the host itself cannot communicate with its macvlan containers.
Internal DNS
Docker includes a DNS server that resolves service names:
services:
app:
networks:
- frontend
- backend
nginx:
networks:
- frontend
db:
networks:
- backend
networks:
frontend:
backend:
Inside every container, this DNS server is reachable at 127.0.0.11. It resolves service names, container names and any declared aliases, but only for containers that share a network. In the example above, app can reach nginx and db, while nginx cannot resolve db: a compromised Nginx has no direct path to the database. If a service is scaled to several containers, its name returns several addresses.
To go further, declare the backend network as internal (no outbound access) and share a network created outside the project with a reverse proxy:
networks:
frontend:
backend:
internal: true
proxy:
external: true
To diagnose a resolution or connectivity problem, start a tools container on the same network rather than installing packages in your images:
docker run --rm -it --network mon-reseau nicolaka/netshoot
# Then, inside the container:
dig db
nc -zv db 3306
Subnet conflicts
Docker assigns each network a private subnet, often in 172.17.0.0/16 and beyond. If your corporate network or your VPN already uses these ranges, some destinations become unreachable from the host. The fix is to define dedicated ranges in /etc/docker/daemon.json, then restart Docker and recreate the networks:
{
"default-address-pools": [
{ "base": "10.200.0.0/16", "size": 24 }
]
}
Driver summary
| Driver | Scope | Use case |
|---|---|---|
| bridge | One host | General case, Compose applications |
| host | One host | Maximum performance, network tools |
| none | One container | Processing with no network access |
| overlay | Several hosts | Docker Swarm clusters |
| macvlan | Physical network | Applications that require a dedicated IP |
Networking best practices
- Isolate services by network (frontend, backend, monitoring)
- Use internal networks for services that are not exposed
- Document your network topologies
- Keep exposed ports to the bare minimum
- Use service names rather than IP addresses, which change every time a container is recreated
- Bind ports that are only meant for local use to
127.0.0.1
For container security beyond networking, see the article Securing your Docker containers.