Docker-Compose-Netzwerk-Kollision — wenn zwei Projekte sich in die Quere kommen

Incident
Montag, 14:17 Uhr — das Staging ist rot. Nicht nur eine Anwendung, sondern das gesamte Stack. docker compose ps zeigt alle Container als running an, aber sobald der API-Container die Datenbank anspricht, gibt es einen Connection refused. Ich logge mich in den Container ein, pinge db — und bekomme ping: db: Name or service not known. DNS-Auflösung funktioniert nicht. Merkwürdig: Am Freitagabend hat noch alles funktioniert.
Mein erster Gedanke: Der Datenbank-Container ist abgestürzt. Ein docker compose logs db später sehe ich aber, dass die Datenbank sauber läuft und auf Port 5432 lauscht. Also liegt es nicht am Dienst selbst. Ich wechsle in den API-Container und versuche, die IP-Adresse der Datenbank direkt zu erreichen. Die Auflösung über den Hostnamen schlägt fehl, ein nslookup db liefert ** server can't find db: NXDOMAIN. Auch nicht über tasks.db, den Compose-Servicenamen. Nichts.
In dem Moment wird mir klar: Es ist kein einzelner Container ausgefallen. Es ist das Netzwerk selbst.
Root Cause
Wir betreiben auf diesem Host zwei Compose-Projekte: die eigentliche Anwendung (app-stack) und ein separates Monitoring-Stack (monitoring). Beide Projekte haben in ihren docker-compose.yml-Dateien eine Verbindung zum Datenbankdienst konfiguriert — ohne dass wir eine explizite Netzwerkdefinition vorgenommen haben.
Docker Compose erstellt pro Projekt standardmäßig ein eigenes Bridge-Netzwerk mit dem Namen <projektverzeichnis>-default. Wenn zwei Projekte denselben Verzeichnisnamen haben oder wenn eines der Projekte nach einem docker system prune neu erstellt wird, ändert sich die interne Netzwerk-ID. Plötzlich befinden sich die Container in verschiedenen Broadcast-Domains. Sie können sich nicht mehr über den Servicenamen erreichen, weil DNS nur innerhalb eines Bridge-Netzwerks funktioniert.
Der Auslöser war in diesem Fall ein Kollege, der am Wochenende mit docker system prune --volumes aufgeräumt hat. Dabei wurde das bestehende app-stack_default-Netzwerk gelöscht und beim nächsten docker compose up mit einer neuen UUID neu erzeugt. Der zweite Stack (monitoring) hing noch im alten Netzwerk und konnte die Datenbank nicht mehr erreichen.
Das eigentliche Problem ist also nicht der prune-Befehl, sondern die fehlende explizite Netzwerkdefinition. Docker Compose erlaubt es, dass Services sich implizit im default-Netzwerk befinden — ohne dass der Name oder die Abhängigkeit dokumentiert ist.
Lösung
Der Fix besteht darin, das Netzwerk explizit zu benennen und bei Bedarf als external zu markieren, wenn mehrere Compose-Projekte darauf zugreifen sollen. Dadurch wird die Netzwerkidentität unabhängig von Container-Neustarts oder Aufräumaktionen stabil.
docker-compose.yml — explizite Netzwerkdefinition
services:
api:
image: app:latest
networks:
- app-net # API erhält expliziten Zugang zum gemeinsamen Netzwerk
depends_on:
- db
db:
image: postgres:16
networks:
- app-net # Datenbank ebenfalls explizit einbinden
networks:
app-net:
name: app-stack-net # Fester Name, unabhängig von Compose-Interna
driver: bridge
Für den zweiten Stack (monitoring) reicht ein Verweis auf das vorhandene Netzwerk:
services:
prometheus:
image: prom/prometheus
networks:
- app-net # Greift auf dasselbe Netzwerk wie der API-Container zu
networks:
app-net:
external: true # Netzwerk existiert bereits, nicht neu erstellen
Damit wird klar: Es gibt genau ein Netzwerk mit einem festen Namen, und alle beteiligten Container sind Mitglied in diesem Netzwerk. Ein docker compose down in einem Projekt löscht das Netzwerk nicht mehr, wenn es von einem anderen Projekt als external referenziert wird.
Verifikation
Nach dem Neustart prüfe ich die Netzwerkmitgliedschaft:
# Alle Container im gemeinsamen Netzwerk auflisten
docker network inspect app-stack-net --format '{{range .Containers}}{{.Name}} {{end}}'
Erwartete Ausgabe: Sowohl app-stack-api-1 als auch app-stack-db-1 (und ggf. monitoring-prometheus-1) erscheinen in der Liste.
Anschließend teste ich die Namensauflösung direkt:
# DNS-Auflösung aus einem Container heraus prüfen
docker compose exec api nslookup db
Erwartete Ausgabe: Eine gültige IP-Adresse, kein NXDOMAIN.
Abschließend ein funktionaler Test mit einem kleinen Go-Programm, das die Verbindung von der API zur Datenbank nachstellt:
package main
import (
"database/sql"
"fmt"
"log"
"os"
"time"
_ "github.com/jackc/pgx/v5/stdlib"
)
func main() {
dsn := os.Getenv("DATABASE_URL")
if dsn == "" {
dsn = "postgres://app:secret@db:5432/app?sslmode=disable"
}
db, err := sql.Open("pgx", dsn)
if err != nil {
log.Fatal(err)
}
defer db.Close()
db.SetConnMaxLifetime(3 * time.Second)
db.SetConnMaxIdleTime(1 * time.Second)
var now time.Time
if err := db.QueryRow("SELECT now()").Scan(&now); err != nil {
log.Fatalf("Datenbank nicht erreichbar: %v", err)
}
fmt.Printf("OK — Datenbank antwortet: %s\n", now.Format(time.RFC3339))
}
Starten mit:
docker compose exec api go run /tmp/check_db.go
Erwartete Ausgabe: OK — Datenbank antwortet: 2026-07-22T... — ohne Fehler.
Fazit
Die Lehre ist simpel: Verlasse dich nie auf Docks Standardnetzwerk, wenn mehr als ein Compose-Projekt auf denselben Dienst zugreifen muss. Ein expliziter Netzwerkname kostet drei Zeilen YAML und schützt vor unerwarteten Netzwerkverlusten nach Aufräumaktionen oder Container-Neustarts.
Ein weiterer Gewinn: Die Abhängigkeiten zwischen den Projekten sind im Code sichtbar. Wer das Repository klont, sieht sofort, dass monitoring auf das app-stack-net angewiesen ist — statt es nach einem Fehlschlag mühsam aus Logfiles rekonstruieren zu müssen.
Eine Frage an dich: Nutzt du in deinen Compose-Projekten bereits externe Netzwerke, oder verlässt du dich auf das default-Netzwerk? Und wenn ja: Hast du schon einmal einen Ausfall durch docker system prune erlebt?
Siehe auch: