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.
Inhaltsverzeichnis
- 1. Das Problem: ein Stack, mehrere Feature-Branches
- 2. COMPOSE_PROJECT_NAME als Isolationsmechanismus
- 3. Dynamische Portvergabe pro Branch
- 4. Profiles mit Projektnamen für Team-spezifische Stacks kombinieren
- 5. Umgebungen automatisch pro Branch erzeugen
- 6. Netzwerk- und Volume-Namenskollisionen vermeiden
- 7. Verwaiste Feature-Umgebungen zuverlässig aufräumen
- 8. Dasselbe Muster für Review-Apps in der CI nutzen
- 9. Ansätze für parallele Umgebungen im Vergleich
- 10. Zusammenfassung
- 11. FAQ
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.