Docker Networking Deep Dive: Bridge, Host, Macvlan en Overlay Uitgelegd
Docker lijkt vaak “magisch” te netwerken: containers kunnen elkaar vinden, poorten worden “gepubliceerd”, en ineens is er een virtueel subnet dat je nooit zelf hebt aangemaakt. In deze tutorial duiken we diep in Docker networking met focus op de vier meest gebruikte drivers: bridge, host, macvlan en overlay. Je leert wat er onder de motorkap gebeurt, wanneer je welke driver kiest, en je krijgt echte commando’s om het zelf te testen en te troubleshooten.
Alle voorbeelden gaan uit van Linux (bijv. Ubuntu/Debian). Op macOS/Windows werkt Docker via een VM; sommige drivers/gedragingen (met name
hostenmacvlan) zijn daar anders of beperkt.
Inhoud
- 1. Basisconcepten: namespaces, veth, bridges en iptables
- 2. Bridge networking (default en user-defined)
- 3. Host networking: performance en valkuilen
- 4. Macvlan: containers als “echte” hosts op je LAN
- 5. Overlay networking: multi-host met Swarm
- 6. Keuzehulp: welke driver wanneer?
- 7. Handige inspect- en debugcommando’s
1. Basisconcepten: namespaces, veth, bridges en iptables
Docker networking bouwt op Linux kernel primitives:
- Network namespace: elke container krijgt (meestal) een eigen netwerkstack: eigen interfaces, routes, ARP cache, iptables binnen de namespace, etc.
- veth pair: een virtuele “kabel” met twee uiteinden. Eén kant in de container namespace (bijv.
eth0), de andere kant op de host (bijv.vethXXXX). - Linux bridge: een L2-switch in software. Docker maakt vaak een bridge zoals
docker0ofbr-<id>. - iptables/nftables: voor NAT (masquerade), port forwarding (DNAT), en filtering.
Je kunt dit letterlijk zien op de host:
ip addr
ip link
ip route
sudo iptables -t nat -L -n -v
sudo iptables -L -n -v
En per container:
docker run --rm -it alpine sh
# in container:
ip addr
ip route
Belangrijk: Docker kan verschillende “drivers” gebruiken om containerinterfaces te koppelen aan de host en/of aan andere hosts. Dat bepaalt hoe verkeer loopt, hoe IP’s worden toegekend, en welke isolatie je krijgt.
2. Bridge networking (default en user-defined)
2.1 Default bridge (docker0)
Als je Docker installeert, krijg je vrijwel altijd de default bridge: docker0. Containers die je start zonder --network komen hierop terecht (tenzij je daemon anders is geconfigureerd).
Check de default bridge:
ip addr show docker0
ip link show docker0
docker network ls
docker network inspect bridge
Je ziet meestal een subnet zoals 172.17.0.0/16. Wanneer je een container start:
docker run -d --name web1 nginx:alpine
docker inspect -f '{{.NetworkSettings.IPAddress}}' web1
Docker maakt een veth pair, hangt één kant in web1 als eth0, en de andere kant in de host namespace aan docker0. De bridge gedraagt zich als een switch: containers op dezelfde bridge kunnen elkaar op L2 bereiken.
Maar: de default bridge heeft beperkingen:
- Geen automatische DNS-resolutie op containernaam (historisch; tegenwoordig beperkt/anders dan user-defined).
- Minder controle over isolatie.
- Legacy gedrag dat vaak verwarrend is.
2.2 User-defined bridge: DNS, isolatie en controle
In de praktijk wil je meestal een user-defined bridge. Voordelen:
- Ingebouwde DNS: containers kunnen elkaar bereiken via naam.
- Betere isolatie: netwerken zijn gescheiden tenzij je containers aan meerdere netwerken hangt.
- Je kunt subnets, gateways en opties instellen.
Maak een eigen bridge:
docker network create \
--driver bridge \
--subnet 172.30.0.0/16 \
--gateway 172.30.0.1 \
appnet
Start twee containers op dit netwerk:
docker run -d --name api --network appnet hashicorp/http-echo -text="hallo"
docker run --rm -it --network appnet alpine sh
In de Alpine container:
apk add --no-cache curl bind-tools
nslookup api
curl http://api:5678
Je ziet dat api via Docker DNS (embedded DNS server) resolvet naar het container-IP op appnet.
Isolatie voorbeeld: maak nog een netwerk en toon dat namen niet resolven over netwerken heen.
docker network create othernet
docker run -d --name db --network othernet postgres:16-alpine
docker run --rm -it --network appnet alpine sh
# in container:
nslookup db # zal falen of geen resultaat geven
2.3 Port publishing en NAT: wat gebeurt er echt?
Als je een container draait op een bridge-netwerk, is hij standaard alleen bereikbaar vanaf:
- de host zelf
- andere containers op hetzelfde netwerk
- andere netwerken waar hij aan gekoppeld is
Om verkeer van buiten naar binnen te krijgen, gebruik je port publishing:
docker run -d --name webpub -p 8080:80 nginx:alpine
curl -I http://127.0.0.1:8080
Wat gebeurt er onder water?
- Docker zet een DNAT rule in
iptables(tabelnat) zodat verkeer naar hostpoort 8080 wordt doorgestuurd naar het container-IP:80. - Vaak komt er ook een MASQUERADE rule voor uitgaand verkeer van containers naar buiten.
Bekijk de NAT rules:
sudo iptables -t nat -L -n -v | sed -n '1,200p'
sudo iptables -t nat -S | grep -E 'DOCKER|MASQUERADE|DNAT'
Je ziet regels die verwijzen naar chain DOCKER. Daarin staan mappings zoals “tcp dpt:8080 to:172.17.x.y:80”.
Belangrijk detail: -p 8080:80 bindt standaard op alle host-interfaces. Wil je alleen localhost:
docker run -d --name weblocal -p 127.0.0.1:8081:80 nginx:alpine
Controleer luisterende sockets:
ss -lntp | grep -E ':8080|:8081'
2.4 Troubleshooting bridge
Handige checks:
- Welke netwerken en IP’s?
docker network ls
docker network inspect appnet
docker inspect web1 | less
- Routing en ARP op host:
ip route
ip neigh show
bridge link
bridge fdb show | head
- Verkeer volgen met tcpdump (host):
Zoek eerst de veth-interface die bij een container hoort:
docker inspect -f '{{.State.Pid}}' web1
# stel PID=12345
sudo nsenter -t 12345 -n ip addr
Op host kun je ook ip link bekijken; veth-namen wisselen. Sniffen op de bridge is vaak makkelijker:
sudo tcpdump -ni docker0 port 80
- Connectivity testen:
docker exec -it web1 sh -c "apk add --no-cache curl >/dev/null 2>&1 || true; curl -I http://127.0.0.1"
3. Host networking: performance en valkuilen
Met --network host krijgt de container geen eigen netwerk namespace (of: hij deelt die van de host). Gevolgen:
- Container gebruikt direct de host interfaces en IP’s.
- Geen port mapping nodig/possible; de container luistert direct op hostpoorten.
- Vaak minder overhead (geen NAT, geen bridge), nuttig voor high-performance of latency-gevoelige workloads.
- Minder isolatie: poortconflicten en security-impact.
Voorbeeld:
docker run --rm -d --name hostng --network host nginx:alpine
Test:
curl -I http://127.0.0.1:80
ss -lntp | grep ':80'
Je zult zien dat nginx nu op de hostpoort 80 luistert (dus als je al een webserver had draaien, faalt dit).
Wanneer gebruiken?
- Monitoring agents die host-netwerk moeten zien (bijv. node-exporters, packet capture).
- Applicaties die multicast/broadcast op het LAN nodig hebben (soms beter met macvlan, maar host kan ook).
- Maximale performance (maar meet het: vaak is bridge “goed genoeg”).
Valkuilen:
- Je verliest netwerkisolatie per container.
- Service discovery via Docker DNS werkt anders/niet zoals op user-defined bridges.
- Port collisions zijn frequent.
4. Macvlan: containers als “echte” hosts op je LAN
Macvlan laat Docker containers een eigen MAC-adres geven op een fysieke interface (de “parent”), waardoor containers direct op je LAN kunnen verschijnen met een IP uit het LAN-subnet. Ze gedragen zich alsof het aparte machines zijn.
Waarom is dit nuttig?
- Legacy apps die een “echt” LAN-IP verwachten.
- Netwerkapparatuur die niet goed over NAT/port mapping werkt.
- Je wilt dat containers direct door andere machines op het LAN benaderd worden zonder
-pmappings.
4.1 Macvlan modes en beperkingen
Macvlan kent meerdere modes; Docker gebruikt meestal bridge mode (niet te verwarren met Docker bridge driver). In macvlan bridge mode kunnen macvlan interfaces onderling communiceren via de parent, afhankelijk van switch/driver.
Belangrijke beperking: de host kan standaard niet met macvlan containers praten via dezelfde parent interface. Dit is een veelvoorkomende “waarom kan ik mijn container niet pingen vanaf de host?”-vraag. Oplossing volgt in 4.3.
Daarnaast:
- Je switch moet meerdere MAC’s op één fysieke poort accepteren (meestal oké, maar sommige managed switches hebben port-security).
- Wi-Fi interfaces werken vaak slecht met macvlan (driver/802.11 beperkingen). Ethernet is sterk aanbevolen.
4.2 Macvlan opzetten (met parent interface)
Stel:
- Je LAN is
192.168.10.0/24 - Gateway/router is
192.168.10.1 - Host zit op interface
eth0 - Je wilt containers IP’s geven uit
192.168.10.200-192.168.10.250
Maak het macvlan netwerk:
docker network create -d macvlan \
--subnet=192.168.10.0/24 \
--gateway=192.168.10.1 \
--ip-range=192.168.10.192/26 \
-o parent=eth0 \
lanmac
Start een container met vast IP:
docker run --rm -it --network lanmac --ip 192.168.10.210 alpine sh
In de container:
ip addr
ip route
apk add --no-cache curl
Vanaf een andere machine op het LAN:
ping 192.168.10.210
Je kunt ook een service draaien zonder port publishing:
docker run -d --name lanweb --network lanmac --ip 192.168.10.211 nginx:alpine
# vanaf een andere machine:
curl -I http://192.168.10.211
4.3 Host ↔ container communicatie oplossen
Standaard kan de host niet direct praten met macvlan containers. Een gangbare oplossing is een macvlan sub-interface op de host maken en die in hetzelfde subnet zetten (met een vrij IP). Bijvoorbeeld:
- Maak macvlan interface op host:
sudo ip link add macvlan0 link eth0 type macvlan mode bridge
sudo ip addr add 192.168.10.190/24 dev macvlan0
sudo ip link set macvlan0 up
- Voeg route toe (vaak al aanwezig door /24, maar expliciet kan helpen):
sudo ip route add 192.168.10.192/26 dev macvlan0
- Test vanaf host:
ping -c 2 192.168.10.211
curl -I http://192.168.10.211
Let op IP-conflicten: kies een host-IP (192.168.10.190) dat niet door DHCP wordt uitgedeeld en niet door Docker --ip-range wordt gebruikt.
Persistency: bovenstaande ip link add is niet persistent na reboot. Maak het persistent via je distro’s network config (bijv. systemd-networkd of NetworkManager), maar dat valt buiten de scope van deze Docker-only tutorial.
5. Overlay networking: multi-host met Swarm
Overlay netwerken zijn bedoeld voor container-to-container networking over meerdere Docker hosts. Docker gebruikt hiervoor o.a. VXLAN encapsulatie. In de praktijk gebruik je overlay meestal samen met Docker Swarm (ingebouwd in Docker Engine).
Belangrijke concepten:
- Swarm nodes: managers en workers.
- Services: declaratieve workloads (replica’s) die op nodes kunnen draaien.
- Overlay network: een virtueel L2/L3 netwerk dat nodes overspant.
- Service discovery: ingebouwde DNS voor services.
5.1 Swarm init en overlay aanmaken
Op de eerste host (manager):
docker swarm init --advertise-addr <MANAGER_IP>
Bekijk join tokens:
docker swarm join-token worker
docker swarm join-token manager
Op een tweede host (worker), voer het join-commando uit dat je net kreeg.
Controleer status op manager:
docker node ls
Maak een overlay netwerk:
docker network create -d overlay --attachable appovl
docker network ls
docker network inspect appovl
--attachable betekent dat je naast services ook losse containers kunt attachen (handig voor debug).
5.2 Services, VIP vs DNSRR, routing mesh
Maak een service op overlay:
docker service create --name whoami --network appovl --replicas 3 traefik/whoami
docker service ls
docker service ps whoami
Test service discovery door een debug container te starten op hetzelfde overlay netwerk:
docker run --rm -it --network appovl alpine sh
In de debug container:
apk add --no-cache curl bind-tools
nslookup whoami
curl http://whoami
VIP (Virtual IP) load balancing: standaard krijgt een service een VIP en Docker load-balancet naar tasks. Je ziet één IP bij nslookup whoami.
Wil je DNS round-robin (DNSRR) in plaats van VIP:
docker service update --endpoint-mode dnsrr whoami
Dan zal DNS meerdere A-records geven (één per task IP), en je client doet de balancing.
Routing mesh en published ports
Swarm heeft een “routing mesh”: als je een service poort publiceert, kan je die poort op elke node benaderen, ongeacht waar de task draait.
Voorbeeld:
docker service create \
--name web \
--network appovl \
--replicas 2 \
--publish published=8080,target=80 \
nginx:alpine
Test vanaf buiten (naar eender welke node IP):
curl -I http://<ANY_NODE_IP>:8080
Wil je host-mode publishing (alleen op nodes waar een task draait, geen routing mesh):
docker service update --publish-rm 8080 web
docker service update --publish-add published=8080,target=80,mode=host web
Controleer waar tasks draaien:
docker service ps web
5.3 Encryptie en poorten
Overlay gebruikt VXLAN (UDP 4789) en control plane verkeer. Zorg dat in je netwerk/firewall minimaal dit openstaat tussen nodes:
- TCP 2377 (Swarm management)
- TCP/UDP 7946 (node discovery/gossip)
- UDP 4789 (VXLAN data path)
Versleutelde overlay (data plane encryptie):
docker network create -d overlay --opt encrypted --attachable secureovl
Dit voegt overhead toe, maar kan nodig zijn op onbetrouwbare netwerken.
5.4 Troubleshooting overlay
- Swarm status:
docker info | sed -n '1,120p'
docker node ls
docker network ls
- Service DNS en connectiviteit:
docker run --rm -it --network appovl alpine sh
# in container:
apk add --no-cache curl bind-tools
nslookup web
curl -I http://web
- Inspect tasks en endpoints:
docker service inspect web --pretty
docker service ps web
docker inspect $(docker ps -q --filter name=web.) | less
- Check poorten op nodes:
ss -lntup | grep -E '2377|7946|4789'
- VXLAN interface op host (kan per distro verschillen):
ip -d link show | grep -A2 -E 'vxlan|docker_gwbridge'
ip addr show docker_gwbridge
docker_gwbridge is een speciale bridge die Swarm vaak gebruikt als gateway tussen overlay en host/extern verkeer.
6. Keuzehulp: welke driver wanneer?
Bridge (user-defined)
- Beste default voor single-host setups.
- Goede isolatie, ingebouwde DNS, makkelijk port publishen.
- Gebruik dit voor de meeste compose-projecten op één host.
Host
- Als je maximale performance wil en isolatie minder belangrijk is.
- Als je direct host-netwerk nodig hebt (bijv. bepaalde monitoring/packet tools).
- Let op poortconflicten en security.
Macvlan
- Als containers “first-class citizens” op je LAN moeten zijn met eigen IP/MAC.
- Handig voor legacy protocollen, of als je geen NAT/port mapping wilt.
- Let op host↔container beperking en switch port-security.
Overlay
- Voor multi-host container networking (Swarm).
- Ingebouwde service discovery en (optioneel) routing mesh.
- Vereist Swarm en open firewallpoorten tussen nodes.
7. Handige inspect- en debugcommando’s
Docker-level
Netwerken en details:
docker network ls
docker network inspect bridge
docker network inspect appnet
Container netwerkinfo:
docker inspect web1 --format '{{json .NetworkSettings.Networks}}' | jq
docker exec -it web1 ip addr
docker exec -it web1 ip route
Snel IP ophalen:
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' web1
Container aan extra netwerk hangen:
docker network connect appnet webpub
docker network disconnect bridge webpub
Linux-level
Interfaces, bridges:
ip link
ip addr
bridge link
bridge vlan show
Routes en NAT:
ip route
sudo iptables -t nat -L -n -v
sudo iptables -t nat -S | grep DOCKER
Traffic capture:
sudo tcpdump -ni docker0
sudo tcpdump -ni br-$(docker network inspect -f '{{.Id}}' appnet | cut -c1-12)
Namespace debugging met nsenter:
PID=$(docker inspect -f '{{.State.Pid}}' web1)
sudo nsenter -t "$PID" -n ip addr
sudo nsenter -t "$PID" -n ip route
sudo nsenter -t "$PID" -n ss -lntup
Afsluiting
Docker networking is geen black box: het is een combinatie van Linux namespaces, virtuele interfaces, bridges, en (meestal) iptables-regels. Door bewust te kiezen tussen bridge, host, macvlan en overlay kun je je netwerkontwerp afstemmen op isolatie, performance, bereikbaarheid en schaal over meerdere hosts.
Als je wilt, kan ik op basis van jouw situatie (single host vs multi-host, LAN-subnet, security-eisen, Compose/Swarm) een concreet netwerkplan uitschrijven met exacte commando’s en een testmatrix (ping/curl/DNS) om te verifiëren dat alles correct werkt.