edu@dorner-it
WerkzeugeFortgeschritten25 min

Docker Compose Fortgeschritten: Profiles, Overrides & Healthchecks

Mehrere Umgebungen, optionale Services und stabile Startreihenfolgen mit Compose: Profiles, Override-Dateien, depends_on mit Healthcheck-Conditions, Secrets, Watch-Mode und YAML-Anchors.

Zuletzt aktualisiert: 12. Mai 2026

docker-compose-basics hat dir gezeigt, wie du mehrere Container in einer docker-compose.yml zusammenführst. Sobald aber Dev, Test und Prod ins selbe Repo wollen — oder bestimmte Services nur manchmal laufen sollen — brauchst du die Werkzeuge aus diesem Kurs.

Lernziele

  • Du strukturierst Compose-Setups mit Profiles und Override-Dateien
  • Du startest Services in der richtigen Reihenfolge dank Healthchecks
  • Du nutzt Secrets, Watch-Mode und YAML-Anchors sinnvoll
  • Du verstehst, wann Compose endet und Kubernetes anfängt

Profiles — optionale Services

Manche Services sollen nicht immer starten — Tools wie pgadmin, ein Mock-SMTP-Server oder Lasttests sind nur situativ nötig.

services:
  api:
    image: myapp:dev
  db:
    image: postgres:16
 
  pgadmin:
    image: dpage/pgadmin4
    profiles: ["tools"]
 
  loadtest:
    image: grafana/k6
    profiles: ["perf"]
docker compose up                       # nur api + db
docker compose --profile tools up       # api + db + pgadmin
docker compose --profile perf up        # api + db + loadtest

Ein Service kann auch zu mehreren Profilen gehören. Ohne Profil gestartet wird er nur, wenn das Profil aktiv ist oder ein anderer Service ihn als depends_on referenziert.

Override-Dateien

Compose liest automatisch zwei Dateien: docker-compose.yml und — wenn vorhanden — docker-compose.override.yml. Die zweite ergänzt / überschreibt die erste.

# docker-compose.yml — Basis für alle
services:
  api:
    image: myapp
    environment:
      NODE_ENV: production
# docker-compose.override.yml — nur Dev, lokal nicht committen
services:
  api:
    build: .
    environment:
      NODE_ENV: development
    volumes:
      - ./src:/app/src
    command: npm run dev

Für andere Umgebungen explizit:

docker compose -f docker-compose.yml -f docker-compose.prod.yml up
flowchart LR
    Base[docker-compose.yml]
    Override[docker-compose.override.yml<br/><small>auto, lokal</small>]
    Prod[docker-compose.prod.yml<br/><small>explizit</small>]
    Test[docker-compose.test.yml<br/><small>explizit, CI</small>]

    Base --> Effective[Effektives Setup]
    Override -. "dev (default)" .-> Effective
    Prod -. "-f prod.yml" .-> Effective
    Test -. "-f test.yml" .-> Effective

Konvention: docker-compose.override.yml in .gitignore lassen. Eine Vorlage als docker-compose.override.yml.example committen.

Healthchecks + depends_on mit Condition

depends_on allein wartet nur, bis der Prozess gestartet ist — nicht, bis der Service bereit ist. Mit Healthcheck-Conditions geht es richtig:

services:
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: secret
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 3s
      retries: 10
      start_period: 5s
 
  api:
    image: myapp
    depends_on:
      db:
        condition: service_healthy
condition Bedeutung
service_started Default — nur „läuft"
service_healthy Wartet auf grünen Healthcheck
service_completed_successfully Für One-Shot-Jobs (Migration)

Migrations-Pattern: Ein migrate-Service mit restart: "no" läuft einmal, und api wartet mit service_completed_successfully darauf.

Secrets in Compose

Niemals Secrets in environment: schreiben (die landen in docker inspect, Logs, Histories). Mit Compose-Secrets bekommen sie als Datei in den Container:

services:
  api:
    image: myapp
    environment:
      DB_PASSWORD_FILE: /run/secrets/db_password
    secrets:
      - db_password
 
secrets:
  db_password:
    file: ./secrets/db_password.txt

Die App liest dann /run/secrets/db_password. Im Dev-Workflow kann die Datei lokal liegen; in Prod kommen die Secrets aus Vault / SOPS / Env.

YAML-Anchors für DRY-Configs

Mehrere ähnliche Services? Anchors (&) und Aliase (*) helfen:

