Docker networking explained bridge host overlay

The three core Docker network drivers, bridge, host, and overlay, each solve a different problem. Pick wrong and you either lose isolation, hit a performance wall, or spend hours debugging cross-host routing. This guide walks through each driver, when to use it, and how to set it up on a multi-node environment. Everything here runs on a Linux VPS with Docker Engine 24+.
Key takeaways
- Bridge is the default driver. Containers on the same bridge talk to each other and are NAT'd to the host IP. Good for single-host microservices.
- Host removes network isolation entirely. The container shares the host's network stack with near-native performance. Useful for low-latency or port-heavy workloads.
- Overlay spans multiple hosts. Requires Docker Swarm or a key-value store. Containers across hosts appear on the same flat network.
- You can attach a container to multiple networks. This is common when a reverse proxy (bridge) needs access to an overlay-only service.
Prerequisites
- A Linux VPS running Ubuntu 24.04 LTS or Debian 12. For overlay you need at least two VPSs.
- Docker Engine 24+ installed (
docker --versionto check). - Root or sudo access. All commands assume a non-root user in the
dockergroup. - For overlay, ports 2377, 7946, and 4789 must be open between hosts.
Why this matters, a real scenario
Say a web app has three services: an Nginx reverse proxy, a Go API server, and a PostgreSQL database. On a single VPS, the default bridge network works fine: the proxy talks to the API by service name, the API writes to the DB by container name. But you need to deploy that same stack across two VPSs for redundancy. Now the database lives on host B, the API on host A. The bridge on host A cannot reach the DB on host B. This is where overlay comes in, it makes cross-host communication look like local networking without you opening arbitrary firewall holes.
The reverse case is also common: a container that handles UDP traffic or needs to bind hundreds of ports. Bridge NAT adds overhead and limits port range. Host mode sidesteps both problems.
Step 1, Bridge network basics and when to use it
The bridge driver creates a private internal network on the host. Docker assigns each container an IP in the 172.17.0.0/16 range by default. The host acts as a NAT gateway. Containers on the same bridge can reach each other by IP or, better, by container name if you use --name and the built-in DNS resolver.
When to use: single-host deployments where you want isolation between different stacks. Each stack gets its own bridge. Example: a dev environment and a staging environment on the same VPS, create two bridges, attach the correct containers to each, and they stay isolated.
# Create a custom bridge (the default "bridge" works but you cannot use DNS resolution on it)
docker network create my-app-net
# Run two containers attached to it
docker run -d --name api --network my-app-net alpine sleep 3600
docker run -d --name proxy --network my-app-net alpine sleep 3600
# Test DNS resolution
docker exec proxy ping api
Verification: the ping output shows the IP of the api container (something like 172.18.0.2). If it resolves by name, the DNS resolver is working.
Gotcha: never use the default bridge network for production, it lacks container-name-based DNS and falls back to link-based resolution which is deprecated. Always create a custom bridge.
On a VPS with multiple network interfaces, you can restrict a bridge to use only one host IP by not publishing ports globally. Instead, bind to a specific IP:
docker run -d --name api -p 192.168.1.10:8080:8080 --network my-app-net my-api-image
This is useful when your VPS has several IPv4 addresses and you want different stacks on different IPs, common on multi-IP VPS plans from providers like thueVPS.
Step 2, Host network for maximum performance
Host mode removes network isolation. The container shares the host's network stack directly, no NAT, no virtual bridge. This means the container sees the host's interfaces and ports as its own. Publishing ports (-p) is ignored because every port the application listens on is automatically exposed on the host.
When to use: latency-sensitive applications (game servers, high-frequency trading bots, VoIP), or services that need to bind many ports dynamically. Also useful for applications that need to sniff traffic or modify network interfaces, tools like tcpdump or a VPN container.
# Run a container in host mode
docker run --rm --network host nginx
# Check listening ports, nginx now uses the host's port 80 directly
ss -tlnp | grep nginx
Verification: ss -tlnp shows 0.0.0.0:80 with process: nginx. If you use bridge mode, the port shows with a Docker-proxy PID, not nginx directly.
All traffic is host traffic. If the host has a firewall configured with ufw/firewalld or nftables, the container inherits all rules. You cannot apply network-level restrictions from inside the container, they are either set on the host or not at all.
Gotcha: you cannot run more than one container on host mode that binds the same port. Two web servers both listening on :80 will conflict. Use bridge mode when you need multiple containers on the same port mapped to different host ports (-p 8080:80 vs -p 8081:80).
Step 3, Overlay network for multi-host setups
The overlay driver creates a flat virtual network that spans multiple Docker hosts. Packets are encrypted in transit by default (AES-GCM with automatic key rotation). Each host runs a distributed data store (built into Docker Swarm) that maintains the overlay state.
When to use: any microservice architecture that runs across two or more hosts, especially when services discover each other by name. Also for databases where replicas run on different hosts but need to see each other on the same subnet.
Overlay networks require a Swarm cluster. Below is the setup for two VPSs.
Initialize the Swarm on the first host
docker swarm init --advertise-addr 10.0.0.1 # replace with VPS1's private IP
# Output gives a join token. Copy it.
docker swarm join-token worker
Join the second host
Run the command from the previous output on VPS2:
docker swarm join --token SWMTKN-1-xxxxxx 10.0.0.1:2377
Verification: on VPS1 run docker node ls. The second node should appear as Ready.
Create an overlay network
docker network create -d overlay --attachable my-overlay-net
The --attachable flag lets standalone containers (non-Swarm services) use the overlay. Without it, only Swarm services can connect.
Run a service on both hosts
docker service create --name web --network my-overlay-net --replicas 2 nginx
Now both Nginx replicas (one on each host) are on the same 10.0.x.x network. They can reach each other by service name (web) or by container name injected by DNS round-robin.
Verification: exec into any container and ping the service name:
docker exec -it $(docker ps -q | head -1) ping web
The output resolves web to two IPs (the two replicas). Every 10 seconds the cursor moves, or set -c 3 to see both responses.
Firewall note: overlay traffic uses VXLAN over UDP port 4789. The swarm control plane uses TCP 2377. Node discovery and gossip use UDP 7946. All three must be open between hosts. On a thueVPS with a custom nftables ruleset, add:
nft add rule inet filter input ip saddr 10.0.0.0/24 tcp dport 2377 accept
nft add rule inet filter input ip saddr 10.0.0.0/24 udp dport { 7946, 4789 } accept
Replace 10.0.0.0/24 with your actual host subnet.
How to choose which driver to use
| Driver | Isolation | Cross-host | Performance | Best for |
|---|---|---|---|---|
| Bridge | Per-bridge per host | No (needs port mapping + host knowledge) | Good (some NAT overhead) | Single-host stacks, dev environments, isolated workloads |
| Host | None (shares host stack) | No (cannot run the same port twice) | Native (zero overhead) | Latency-critical, port-heavy, networking tools |
| Overlay | Per-overlay across hosts | Yes (flat subnet across hosts) | Moderate (encryption + VXLAN overhead) | Multi-host microservices, Swarm services |
Troubleshooting
Containers on a custom bridge cannot resolve each other
You probably used the default bridge network. Delete and recreate on a custom bridge:
docker network create my-net
docker network connect my-net container1
docker network connect my-net container2
Overlay services cannot communicate
Check that port 4789 (VXLAN) is open between all hosts. A common mistake is opening it only on the manager node. Every node in the Swarm needs these ports. Also verify that docker network inspect my-overlay-net shows all nodes as Peers with correct endpoints.
Host mode container cannot bind a port
Check if something on the host already uses that port. Host mode gives the container full access, including conflicts. Run ss -tlnp to see the conflict. There is no workaround, you must stop the other process or use bridge mode.
FAQ
Can a container be on both a bridge and an overlay network?
Yes. Connect the container to both networks with docker network connect. It gets an IP on each. Common pattern: a database on overlay for cross-host access, plus a bridge for local admin tools.
Does the overlay driver support encryption?
Yes, by default since Docker 17.12+. Traffic between hosts is encrypted with AES in GCM mode. You can disable it with the --opt encrypted=false flag for older tooling compatibility.
Is there an ipvlan or macvlan driver?
Yes. They assign containers IPs directly from the host's physical LAN. Use them when containers must be on the same subnet as the host's external network. They are less common on budget VPSs because the provider must allow multiple MAC addresses on the virtual port, most do not. Stick to bridge for single-host and overlay for multi-host unless the use case demands it.
What is the performance penalty of overlay compared to bridge?
Overlay has higher CPU overhead due to VXLAN encapsulation and encryption, about 10-20% throughput loss in our testing with iperf3 over a 1 Gbps link. For most web applications this is invisible. If you are moving bulk data (>500 Mbps sustained), consider host networking or a dedicated bridge with direct host routing (requires custom routing on both hosts).
Can I use an overlay network without Docker Swarm?
No. The overlay driver depends on Swarm's distributed state store. If you cannot use Swarm (need Kubernetes or a different orchestrator), consider third-party overlay plugins like Weave Net or Flannel.
How do I connect a standalone container to an existing overlay?
Create the overlay with --attachable (as shown in Step 3). Then it behaves like a bridge, any docker run --network my-overlay-net works. Without --attachable, only Swarm services can attach.
Related articles
- Self-host n8n on a VPS for workflow automation
- Reverse proxy with automatic HTTPS using Caddy
- Zero-downtime deploy with Nginx and systemd
- Set up a WireGuard VPN on your VPS


