Docker Compose · Profiles · Dev · Test · CI/CD
Docker Compose Profiles für Dev, Test und CI
eine Compose-Datei für alle Umgebungen

Wer für Dev, Test und CI drei separate Compose-Dateien pflegt, schreibt Konfiguration dreifach und vergisst regelmäßig, Änderungen zu synchronisieren. Docker Compose Profiles lösen dieses Problem: Services werden nur dann gestartet, wenn das passende Profil aktiv ist – eine Datei, alle Umgebungen.

13 Min. Lesezeit profiles · --profile · COMPOSE_PROFILES · depends_on · service_completed_successfully Docker Compose v2 · GitHub Actions · GitLab CI

1. Das Problem mit mehreren Compose-Dateien

Das klassische Muster für mehrere Umgebungen in Docker Compose lautet: docker-compose.yml als Basis, docker-compose.dev.yml für die Entwicklung und docker-compose.ci.yml für CI – dann kombiniert über docker compose -f docker-compose.yml -f docker-compose.dev.yml up. Dieses Muster funktioniert, aber es hat einen fundamentalen Nachteil: Jede Änderung an einem Service muss manuell in allen Dateien synchronisiert werden. Ein neuer Volume-Mount, eine geänderte Umgebungsvariable oder ein neues Health-Check-Intervall – jede dieser Änderungen wird in der Praxis oft nur in der Hauptdatei gemacht und in den Override-Dateien vergessen.

Docker Compose Profiles, eingeführt mit Compose v2, lösen dieses Problem grundlegend anders: Statt Services in separaten Dateien zu definieren, werden sie in einer einzigen Datei mit einem profiles-Schlüssel versehen. Services mit Profil werden nur gestartet, wenn das Profil über --profile oder die Umgebungsvariable COMPOSE_PROFILES aktiviert wird. Services ohne Profil starten immer. Das Ergebnis ist eine einzige, vollständige compose.yml, die für alle Umgebungen funktioniert – ohne Synchronisierungsaufwand und ohne Merge-Konflikte.

2. Docker Compose Profiles: Grundlagen und Syntax

Die Syntax für Docker Compose Profiles ist einfach: der profiles-Schlüssel in einem Service-Block nimmt eine Liste von Profil-Namen als Strings. Ein Service kann mehreren Profilen zugeordnet sein. Services ohne profiles-Schlüssel gehören zum Standard-Profil und starten immer, unabhängig davon, welche Profile aktiv sind. Das ist das wichtigste Konzept: Kernservices wie die Datenbank und der Web-Server sind immer aktiv, optionale Services wie Mailhog, Seed-Container oder Monitoring-Exporter sind nur profilgebunden.

Profile werden über den --profile-Parameter beim docker compose up-Befehl aktiviert: docker compose --profile dev up startet alle Services ohne Profil plus alle Services, die das dev-Profil haben. Mehrere Profile können gleichzeitig aktiv sein: docker compose --profile dev --profile debug up. Alternativ setzt man die Umgebungsvariable COMPOSE_PROFILES=dev,debug in einer .env-Datei oder in der Shell, dann braucht man den Flag beim Befehl nicht. Das macht Docker Compose Profiles ideal für automatisierte Umgebungen, wo Umgebungsvariablen pro Kontext gesetzt werden.


# compose.yml — single file for all environments using Docker Compose Profiles

services:
  # Core services: always started (no profiles key)
  app:
    image: my-php-app:${APP_VERSION:-latest}
    depends_on:
      db:
        condition: service_healthy
    environment:
      - APP_ENV=${APP_ENV:-production}

  db:
    image: mysql:8.4
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${MYSQL_DATABASE:-app}
    volumes:
      - db-data:/var/lib/mysql
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      retries: 5

  # Dev-only: email testing tool — started only with --profile dev
  mailhog:
    image: mailhog/mailhog:latest
    profiles: [dev]
    ports:
      - "8025:8025"

  # Dev-only: database admin UI
  adminer:
    image: adminer:latest
    profiles: [dev]
    ports:
      - "8080:8080"