x-defaults: &api-defaults
  image: myapp:dev
  environment:
    NODE_ENV: development
  depends_on:
    - db
 
services:
  api-eu:
    <<: *api-defaults
    environment:
      REGION: eu
  api-us:
    <<: *api-defaults
    environment:
      REGION: us

Der x--Prefix sorgt dafür, dass Compose den Block ignoriert — er ist nur ein Anchor-Container.

Compose Watch — Hot-Reload ohne Bind-Mounts

Statt das ganze Quellverzeichnis in den Container zu mounten (was auf macOS/Windows langsam ist), kann Compose Dateiänderungen erkennen und gezielt reagieren:

services:
  web:
    build: .
    develop:
      watch:
        - action: sync
          path: ./src
          target: /app/src
        - action: rebuild
          path: ./package.json
docker compose watch

Ergebnis: schneller, plattform-unabhängiger, und der package.json- Change löst automatisch einen Rebuild aus.

Resource Limits

Compose kennt deploy.resources (auch ohne Swarm):

services:
  api:
    image: myapp
    deploy:
      resources:
        limits:
          cpus: "1.5"
          memory: 512M
        reservations:
          cpus: "0.25"
          memory: 128M

Praktisch, um lokal realistische Bedingungen zu testen — bevor du in Kubernetes feststellst, dass deine App 800 MB zieht.

Networks — gezielt segmentieren

Standard: Compose erzeugt ein Netzwerk pro Projekt. Mehrere Netzwerke helfen, Services voneinander zu isolieren:

services:
  api:
    networks: [frontend, backend]
  web:
    networks: [frontend]
  db:
    networks: [backend]   # nicht von web erreichbar
 
networks:
  frontend:
  backend:

Ein externes Netzwerk teilen (z. B. mit einem zweiten Compose-Projekt):

networks:
  shared:
    external: true

Compose in CI

Compose ist auch in der CI nützlich — für Integrationstests gegen echte Abhängigkeiten:

# docker-compose.test.yml
services:
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: ci
    tmpfs: /var/lib/postgresql/data   # schneller, flüchtig
 
  tests:
    build: .
    command: npm test
    depends_on:
      db:
        condition: service_healthy
docker compose -f docker-compose.test.yml run --rm tests

run --rm startet den tests-Service einmalig und räumt auf — perfekt für CI-Pipelines.

Häufige Stolperfallen

depends_on startet trotzdem zu früh"

Du hast service_started (Default) statt service_healthy — und der DB-Container braucht 4 Sekunden bis Postgres lauscht. Healthcheck + Condition setzen.

„Mein Override greift nicht"

docker compose config ausführen — das zeigt das effektive Resultat aller Dateien zusammen. Häufig: falsche Verschachtelung oder ein Tippfehler im Service-Namen.

„Volumes überleben Neustarts nicht"

Wahrscheinlich ein Bind-Mount statt Named Volume, oder docker compose down -v wurde aufgerufen (löscht Volumes explizit).

„Profile-Service startet, obwohl ich ihn nicht will"

Ein anderer Service hat ihn als depends_on. Profile deaktivieren einen Service nicht hart — sie schließen ihn nur aus der automatischen Auswahl aus.

Wo Compose endet, Kubernetes anfängt

Compose ist für einen Host gedacht. Brauchst du Ausfallsicherheit über mehrere Maschinen, Autoscaling, Rollouts, Self-Healing über Host- Grenzen hinweg — dann ist es Zeit für Kubernetes (siehe nächste Kursreihe).

Feature Compose Kubernetes
Single-Host Orchestrierung Stark Möglich, aber overkill
Multi-Host Scheduling Nein Kernfeature
Self-Healing / Replicas Begrenzt Kernfeature
Lokale Entwicklung Top Mit Kind/Minikube ok
Lernkurve Flach Steil

Praxis-Übung

Erweitere ein bestehendes Compose-Setup:

  1. Lege ein tools-Profile an (z. B. pgadmin oder redis-commander).
  2. Splitte in docker-compose.yml (Basis) und docker-compose.override.yml (Dev).
  3. Ergänze einen Healthcheck für die Datenbank und condition: service_healthy an einem abhängigen Service.
  4. Verschiebe ein Passwort aus environment in einen Compose-Secret.
  5. Probier docker compose watch statt klassischer Bind-Mounts.

Weiterführendes