Parallele Feature-Umgebungen mit Compose-Projektnamen und Profiles
AI generated
FROM
RUN
Docker Compose · Feature-Branches · Isolation
Parallele Feature-Umgebungen
mit Compose-Projektnamen und Profiles

Wer mehrere Feature-Branches gleichzeitig prüfen muss, stößt schnell an die Grenzen eines einzelnen Docker-Compose-Stacks: Portkonflikte, geteilte Datenbanken und Namenskollisionen bei Containern und Netzwerken. COMPOSE_PROJECT_NAME in Kombination mit Profiles löst genau dieses Problem, indem jede Umgebung sauber isoliert nebeneinander läuft.

15 Min. Lesezeit COMPOSE_PROJECT_NAME · Profiles · Dynamische Ports Docker Compose · Git-Branches

1. Das Problem: ein Stack, mehrere Feature-Branches

Sobald mehrere Feature-Branches parallel entwickelt werden, reicht ein einzelner Docker-Compose-Stack nicht mehr aus. Ein klassisches Szenario: Entwickler A arbeitet an einem Checkout-Feature, Entwickler B gleichzeitig an einer Produktimport-Änderung, beide brauchen einen laufenden Shop-Stack mit eigener Datenbank, aber keiner möchte den anderen versehentlich mit Testdaten überschreiben. Ohne Isolation zwischen den Branches teilen sich beide dieselben Container-Namen, dieselben Ports und dieselbe Datenbank, was zu ständigen Konflikten führt.

Der naheliegende, aber unpraktische Ausweg ist, für jeden Branch einen eigenen Rechner oder eine eigene virtuelle Maschine zu betreiben. Deutlich effizienter ist es, mehrere parallele Feature-Umgebungen auf demselben Rechner laufen zu lassen, jede vollständig isoliert über einen eigenen Compose-Projektnamen. So lassen sich mehrere Branches gleichzeitig bauen, testen und manuell prüfen, ohne dass sich die Umgebungen gegenseitig stören.

Dieser Artikel zeigt, wie parallele Feature-Umgebungen mit COMPOSE_PROJECT_NAME, dynamischer Portvergabe und Compose Profiles technisch sauber umgesetzt werden, inklusive eines Automatisierungsskripts, das die Umgebung direkt aus dem aktuellen Git-Branch-Namen ableitet.

2. COMPOSE_PROJECT_NAME als Isolationsmechanismus

Docker Compose nutzt intern einen Projektnamen, um Container, Netzwerke und Volumes eindeutig zu benennen. Standardmäßig wird dieser Name aus dem Verzeichnisnamen abgeleitet, was bei einem einzigen Checkout des Repositories unproblematisch ist, aber bei mehreren parallelen Checkouts desselben Projekts zu Kollisionen führt. Die Umgebungsvariable COMPOSE_PROJECT_NAME überschreibt diesen automatischen Namen explizit und ist der zentrale Baustein für parallele Feature-Umgebungen.

Wird der Projektname beispielsweise aus dem Branch-Namen abgeleitet, etwa shop-feature-checkout statt schlicht shop, erzeugt Docker Compose automatisch eindeutige Container-, Netzwerk- und Volume-Namen mit diesem Präfix. Zwei Checkouts desselben Repositories auf unterschiedlichen Branches können damit gleichzeitig docker compose up ausführen, ohne dass Docker die Ressourcen des jeweils anderen Branches überschreibt oder wiederverwendet.


# .env file per checkout, derived from the current branch
COMPOSE_PROJECT_NAME=shop-feature-checkout

# Running compose now creates isolated resources:
# Network:    shop-feature-checkout_default
# Volume:     shop-feature-checkout_db_data
# Container:  shop-feature-checkout-app-1
docker compose up -d

# A second checkout on a different branch uses a different name
# and never collides with the first environment
export COMPOSE_PROJECT_NAME=shop-feature-import
docker compose up -d

Wichtig für parallele Feature-Umgebungen: der Projektname muss pro Checkout-Verzeichnis konsistent gesetzt sein, sonst verwendet Docker Compose bei jedem Aufruf wieder den Verzeichnisnamen als Fallback. Am zuverlässigsten ist eine projektlokale .env-Datei im jeweiligen Checkout, die nicht versioniert wird und pro Branch individuell erzeugt wird.

3. Dynamische Portvergabe pro Branch

Neben eindeutigen Namen brauchen parallele Feature-Umgebungen auch eindeutige Ports, sonst blockiert die zuerst gestartete Umgebung den Port für alle weiteren. Statt feste Ports wie 8080:80 in der Compose-Datei zu hinterlegen, referenziert man Umgebungsvariablen, die pro Checkout unterschiedlich gesetzt werden. Ein einfacher, aber robuster Ansatz berechnet den Port aus einem Hash des Branch-Namens, sodass jeder Branch reproduzierbar denselben Port erhält, ohne dass Entwickler sich Portnummern merken müssen.