volumes:
  db-data:

3. Das Dev-Profil: Mailhog, Adminer und Debug-Tools

Das dev-Profil in Docker Compose Profiles ist für Werkzeuge gedacht, die ausschließlich in der lokalen Entwicklung sinnvoll sind und in CI oder Produktion nicht gebraucht werden. Die typischen Kandidaten sind: Mailhog oder Mailpit für das Abfangen von E-Mails (statt echte SMTP-Server zu konfigurieren), Adminer oder phpMyAdmin für die Datenbankadministration, Redis Commander oder RedisInsight für das Debugging des Redis-Inhalts, und ggf. ein lokaler S3-Ersatz wie MinIO oder LocalStack für AWS-S3-Integrationen. All diese Services kosten in Produktion und CI unnötige Ressourcen und erhöhen die Angriffsfläche.

Ein wichtiges Detail bei Docker Compose Profiles für das Dev-Profil: Wenn ein Service mit Profil einen Port nach außen belegte, ist er in CI nicht gestartet und belegt den Port nicht. Das vermeidet Port-Konflikte auf CI-Runnern, die häufig mehrere parallele Pipeline-Jobs ausführen. Außerdem gibt es keinen Grund, den Mailhog-Container in CI zu starten, wenn Tests ohnehin keine E-Mails verschicken sollen. Das dev-Profil kann auch Volumes mit Quellcode-Mounts enthalten, die in CI nicht gemountet werden – ein häufiger Performancegewinn in CI-Pipelines.

4. Das Test-Profil: Seed-Container und Fixtures

Das test-Profil in Docker Compose Profiles ist für Hilfsdienste gedacht, die nur für die Testausführung benötigt werden. Der klassische Anwendungsfall ist ein Seed-Container, der die Datenbank mit Testdaten befüllt, bevor die Tests starten. Mit service_completed_successfully als Condition in depends_on kann der Test-Runner darauf warten, dass der Seed-Container erfolgreich abgeschlossen hat, bevor er selbst startet.

Ein weiteres typisches Beispiel für Docker Compose Profiles mit dem Test-Profil: eine dedizierte Testdatenbank, die bei jedem Testlauf frisch initialisiert wird. Statt der Produktionsdatenbank (die in ihrem eigenen Service läuft) wird eine separate MySQL-Instanz mit einem anderen Port und einem anderen Volume gestartet – nur wenn das Test-Profil aktiv ist. Die Tests laufen gegen diese isolierte Testdatenbank und hinterlassen keinen Zustand in der Entwicklungs- oder Produktionsdatenbank. Nach dem Testlauf kann die Testdatenbank mit docker compose --profile test down -v einschließlich ihrer Daten entfernt werden.


# Test and CI profile additions to compose.yml

  # Test profile: isolated test database (separate from dev DB)
  db-test:
    image: mysql:8.4
    profiles: [test, ci]
    environment:
      MYSQL_ROOT_PASSWORD: test
      MYSQL_DATABASE: app_test
    tmpfs:
      # Use tmpfs for test DB — faster I/O, no persistence needed
      - /var/lib/mysql
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 5s
      retries: 10

  # Test profile: seed container that runs once and exits
  db-seed:
    image: my-php-app:${APP_VERSION:-latest}
    profiles: [test, ci]
    command: ["php", "bin/console", "doctrine:fixtures:load", "--no-interaction"]
    depends_on:
      db-test:
        condition: service_healthy
    environment:
      DATABASE_URL: mysql://root:test@db-test/app_test

  # Test runner: waits for seed to complete before running
  test-runner:
    image: my-php-app:${APP_VERSION:-latest}
    profiles: [test, ci]
    command: ["vendor/bin/phpunit", "--testdox"]
    depends_on:
      db-seed:
        condition: service_completed_successfully
    environment:
      DATABASE_URL: mysql://root:test@db-test/app_test

