Compose-Invocation-Discipline¶
Prinzip¶
docker compose-Invocations ohne --env-file können abhängige Services
stillschweigend mit leerer Env recreaten. Bei kritischen Secrets (z.B.
KC_DB_PASSWORD) führt das zu Crashloops, die schwer zu diagnostizieren
sind. Beispiel: Keycloak-Crashloop während B2-03-Verifikation am
2026-04-22 — ein docker compose run ohne --env-file hatte den
kora-platform-keycloak-Container mit leerem KC_DB_PASSWORD
recreated, 18 Restart-Zyklen mit SCRAM-Auth-Fehler waren die Folge.
Konsequenz: Alle docker compose-Invocations für den
kora-platform-Stack laufen über den Makefile-Wrapper $(KORA_COMPOSE).
Ad-hoc-Invocations direkt in der Shell (ohne expliziten --env-file)
sind zu vermeiden.
Makefile-Wrapper¶
KORA_COMPOSE = docker compose -p kora-platform -f docker-compose.platform.yml --env-file .env.platform
Alle Platform-Targets im Makefile nutzen diese Variable:
make dev-platform/make down-platformmake redeploy-platformmake logs-platformmake docs-kora/make docs-kora-buildmake platform-exec cmd="…"— Ad-hoc-Befehl imkora-platform-apimake platform-bootstrap cmd="…"— Bootstrap-CLI mit Master-PW
kora-*-Convenience-Targets mit SVC=-Pattern (K-Q4-Fix-B.3)¶
Für Single-Service-Operationen ohne den ganzen Stack zu touchen:
| Target | Zweck | Beispiel |
|---|---|---|
make kora-up / kora-down |
Stack starten/stoppen (Aliase) | make kora-up |
make kora-ps |
Container-Status auflisten | make kora-ps |
make kora-restart SVC=<svc> |
Service neu starten | make kora-restart SVC=demo-frontend |
make kora-recreate SVC=<svc> |
force-recreate für Bind-Mount-Refresh | make kora-recreate SVC=demo-frontend |
make kora-logs SVC=<svc> |
Logs eines Services tailen | make kora-logs SVC=api |
make kora-rebuild SVC=<svc> |
--no-cache rebuild gegen Stale-CMD-Drift |
make kora-rebuild SVC=api |
SVC= ist der Compose-Service-Name (z.B. demo-frontend), nicht
der container_name (kora-platform-demo-frontend). Der Compose-Service-
Name steht in docker-compose.platform.yml als Top-Level-Key unter
services:.
kora-recreate vs. kora-restart: restart reicht nicht, wenn
Linux-Bind-Mounts nach einem git checkout einen Stale-Inode haben
(K-Q4-Fix-B.1-Recovery, demo-frontend zeigte leeres /usr/share/nginx/html).
kora-recreate macht up -d --force-recreate, was den Mount frisch
bindet.
kora-rebuild vs. redeploy-platform-api: kora-rebuild mit
--no-cache ist die richtige Wahl nach Modul-Renamings oder Major-
Dependency-Updates (z.B. avs_chatbot → kora_platform-Rename in
K-Q4-Fix-A — der normale Build hatte den alten CMD aus dem Layer-Cache
gehalten).
Bind-Mount-Drift nach git checkout¶
Linux bind-mounts pinnen sich an den Inode des Source-Pfades zum
Mount-Zeitpunkt. Schreibt git checkout einen frischen Inode für
ein Verzeichnis (etwa weil ein Branch das Verzeichnis anders
strukturiert), liest der Container weiter aus dem alten Inode —
meist ein leeres Verzeichnis. make kora-restart reicht nicht;
nur ein force-recreate refresht die Mount-Tabelle.
make refresh-mounts # Default: demo-frontend
make refresh-mounts SVC="demo-frontend api" # explizite Liste
Vorfall: K-Q4-Fix-B.1 recovery 2026-05-06, kora-platform-demo-frontend
zeigte leeres /usr/share/nginx/html nach Branch-Switch zwischen
Feature-Branch und platform/v1.0.0. Restart half nicht, recreate
schon. Helper kapselt das Pattern.
Optional als opt-in Git-Hook lokal aktivieren (NICHT im Repo, opt-in pro Dev):
cat > .git/hooks/post-checkout <<'EOF'
#!/bin/sh
if git diff-tree -r --name-only HEAD@{1} HEAD 2>/dev/null \
| grep -q '^infra/demo-frontend/html/'; then
echo "Branch-switch touched demo-frontend html — refreshing mounts..."
./infra/scripts/refresh-mounts.sh
fi
EOF
chmod +x .git/hooks/post-checkout
make platform-exec — Ad-hoc-Befehl im laufenden API-Container¶
Für Befehle, die im laufenden kora-platform-api-Container ausgeführt
werden sollen (Diagnose, Settings-Dump, etc.):
make platform-exec cmd="kora-platform --help"
make platform-exec cmd="sh -c 'env | grep -i KEYCLOAK'"
Der Wrapper nutzt docker compose … exec api, d.h. der bestehende
Container wird benutzt, kein neuer Run-Container erzeugt — kein
stilles Service-Recreate.
make platform-bootstrap — Bootstrap-CLI mit Master-PW¶
Für einmalige Bootstrap-Operationen (z.B.
bootstrap-operator-admin), die Master-Admin-Credentials gegen
Keycloak brauchen:
# tmpfs-Pfad (empfohlen, weg nach Reboot)
mkdir -p /run/user/$UID
cp .env.bootstrap.example /run/user/$UID/.env.bootstrap
$EDITOR /run/user/$UID/.env.bootstrap # Master-PW eintragen
set -a
source /run/user/$UID/.env.bootstrap
set +a
make platform-bootstrap cmd="bootstrap-operator-admin --email admin@example.com"
shred -u /run/user/$UID/.env.bootstrap 2>/dev/null || \
rm -f /run/user/$UID/.env.bootstrap
Der Wrapper scheitert früh mit klarer Fehlermeldung, wenn
KC_BOOTSTRAP_ADMIN_USERNAME oder KC_BOOTSTRAP_ADMIN_PASSWORD in der
Shell-Env fehlen. Intern nutzt er docker exec -e … (keine
docker compose run-Invocation), weil --env-file im run-Subcommand
in unserer Compose-Version (v5.1.3) nicht unterstützt wird — und weil
exec ohnehin keine Service-Graph-Re-Evaluation auslöst.
Was zu vermeiden ist¶
- Nicht:
docker compose run --rm api …ohne explizites--env-file .env.platform. Dieses Pattern war der Trigger für den Keycloak-Crashloop vom 2026-04-22. - Nicht: Direkte
docker exec …-Befehle ohne Env-Awareness, wenn der Befehl Env-Variablen braucht. Für Platform-Scope immermake platform-execbzw.make platform-bootstrapnutzen. - Nicht: Ad-hoc-Scripts, die
docker compose …-Invocations enthalten, ohne den Wrapper zu nutzen. Stattdessen auf ein Makefile-Target zurückgreifen oder neues Target hinzufügen.
Diagnose bei Crashloop-Verdacht¶
Wenn ein Container aus dem Platform-Stack unerwartet in einen Crashloop geht:
# 1. Symptom-Check
docker inspect kora-platform-keycloak --format '{{.RestartCount}} restarts, exit {{.State.ExitCode}}'
# 2. Logs
docker logs kora-platform-keycloak --tail 50 2>&1 | grep -iE "error|fatal|auth"
# 3. Container-Env auf leere Secrets prüfen
docker inspect kora-platform-keycloak \
--format '{{range .Config.Env}}{{println .}}{{end}}' | \
grep -iE "password|secret"
# Einträge, die als 'KEY=' (leerer Wert) erscheinen, sind Hinweis auf
# eine frühere Invocation ohne --env-file.
# 4. Reparatur — immer via Makefile-Wrapper:
make redeploy-platform
# oder punktuell:
$(KORA_COMPOSE) up -d --force-recreate <service>
Warum nicht Docker-Secrets?¶
Docker-Secrets wären die robusteste Lösung (Secrets sind read-only Mount, nicht ENV), aber:
- Umbau aller bestehenden Settings-Reader nötig (lesen aus Files statt ENVs)
- Zusätzliche Docker-Swarm-/Compose-Infrastruktur
- Für den Scope v1.0.0 Overkill — der Makefile-Wrapper eliminiert 95% der Fehlerklasse mit 20 Zeilen Makefile.
Als v1.x-Kandidat im Backlog, falls die Wrapper-Disziplin nicht hält oder neue Secret-Kategorien dazukommen.
Referenzen¶
- Wrapper-Variable:
Makefile:105(KORA_COMPOSE) - Neue Ad-hoc-Targets:
Makefile:151-176(platform-exec,platform-bootstrap) - Trigger-Vorfall: Keycloak-Crashloop vom 2026-04-22, dokumentiert
im TODO-Cleanup03-02-Archiv-Eintrag und im Changelog unter
[Unreleased]. - Verwandt: bind-mount-discipline.md, keycloak-service-account.md.