# docker-compose.yml — ports come from environment variables, not hardcoded
services:
  app:
    build: .
    ports:
      - "${APP_PORT:-8080}:80"
  db:
    image: mysql:8.4
    ports:
      - "${DB_PORT:-3306}:3306"
    environment:
      MYSQL_ROOT_PASSWORD: secret

#!/usr/bin/env bash
# derive-ports.sh — deterministic port offset from branch name
set -euo pipefail

branch="$(git rev-parse --abbrev-ref HEAD)"
# Hash the branch name into a stable offset between 0 and 999
offset=$(( 0x$(echo -n "$branch" | md5sum | cut -c1-4) % 1000 ))

echo "APP_PORT=$((8080 + offset))"
echo "DB_PORT=$((13306 + offset))"

Dieses Skript liefert für jeden Branch-Namen einen reproduzierbaren, aber eindeutigen Offset, sodass feature-checkout immer denselben Port erhält, egal wie oft die Umgebung neu gestartet wird, während feature-import garantiert einen anderen Port bekommt. Für parallele Feature-Umgebungen ist diese Determinismus-Eigenschaft wichtig, weil sie Lesezeichen im Browser und Notizen im Team-Chat über die Zeit gültig hält.

4. Profiles mit Projektnamen für Team-spezifische Stacks kombinieren

Compose Profiles erlauben, Services optional zu machen, sodass ein Service nur startet, wenn sein Profil explizit aktiviert wird. In Kombination mit COMPOSE_PROJECT_NAME entsteht daraus ein mächtiges Muster für parallele Feature-Umgebungen: jeder Branch bekommt nicht nur eigene Container-Namen und Ports, sondern kann zusätzlich selbst entscheiden, welche optionalen Services er überhaupt braucht. Ein Feature-Branch, der nur Backend-Änderungen testet, muss beispielsweise keinen Elasticsearch-Container starten, während ein Branch mit Suchfunktion-Änderungen genau dieses Profil aktiviert.


# docker-compose.yml — optional services gated behind profiles
services:
  app:
    build: .
    ports:
      - "${APP_PORT:-8080}:80"

  db:
    image: mysql:8.4
    ports:
      - "${DB_PORT:-3306}:3306"

  search:
    image: elasticsearch:8.15.0
    profiles: ["search"]
    ports:
      - "${SEARCH_PORT:-9200}:9200"

  mail-catcher:
    image: axllent/mailpit
    profiles: ["debug"]
    ports:
      - "${MAIL_PORT:-8025}:8025"

# Feature branch touching search functionality activates the "search" profile
export COMPOSE_PROJECT_NAME=shop-feature-search-relevance
export APP_PORT=8180 SEARCH_PORT=9300
docker compose --profile search up -d

# A plain backend feature branch skips search and mail-catcher entirely
export COMPOSE_PROJECT_NAME=shop-feature-checkout
export APP_PORT=8080
docker compose up -d

Diese Kombination reduziert unnötigen Ressourcenverbrauch bei parallelen Feature-Umgebungen erheblich: Elasticsearch, das mit Abstand speicherhungrigste Element vieler Magento-Stacks, läuft nur dort, wo es tatsächlich gebraucht wird, während einfache Backend-Branches deutlich schlanker bleiben und weniger RAM auf dem Entwicklerrechner belegen.

5. Umgebungen automatisch pro Branch erzeugen

Damit Entwickler nicht bei jedem Branch-Wechsel manuell Projektnamen und Ports setzen müssen, lohnt sich ein kleines Wrapper-Skript, das die komplette Ableitung übernimmt. Dieses Skript liest den aktuellen Git-Branch, berechnet den passenden Projektnamen und die Ports, schreibt beides in eine lokale .env-Datei und startet anschließend den Stack. So wird die Verwaltung paralleler Feature-Umgebungen zu einem einzigen Befehl statt einer Liste manueller Schritte.


#!/usr/bin/env bash
# feature-env.sh — one command per branch checkout
set -euo pipefail

branch="$(git rev-parse --abbrev-ref HEAD | tr '/' '-')"
project_name="shop-${branch}"
offset=$(( 0x$(echo -n "$branch" | md5sum | cut -c1-4) % 1000 ))

cat > .env <<EOF
COMPOSE_PROJECT_NAME=${project_name}
APP_PORT=$((8080 + offset))
DB_PORT=$((13306 + offset))
SEARCH_PORT=$((19200 + offset))
EOF

echo "[INFO] Starting environment '${project_name}' on port $((8080 + offset))"
docker compose up -d