5. Das CI-Profil: schlanke Services ohne Dev-Overhead

Das ci-Profil in Docker Compose Profiles hat eine andere Anforderung als das dev-Profil: CI-Umgebungen brauchen oft Services, die in der lokalen Entwicklung nicht gebraucht werden, und umgekehrt. In CI sind Volume-Mounts des Quellcodes (live reload) sinnlos – der Code ist bereits im Image. Debug-Tools wie Adminer sind nicht nötig. Aber ein dedizierter Test-Reporting-Service oder eine Integration mit einer Testabdeckungs-Datenbank könnte im CI-Profil nützlich sein.

Ein wichtiger Anwendungsfall für Docker Compose Profiles in CI ist das Steuern von Service-Konfigurationsunterschieden ohne Override-Dateien. In CI könnte die Applikation mit einem anderen APP_ENV-Wert starten, ohne dass dafür eine separate Compose-Datei nötig ist. Mit Profiles und der COMPOSE_PROFILES-Umgebungsvariable, die in der CI-Pipeline gesetzt wird, wird das Profil automatisch aktiviert. Die Pipeline braucht keine speziellen Compose-Befehle – docker compose up -d reicht, und die richtige Konfiguration wird über die Umgebungsvariable gesteuert.

6. depends_on mit Conditions und Profile kombinieren

Die Kombination aus Docker Compose Profiles und depends_on mit Conditions ermöglicht komplexe Service-Abhängigkeitsgraphen, die umgebungsabhängig gelten. Seit Compose v2 kennt depends_on drei Conditions: service_started (Container läuft, kein Health-Check-Wait), service_healthy (Health-Check ist grün), und service_completed_successfully (Container ist mit Exit-Code 0 beendet). Die letzte Condition ist besonders wertvoll für Migrationslauf- und Seed-Container in Test- und CI-Profilen.

Eine wichtige Einschränkung bei Docker Compose Profiles und depends_on: Wenn Service B auf Service A wartet, und Service A einem Profil zugeordnet ist, das nicht aktiv ist, dann schlägt der Start von Service B fehl. Das ist eine häufige Fehlerquelle, wenn man profilegebundene Services als Abhängigkeiten von Services ohne Profil verwendet. Die Lösung: Kernservices (ohne Profil) dürfen nur von anderen Kernservices abhängen. Services mit Profil können von Kernservices oder von anderen Services desselben Profils abhängen.


# Activate profiles via environment variable (ideal for CI)
# In .env.ci file:
COMPOSE_PROFILES=ci
APP_ENV=testing
MYSQL_ROOT_PASSWORD=ci-secret
MYSQL_DATABASE=app_test

# In CI pipeline (GitHub Actions / GitLab CI):
# Copy the CI env file and start with profiles
cp .env.ci .env
docker compose up -d

# Or explicitly with --profile flag:
docker compose --profile ci up -d

# Wait for test runner to complete and capture exit code
docker compose --profile ci run --rm test-runner
TEST_EXIT_CODE=$?

# Cleanup after tests (remove test volumes too)
docker compose --profile ci down --volumes

exit $TEST_EXIT_CODE

# Run only the dev profile (local development)
COMPOSE_PROFILES=dev docker compose up -d
# Or:
docker compose --profile dev up -d

7. COMPOSE_PROFILES und .env-Dateien pro Umgebung

Die eleganteste Art, Docker Compose Profiles umgebungsabhängig zu aktivieren, ist die Kombination aus COMPOSE_PROFILES in einer .env-Datei und einer umgebungsspezifischen .env.*-Datei, die vor dem Compose-Befehl geladen wird. Compose lädt automatisch eine .env-Datei aus dem aktuellen Verzeichnis. Wenn man unterschiedliche .env.dev, .env.ci und .env.test-Dateien anlegt und mit cp .env.dev .env oder Symlinks aktiviert, hat man eine klare, nachvollziehbare Konfigurationsverwaltung.

