Docker-Netzwerke sind der Teil, der Einsteiger am häufigsten überrascht. Container haben eigene Netzwerkschnittstellen, eigene Adressen und eigene Namensauflösung – und welches Netzwerk ein Container nutzt, entscheidet darüber, ob zwei Container sich sehen und ob Dienste von außen erreichbar sind.
Die vier Netzwerktreiber
- bridge (Standard): Ein eigenes Software-Netz je Docker-Installation. Container bekommen Adressen aus einem internen Bereich (üblicherweise 172.17.0.0/16) und erreichen sich über eigene Namen, wenn sie im selben benutzerdefinierten Netz sind.
- host: Der Container nutzt die Netzwerkschnittstellen des Hosts direkt. Keine eigene IP, kein Portmapping nötig. Schnellste Variante, aber ohne Isolation – zwei Container können nicht denselben Port belegen.
- none: Kein Netzwerk außer der eigenen Schleife. Richtig für Aufgaben ohne Netzzugriff und für maximale Isolation.
- overlay: Für Verbindungen zwischen mehreren Hosts (Docker Swarm). Setzt eine Steuerungsebene voraus.
- macvlan: Der Container erhält eine eigene MAC-Adresse im physischen Netz – er erscheint dort als eigenes Gerät. Nützlich für Alt-Anwendungen, die eine echte Netzpräsenz erwarten.
Warum benutzerdefinierte Netze fast immer richtig sind
# Das Standard-Netz "bridge" hat zwei grosse Nachteile:
# 1. KEINE Namensauflösung zwischen Containern
# (der veraltete --link funktioniert nur in eine Richtung)
# 2. Alle Container teilen sich ein Netz
# Benutzerdefiniertes Netz: Namensauflösung inklusive
docker network create app-netz
docker run -d --name db --network app-netz postgres:16
docker run -d --name api --network app-netz myapi:1.0
# Der Container "api" erreicht die Datenbank jetzt ueber den NAMEN
# psql -h db -U app <- "db" wird aufgeloest
# kein IP-Eintrag noetig, kein Wissen ueber interne Adressen
# Netzwerke ansehen
docker network ls
docker network inspect app-netz
docker network inspect app-netz --format '{{range .Containers}}{{.Name}} {{.IPv4Address}}{{"\n"}}{{end}}'
# Container nachtraeglich verbinden
docker network connect app-netz mein-container
docker network disconnect app-netz mein-container
# Netzwerk mit eigenem Bereich (Kollisionen mit dem Firmennetz vermeiden)
docker network create --subnet 10.99.0.0/24 --gateway 10.99.0.1 intern
Ports: veröffentlichen oder nicht
# Ein Container ist OHNE -p von aussen NICHT erreichbar.
# Die interne IP ist nur auf dem Docker-Host erreichbar.
# Nur fuer den Host sichtbar (empfohlen fuer Datenbanken!)
docker run -d -p 127.0.0.1:5432:5432 postgres:16
# Achtung: "-p 5432:5432" ohne Adresse = auf ALLEN Schnittstellen,
# auch im Internet, wenn der Host dort direkt steht!
# Fuer alle erreichbar (nur fuer echte oeffentliche Dienste)
docker run -d -p 80:80 -p 443:443 nginx:alpine
# Beliebiges Host-Port (praktisch zum Testen, unbrauchbar fuer Betrieb)
docker run -d -p 8080 nginx:alpine
# Nur internes Netz, kein Portmapping
docker run -d --network app-netz postgres:16
# Die haeufigste Fehlannahme: "EXPOSE 5432" im Dockerfile
# EXPOSE veroeffentlicht NICHTS! Es ist nur eine Dokumentation.
# Erreichbar wird ein Dienst ausschliesslich ueber -p / --publish
# oder weil ein anderer Container im selben Netz darauf zugreift.
# Ports laufender Container ansehen
docker port mein-container
Der Weg einer Anfrage
Internet
|
v
Host-Schnittstelle (z. B. 203.0.113.10:443)
|
v Docker-Proxy-Regel (DNAT)
Container-IP im Bridge-Netz (172.17.0.5:443)
# Folge: Im Container sieht die Anwendung die HOST-Adresse
# (die Docker-Bruecke ist die Gegenstelle), NICHT die echte Client-IP.
# Deshalb sind die Kopfzeilen X-Real-IP / X-Forwarded-For so wichtig,
# und deshalb erscheint alles im Protokoll mit derselben Quelladresse.
# Bei "host"-Netzwerk entfaellt die Uebersetzung - die echte
# Client-Adresse kommt durch. Ein Grund, warum Reverse-Proxies
# haeufig im Host-Netz betrieben werden.
Fehlersuche Schritt für Schritt
# 1. Laeuft der Container ueberhaupt?
docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'
docker inspect mein-container --format '{{.State.Status}} exit={{.State.ExitCode}}'
docker logs --tail 50 mein-container
# 2. In welchem Netz ist er - und mit welcher Adresse?
docker inspect mein-container --format '{{json .NetworkSettings.Networks}}' | jq
docker inspect mein-container --format '{{range $k,$v := .NetworkSettings.Networks}}{{$k}} {{$v.IPAddress}}{{"\n"}}{{end}}'
# 3. Laeuft IM Container ein Dienst - und auf welcher Adresse?
# Der haeufigste Fehler: Der Dienst lauscht nur auf 127.0.0.1!
docker exec mein-container ss -tlnp
# "127.0.0.1:8080" -> von aussen NICHT erreichbar
# "0.0.0.0:8080" -> erreichbar
# Bei Anwendungen: Host-Bindung auf 0.0.0.0 konfigurieren
# (nginx: listen 0.0.0.0:80; node: app.listen(8080, '0.0.0.0'))
# 4. Ist die Gegenstelle vom Container aus erreichbar?
docker exec -it mein-container sh -c 'nc -vz db 5432'
docker exec -it mein-container cat /etc/hosts
docker exec -it mein-container getent hosts db
# Leer -> kein gemeinsames Netzwerk oder falscher Name
# 5. Ist das Portmapping aktiv?
docker port mein-container
curl -v http://127.0.0.1:8080/
# Verbindungsabbruch -> Dienst lauscht nicht auf 0.0.0.0 (Schritt 3)
# 6. Auf dem Host pruefen
ss -tlnp | grep -E '8080|:80 |:443'
iptables -t nat -L DOCKER -n | head -20 # Docker-Regeln
# 7. Protokoll der Docker-Verwaltung
journalctl -u docker --since '10 min ago' | tail -30
Häufige Fehler
- Dienst lauscht auf 127.0.0.1 im Container. Der Klassiker. Der Container selbst funktioniert, ist aber von außen unerreichbar.
- Standard-Netz verwendet. Keine Namensauflösung, dadurch hartcodierte IP-Adressen, die sich nach einem Neustart ändern.
-p 5432:5432statt-p 127.0.0.1:5432:5432. Datenbanken werden versehentlich im Internet erreichbar – eine der häufigsten Ursachen für Datenabflüsse bei Docker.localhoststatt Dienstnamen verwendet.localhostzeigt innerhalb des Containers auf den Container selbst, nicht auf den Host. Für den Host:host.docker.internal(Docker Desktop) oder die Adresse der Brücke.- Subnetz kollidiert. Das Docker-Netz verwendet 172.17.0.0/16. Befindet sich dieser Bereich im Firmennetz, ist er nicht erreichbar. Eigenen Bereich beim Anlegen festlegen.
- Firewall auf dem Host blockiert. Docker schreibt eigene Regeln in
iptables(KetteDOCKER). Eine nachträglich geladene Firewall-Konfiguration kann diese überschreiben. - IPv6 vergessen. Ohne
--ipv6oder eine passende Konfiguration ist der Dienst über IPv6 nicht erreichbar – und manche Clients versuchen es zuerst. - Zu viele benutzerdefinierte Netze. Jedes Netz ist eine Brücke mit eigenen Regeln. Ein bis zwei Netze je Anwendung genügen: eines für die Außenwelt, eines für die interne Kommunikation.
Muster für einen sauberen Aufbau
docker network create --subnet 10.99.1.0/24 app-intern
# Datenbank: nur intern, kein Portmapping
docker run -d --name db \
--network app-intern \
--restart unless-stopped \
-e POSTGRES_PASSWORD_FILE=/run/secrets/db_pass \
-v db-daten:/var/lib/postgresql/data \
postgres:16
# Anwendung: intern, plus Portmapping nur auf die Schleife
docker run -d --name api \
--network app-intern \
--restart unless-stopped \
-p 127.0.0.1:8080:8080 \
myapi:1.0
# Reverse-Proxy: veroeffentlicht nach aussen, leitet weiter
docker run -d --name proxy \
--network app-intern \
--restart unless-stopped \
-p 80:80 -p 443:443 \
-v /srv/proxy/conf:/etc/nginx/conf.d:ro \
nginx:alpine
# Ergebnis:
# Internet -> Proxy (Port 80/443) -> api:8080 -> db:5432
# Datenbank und Anwendung sind von aussen NICHT erreichbar.
# Nur der Proxy ist Angriffsflaeche.
# In der compose-Datei dasselbe Muster
# services:
# db:
# networks: [intern] # kein "ports"!
# api:
# networks: [intern]
# ports: ["127.0.0.1:8080:8080"]
# proxy:
# networks: [intern]
# ports: ["80:80", "443:443"]
Verwandte Grundlagen: Docker, Docker Compose, Docker-Grundlagen, Firewall.