Ein solches Skript, einmal ins Repository aufgenommen, macht parallele Feature-Umgebungen für das gesamte Team zugänglich, ohne dass jeder Entwickler die Details von COMPOSE_PROJECT_NAME oder Port-Hashing verstehen muss. Neue Teammitglieder führen einfach ./feature-env.sh aus und erhalten eine isolierte Umgebung für ihren aktuellen Branch.

6. Netzwerk- und Volume-Namenskollisionen vermeiden

Auch bei korrekt gesetztem Projektnamen können Kollisionen entstehen, wenn Compose-Dateien explizite Namen für Netzwerke oder Volumes definieren, statt sie automatisch aus dem Projektnamen ableiten zu lassen. Ein hartcodiertes name: shop_network im networks-Abschnitt überschreibt die automatische Präfixierung und führt bei zwei parallelen Checkouts wieder zu Konflikten, weil beide Umgebungen versuchen, dasselbe explizit benannte Netzwerk zu belegen.


# WRONG: explicit names bypass project-name prefixing
networks:
  default:
    name: shop_network   # collides across all parallel environments

volumes:
  db_data:
    name: shop_db_data    # collides across all parallel environments

# RIGHT: let Compose derive names from COMPOSE_PROJECT_NAME automatically
networks:
  default: {}

volumes:
  db_data: {}

Für parallele Feature-Umgebungen gilt deshalb als feste Regel: explizite name-Schlüssel bei Netzwerken und Volumes vermeiden, außer es gibt einen bewussten Grund, eine Ressource geteilt über mehrere Projekte hinweg zu nutzen. Die automatische Namensableitung aus COMPOSE_PROJECT_NAME ist in fast allen Fällen die robustere Wahl.

7. Verwaiste Feature-Umgebungen zuverlässig aufräumen

Je mehr parallele Feature-Umgebungen im Laufe einer Woche entstehen, desto wichtiger wird ein zuverlässiger Aufräumprozess. Nicht mehr benötigte Branches werden oft gelöscht, ohne dass jemand daran denkt, den zugehörigen Docker-Stack zu stoppen, wodurch RAM und Festplattenspeicher langsam, aber stetig durch vergessene Container und Volumes belegt bleiben.


#!/usr/bin/env bash
# cleanup-feature-envs.sh — remove environments for deleted branches
set -euo pipefail

existing_branches=$(git branch --format='%(refname:short)' | tr '/' '-')

docker compose ls --format json | jq -r '.[].Name' | while read -r project; do
  branch_part="${project#shop-}"
  if ! echo "$existing_branches" | grep -qx "$branch_part"; then
    echo "[CLEANUP] Removing orphaned environment: $project"
    docker compose -p "$project" down --volumes --remove-orphans
  fi
done

Dieses Skript vergleicht laufende Compose-Projekte mit noch existierenden Git-Branches und entfernt jede Umgebung, deren Branch bereits gelöscht wurde, inklusive der zugehörigen Volumes. Als wöchentlicher Cronjob oder manueller Aufruf vor dem Feierabend hält es den Ressourcenverbrauch paralleler Feature-Umgebungen dauerhaft unter Kontrolle, ohne dass Entwickler manuell an jede einzelne Umgebung denken müssen.

8. Dasselbe Muster für Review-Apps in der CI nutzen

Das Prinzip hinter parallelen Feature-Umgebungen lässt sich eins zu eins auf CI-Systeme übertragen, die für jeden offenen Merge Request eine eigene Review-App bereitstellen. Der CI-Runner setzt COMPOSE_PROJECT_NAME auf eine Kombination aus Projektname und Merge-Request-Nummer, wodurch mehrere Review-Deployments gleichzeitig auf demselben CI-Host oder derselben Staging-Maschine existieren können, exakt nach demselben Isolationsmuster wie lokal auf dem Entwicklerrechner.

Diese Konsistenz zwischen lokalem Workflow und CI-Pipeline hat einen praktischen Zusatznutzen: Entwickler, die parallele Feature-Umgebungen lokal bereits gewohnt sind, verstehen das Verhalten der Review-Apps in der CI intuitiv, weil dieselben Umgebungsvariablen und dasselbe Namensschema zugrunde liegen. Das reduziert Rückfragen im Team und macht Troubleshooting bei fehlgeschlagenen Review-App-Deployments schneller.

9. Ansätze für parallele Umgebungen im Vergleich

Es gibt mehrere Wege, mehrere Branches gleichzeitig testbar zu machen, mit deutlich unterschiedlichem Ressourcenbedarf und Isolationsgrad.

Ansatz Isolation Ressourcenbedarf Setup-Aufwand
Ein Stack, seriell Keine, ständiges Umschalten Niedrig Keiner
Projektname plus Profiles Vollständig Mittel Gering bis mittel
Eigene VM pro Branch Vollständig Hoch Hoch