Für lokale Entwicklung bietet sich ein kleines Hilfsskript an, das die richtige .env-Datei setzt und Compose startet. Wer Docker Compose Profiles in einem Team einsetzt, sollte die .env.*-Dateien (ohne Secrets) ins Repository einchecken und die .env-Datei in .gitignore aufnehmen. So hat jeder Entwickler dieselbe Ausgangsbasis und kann die Umgebung mit einem einzigen Befehl wechseln. Secrets werden separat über tatsächliche Umgebungsvariablen oder Docker Secrets verwaltet.

8. Profiles in GitHub Actions und GitLab CI einsetzen

In GitHub Actions werden Docker Compose Profiles am saubersten über die Umgebungsvariable COMPOSE_PROFILES aktiviert. Im Workflow YAML setzt man sie im env-Block des Jobs oder Steps: COMPOSE_PROFILES: ci. Docker Compose liest diese Variable automatisch, sodass docker compose up -d ohne expliziten --profile-Flag auskommt. Das macht den Workflow lesbarer und den Compose-Befehl selbst unverändert – nur die Umgebungsvariable entscheidet, welche Services gestartet werden.

In GitLab CI ist das Muster ähnlich: die COMPOSE_PROFILES-Variable wird in der variables-Sektion des Jobs gesetzt. Für unterschiedliche Pipeline-Stages kann die Variable unterschiedliche Werte haben – in der Build-Stage keine Profiles, in der Test-Stage ci,test. Das macht Docker Compose Profiles zu einem leistungsstarken Werkzeug für mehrstufige CI/CD-Pipelines, ohne dass separate Compose-Dateien gepflegt werden müssen. Ein häufiger Vorteil in der Praxis: CI-Runner brauchen keine Quellcode-Volume-Mounts, die die Build-Zeit erhöhen – das Dev-Profil mit diesen Mounts wird in CI einfach nicht aktiviert.

9. Profile-Strategien im Vergleich

Es gibt verschiedene Ansätze, um Compose-Konfigurationen für unterschiedliche Umgebungen zu verwalten. Docker Compose Profiles sind eine von mehreren Möglichkeiten und haben klare Stärken und Grenzen.

Strategie Vorteil Nachteil Beste Anwendung
Compose Profiles Eine Datei, Service-Selektivierung Keine Konfigurationsüberschreibung möglich Optionale Services pro Umgebung
Override-Dateien (-f) Service-Konfiguration überschreibbar Mehrere Dateien zu synchronisieren Port/Volume-Änderungen pro Umgebung
Separate Dateien Maximale Isolation Hoher Wartungsaufwand, Duplikation Radikale Umgebungsunterschiede
Env-Variablen Keine Datei-Komplexität Keine selektiven Services Konfigurationswerte variieren
Profiles + Override Beste Kombination Etwas mehr Komplexität Reale Produktionsprojekte

In der Praxis empfiehlt sich die Kombination: Docker Compose Profiles für die Selektivierung von Services (Mailhog nur in Dev, Seed nur in CI) und eine kleine Override-Datei für echte Konfigurationsunterschiede (andere Ports in CI, andere Volume-Mounts in Dev). Diese Kombination gibt die Flexibilität beider Ansätze ohne deren Nachteile.

Mironsoft

Docker Compose, CI/CD-Integration und Entwicklungsumgebungen

Saubere Docker-Umgebungen für Dev, Test und CI?

Wir strukturieren Docker-Compose-Setups mit Profiles, konfigurierbaren .env-Dateien und klaren Service-Abhängigkeiten – sodass Entwickler sofort starten können und CI automatisch das Richtige tut.

Compose-Redesign

Mehrere Compose-Dateien konsolidieren und mit Profiles sauber strukturieren

CI-Integration

