Docker Compose Best Practices
Einleitung
Docker Compose ist nach wie vor der schnellste Weg, eine Multi-Container-Anwendung auf einem einzelnen Host zu betreiben — aber viele der noch kursierenden Ratschläge beschreiben ein Tool, das es so nicht mehr gibt. Zwei Dinge gilt es zuerst zu verlernen: Compose v2 kommt als Docker-CLI-Plugin, wird als docker compose (ohne Bindestrich) aufgerufen und mit Docker Desktop oder dem Paket docker-compose-plugin installiert — das eigenständige docker-compose-v1-Binary erhält seit Juli 2023 keine Updates mehr; jedes Tutorial, das Sie es herunterladen und per chmod ausführbar machen lässt, ist also veraltet. Und der version:-Schlüssel am Anfang der Datei ist obsolet: Die zusammengeführte Compose Specification hat die alten v2/v3-Dateiformate abgelöst, und aktuelles Compose gibt eine Warnung aus, wenn der Schlüssel vorhanden ist. Nennen Sie die Datei compose.yaml und beginnen Sie direkt mit services:.
Ein funktionierendes Beispiel
services:
app:
image: ghcr.io/acme/shop-api:1.8.3
restart: unless-stopped
environment:
DATABASE_URL: postgres://shop:${DB_PASSWORD:?set in .env}@db:5432/shop
ports:
- "8080:8080"
depends_on:
db:
condition: service_healthy
db:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_USER: shop
POSTGRES_DB: shop
POSTGRES_PASSWORD: ${DB_PASSWORD:?set in .env}
volumes:
- db-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U shop -d shop"]
interval: 5s
timeout: 3s
retries: 10
adminer:
image: adminer:4
ports:
- "8081:8080"
profiles: ["debug"]
volumes:
db-data:
Die Praktiken, auf die es ankommt
1. depends_on mit einem Healthcheck kombinieren
Das ist die klassische Compose-Falle: depends_on allein steuert nur die Start-Reihenfolge — es wartet nicht darauf, dass die Abhängigkeit bereit ist; die App liefert sich also weiterhin ein Wettrennen mit einem Postgres, das noch keine Verbindungen annimmt. Die Lösung zeigt das Beispiel: Geben Sie der Abhängigkeit einen healthcheck und deklarieren Sie am abhängigen Service condition: service_healthy. Compose hält die App dann so lange zurück, bis die Datenbank tatsächlich antwortet.
2. Bei fehlenden Umgebungsvariablen sofort scheitern
Compose interpoliert ${VARIABLEN} aus der Shell und aus einer .env-Datei neben der Compose-Datei. Verwenden Sie für alles Erforderliche die Form ${VAR:?message}, damit ein fehlendes Secret den Start mit einer klaren Fehlermeldung abbricht, statt einen halb konfigurierten Stack zu erzeugen. Halten Sie .env aus der Versionskontrolle heraus; committen Sie stattdessen eine .env.example.
3. Profile für optionale Services nutzen
Debug-Tooling wie der adminer-Service oben muss nicht bei jedem docker compose up mitlaufen. Weisen Sie ihm ein Profil zu und aktivieren Sie es nur bei Bedarf: docker compose --profile debug up. Profile lassen eine einzige Datei mehrere Workflows bedienen, ohne dass Blöcke ein- und auskommentiert werden müssen.
4. Umgebungen mit Override-Dateien trennen
Halten Sie compose.yaml als neutrale Basis. Compose merged compose.override.yaml automatisch — der richtige Ort für reine Entwicklungsbelange wie build:-Abschnitte, Bind-Mounts und freigegebene Debugger-Ports. Für andere Umgebungen übergeben Sie die Dateien explizit (docker compose -f compose.yaml -f compose.prod.yaml up -d) und prüfen das zusammengeführte Ergebnis mit docker compose config. Dieses Layering hat die meisten historischen Einsätze von extends abgelöst.
5. In Produktion versionierte Images pullen, lokal bauen
Ein pauschales „nutzen Sie build: statt Registry-Images“ ist für alles jenseits des Laptops verkehrt herum. Die CI sollte das Image einmal bauen und in eine Registry pushen; jedes Deployment pullt anschließend denselben gepinnten Tag — das Beispiel pinnt shop-api:1.8.3, niemals :latest. Reservieren Sie build: für die Dev-Override-Datei. Die Wahl und Härtung dieser Registry ist ein eigenes Thema — siehe unseren Leitfaden Wo Docker-Images speichern?.
6. Named Volumes für State verwenden
Named Volumes wie db-data überstehen ein docker compose down und werden sauber neu angelegt; außerdem halten sie Datenbank-Interna aus Ihrem Projektverzeichnis heraus. Reservieren Sie Bind-Mounts für Quellcode in der Entwicklung.
Wann der Wechsel zu Kubernetes ansteht
Compose ist ein Single-Host-Tool, und das ist ein Feature: Für einen einzelnen Server schlägt es einen Cluster in puncto Einfachheit jedes Mal. Die ehrlichen Auslöser für den Wechsel sind Anforderungen, die Compose strukturell nicht erfüllen kann — Hochverfügbarkeit über Hosts hinweg, Autoscaling, Rolling Deployments mit automatisiertem Rollback und erstklassiges Secret-Management. Tauchen diese Punkte auf Ihrer Roadmap auf, lesen Sie vor der Entscheidung unsere Notizen zum Kubernetes-Betrieb im echten Leben; die operative Rechnung ist real. Unser DevOps-Team hilft Teams regelmäßig bei der Einschätzung, auf welcher Seite dieser Linie sie stehen — und viele Produktions-Workloads laufen völlig zu Recht weiterhin auf Compose.
Das in Produktion betreiben? Unser AWS-Managed-Services-Team betreibt solche Umgebungen rund um die Uhr — Monitoring, Incident Response, Patching und Kostenkontrolle durch namentlich benannte Senior Engineers, ab 3.000 €/Monat.