Der serielle Ansatz mit einem einzigen Stack spart Ressourcen, kostet aber ständig Zeit für Umschalten und Neustarten. Eine vollständige VM pro Branch bietet maximale Isolation, verbraucht aber unnötig viel RAM und Festplattenspeicher für Anwendungsfälle, die keine so strikte Trennung brauchen. Parallele Feature-Umgebungen über Projektname und Profiles liegen genau in der Mitte: vollständige Isolation bei überschaubarem Ressourcenbedarf.

Mironsoft

Docker-Compose-Automatisierung und Multi-Branch-Workflows

Mehrere Feature-Branches gleichzeitig testen, ohne Chaos?

Wir richten Compose-Setups mit automatischer Projektnamen-Ableitung, dynamischer Portvergabe und Profile-Strategie für euer Team ein, inklusive Cleanup-Automatisierung und optionaler CI-Integration für Review-Apps.

Isolations-Setup

COMPOSE_PROJECT_NAME, dynamische Ports und Profiles pro Branch einrichten

Automatisierungsskripte

Wrapper-Skripte für Start, Stop und Cleanup verwaister Umgebungen

CI-Integration

Dasselbe Muster für Review-Apps in Merge-Request-Pipelines nutzen

10. Zusammenfassung

Parallele Feature-Umgebungen lösen ein Problem, das jedes wachsende Entwicklerteam früher oder später trifft: mehrere Branches müssen gleichzeitig lauffähig sein, ohne sich gegenseitig Ports, Datenbanken oder Container-Namen streitig zu machen. COMPOSE_PROJECT_NAME als expliziter Isolationsmechanismus, kombiniert mit dynamischer Portvergabe und optionalen Profiles für schwere Services wie Elasticsearch, macht genau das möglich, ohne für jeden Branch eine eigene virtuelle Maschine zu betreiben.

Ein Automatisierungsskript, das Projektname und Ports direkt aus dem Git-Branch-Namen ableitet, senkt die Einstiegshürde für das gesamte Team auf einen einzigen Befehl. Ein passender Cleanup-Prozess verhindert, dass vergessene Branches dauerhaft Ressourcen belegen. Dasselbe Muster lässt sich nahtlos in CI-Pipelines für Review-Apps übertragen, was parallele Feature-Umgebungen zu einem konsistenten Konzept über lokale Entwicklung und CI hinweg macht.

Parallele Feature-Umgebungen — Das Wichtigste auf einen Blick

COMPOSE_PROJECT_NAME

Pro Checkout gesetzt, isoliert Container, Netzwerke und Volumes automatisch mit eindeutigem Präfix.

Dynamische Ports

Aus dem Branch-Namen deterministisch abgeleitet, kein manuelles Merken von Portnummern nötig.

Profiles

Schwere, optionale Services wie Elasticsearch nur dort aktivieren, wo sie tatsächlich gebraucht werden.

Cleanup

Regelmäßiger Abgleich zwischen laufenden Compose-Projekten und existierenden Branches verhindert Ressourcenverschwendung.

11. FAQ: Parallele Feature-Umgebungen

1Was macht COMPOSE_PROJECT_NAME genau?
Überschreibt den Standard-Projektnamen und sorgt für eindeutige Container-, Netzwerk- und Volume-Namen pro Umgebung.
2Braucht jeder Branch ein eigenes Checkout?
Ja, für echte Parallelität, etwa über git worktree, da ein Checkout nur einen Branch gleichzeitig ausgecheckt haben kann.
3Wie vermeide ich Portkonflikte?
Über deterministisch aus dem Branch-Namen berechnete Ports, etwa per Hash-Offset, sodass jeder Branch einen eigenen Port bekommt.
4Was bringen Profiles zusätzlich?
Machen schwere Services optional, sodass nur Branches, die sie brauchen, den zusätzlichen Ressourcenverbrauch tragen.
5Wie räume ich vergessene Umgebungen auf?
Mit einem Skript, das laufende Projekte mit existierenden Branches abgleicht und Umgebungen gelöschter Branches entfernt.
6Funktioniert das auch mit Podman oder Colima?
Ja, beides ist Teil der Compose-Spezifikation und unabhängig von der darunterliegenden Runtime.
7Wie viele parallele Umgebungen sind realistisch?
Ohne Elasticsearch meist drei bis fünf leichte Umgebungen problemlos, mit schweren Services entsprechend weniger.
8Gehört die .env-Datei ins Repository?
Nein, sie sollte in .gitignore stehen, da sie individuell pro Checkout generiert wird und nicht geteilt werden soll.
9Übertragbar auf Staging-Server?
Ja, dasselbe Muster funktioniert auf CI-Runnern oder Staging-Hosts für Review-Apps pro Merge Request.
10Was bei gleichem Branch-Namen auf zwei Rechnern?
Kein Konflikt, da Isolation lokal pro Rechner gilt. Auf demselben Rechner sollte zusätzlich ein Nutzerpräfix ergänzt werden.