GitHub Actions und GitLab CI mit Compose Profiles und Testdatenbank-Isolation einrichten

Team-Onboarding

Entwicklungsumgebung so einrichten, dass neue Teammitglieder in Minuten starten können

10. Zusammenfassung

Docker Compose Profiles sind das Werkzeug der Wahl, wenn man Services selektiv für unterschiedliche Umgebungen aktivieren will, ohne mehrere Compose-Dateien zu pflegen. Services ohne profiles-Schlüssel starten immer, profilgebundene Services nur wenn das Profil über --profile oder COMPOSE_PROFILES aktiviert ist. Das Dev-Profil bündelt Werkzeuge wie Mailhog und Adminer, die in CI und Produktion nicht benötigt werden. Das Test-Profil bündelt Seed-Container und Testdatenbanken. Das CI-Profil kann CI-spezifische Services und eine schlankere Konfiguration aktivieren.

Die Kombination aus Docker Compose Profiles für Service-Selektivierung und .env.*-Dateien für Konfigurationswerte pro Umgebung ist das vollständige Muster für reale Projekte. depends_on mit service_completed_successfully ermöglicht Seed-Container, die vor dem Test-Runner fertig sein müssen. Das häufigste Antipattern bei Docker Compose Profiles ist es, einen Kernservice (ohne Profil) von einem profilgebundenen Service abhängig zu machen – das führt zu Fehlern, wenn das Profil nicht aktiv ist.

Docker Compose Profiles — Das Wichtigste auf einen Blick

Syntax

profiles: [dev] im Service-Block. Services ohne profiles: starten immer. Aktivierung via --profile dev oder COMPOSE_PROFILES=dev.

Dev-Profil

Mailhog, Adminer, Debug-Tools. Nicht in CI, nicht in Produktion. Ports und Volumes, die lokal gebraucht werden.

Test/CI-Profil

Seed-Container mit service_completed_successfully, isolierte Testdatenbank auf tmpfs, Test-Runner.

Wichtiges Antipattern

Niemals Kernservice (kein Profil) von profilgebundenem Service abhängig machen – führt zu Fehlern wenn Profil nicht aktiv.

11. FAQ: Docker Compose Profiles für Dev, Test und CI

1Was sind Docker Compose Profiles?
Services mit profiles-Schlüssel werden nur gestartet, wenn das Profil via --profile oder COMPOSE_PROFILES aktiv ist. Services ohne profiles starten immer.
2Profil aktivieren?
docker compose --profile dev up oder COMPOSE_PROFILES=dev docker compose up. Mehrere Profile: --profile dev --profile debug.
3Service in mehreren Profilen?
Ja. profiles: [dev, test] startet den Service wenn dev oder test aktiv ist.
4Profiles vs. Override-Dateien?
Profiles selektieren Services. Override-Dateien überschreiben Konfigurationen. Kombination beider ist das vollständige Muster.
5Profiles in GitHub Actions?
env: COMPOSE_PROFILES: ci im Workflow-YAML. Kein --profile Flag im docker compose-Befehl nötig.
6service_completed_successfully?
Wartet bis Service mit Exit-Code 0 beendet ist. Ideal für Seed-Container: Test-Runner startet erst nach erfolgreichem Seed.
7Häufigstes Antipattern?
Kernservice (kein Profil) hängt per depends_on von profilgebundenem Service ab. Fehler wenn Profil nicht aktiv ist.
8Konfigurationswerte pro Umgebung?
Separate .env.dev, .env.ci, .env.test Dateien. Aktive per cp aktivieren. COMPOSE_PROFILES darin setzen.
9Testdatenbank auf tmpfs?
profiles: [test, ci] + tmpfs: [/var/lib/mysql]. Schnelle I/O ohne Persistence, nur in Test/CI aktiv.
10COMPOSE_PROFILES aus .env gelesen?
Ja. Docker Compose lädt .env automatisch. Shell-Variablen überschreiben .env-Werte.