und Shared Configs im Release-Modell
env.php gehört nicht ins Repository und nicht in den Build-Artefakt. Wer das missachtet, baut ein Release-Modell, das beim ersten Umgebungswechsel bricht. Dieser Beitrag erklärt, wie app/etc, Shared-Verzeichnisse und config.php korrekt im Symlink-Release-Modell mit GitLab CI/CD zusammenspielen.
Inhaltsverzeichnis
- 1. Was env.php, config.php und app/etc trennen
- 2. Das Release-Modell und die Rolle von Shared-Pfaden
- 3. Shared-Verzeichnisstruktur korrekt anlegen
- 4. env.php sicher auf den Server bringen
- 5. config.php im Build-Artefakt oder im Shared-Ordner?
- 6. GitLab-Pipeline: Symlinks und Shared-Dateien im Deploy-Job
- 7. Direkter Vergleich: Shared vs. nicht-Shared
- 8. Typische Fehlerbilder bei env.php und Shared Configs
- 9. Rollback und Shared-Dateien: Was sich ändert, was bleibt
- 10. Zusammenfassung
- 11. FAQ
1. Was env.php, config.php und app/etc trennen
In Magento existieren zwei grundlegend verschiedene Konfigurationsdateien im Verzeichnis app/etc/, die in der Praxis häufig verwechselt werden: env.php und config.php. env.php enthält umgebungsspezifische Laufzeitdaten – Datenbankzugangsdaten, Redis-Verbindungen, Crypt-Key, Backend-URL und Session-Konfiguration. Diese Datei darf niemals in das Git-Repository eingecheckt werden, weil sie Geheimnisse enthält, die für jede Umgebung unterschiedlich sind. Sie wird einmalig auf dem Zielserver angelegt und über Shared-Pfade zwischen Releases geteilt.
config.php hingegen enthält den Modulstatus und kann optional Store-spezifische Konfigurationen enthalten, die über bin/magento app:config:dump exportiert wurden. Diese Datei ist deutlich unkritischer und kann im Repository versioniert werden, sofern das Team bewusst entschieden hat, die Konfiguration über das Repository zu steuern. Der Unterschied ist entscheidend für das Release-Modell: Was im Repository steht, wird mit jedem Release ausgerollt; was im Shared-Pfad liegt, bleibt über alle Releases hinweg konstant.
Das Verzeichnis app/etc/ selbst ist ein scheinbar einfacher Ordner, der im Release-Modell jedoch eine kritische Rolle spielt. Wer das Verzeichnis vollständig im Build-Artefakt mitliefert und danach env.php kopiert, arbeitet korrekt. Wer hingegen versucht, app/etc/ als Symlink auf ein Shared-Verzeichnis zu legen, riskiert, dass das Magento-Setup-System beim ersten Lauf Berechtigungsprobleme meldet. Die sicherste Lösung ist ein fester Ordner im Release-Verzeichnis mit einem danach aufgespielten env.php aus dem Shared-Bereich.
2. Das Release-Modell und die Rolle von Shared-Pfaden
Das Symlink-Release-Modell für Magento arbeitet mit einem einfachen Prinzip: Jeder Release landet in einem eigenen Verzeichnis unter releases/. Der aktive Release wird durch einen Symlink current referenziert, den der Webserver als Document-Root nutzt. Das Umschalten von einem Release auf den nächsten ist eine atomare Operation – ein einziger ln -sfn-Aufruf – und dauert Millisekunden. Laufende Requests unter dem alten Release werden noch abgeschlossen, neue Requests landen sofort im neuen Release.
Shared-Pfade sind die Verbindung zwischen diesem isolierten Release-Konzept und dem dauerhaften Serverzustand. Dateien und Verzeichnisse, die über Releases hinweg identisch bleiben oder sich außerhalb des Code-Lebenszyklus verändern, gehören in den shared/-Bereich. Klassische Kandidaten für Magento sind pub/media/, var/log/, var/session/ und app/etc/env.php. Der Deploy-Prozess erstellt für jeden neuen Release Symlinks auf diese Shared-Ressourcen, bevor der Symlink auf current umgestellt wird.
Das Konzept funktioniert nur, wenn die Shared-Struktur vor dem ersten Deployment angelegt und danach konsequent gepflegt wird. Ein häufiger Fehler in der Praxis: Das erste Deployment legt env.php im Release-Verzeichnis an statt im Shared-Ordner. Nach einem Rollback oder beim nächsten Deployment fehlt die Datei im neuen Release, weil niemand den Symlink angelegt hat. GitLab-Pipelines müssen daher einen expliziten Schritt enthalten, der prüft, ob der Shared-Ordner und seine kritischen Dateien vorhanden sind, bevor der Deploy-Job fortfährt.
3. Shared-Verzeichnisstruktur korrekt anlegen
Die initiale Einrichtung der Shared-Struktur auf dem Zielserver ist ein einmaliger Schritt, der aber sorgfältig dokumentiert werden muss. Teams, die diesen Schritt nicht dokumentieren, stehen beim Aufsetzen eines zweiten Servers oder nach einem Server-Wechsel vor dem Problem, dass niemand mehr weiß, welche Verzeichnisse manuell angelegt wurden und welche Berechtigungen sie haben. Die Shared-Struktur gehört in die Infrastruktur-Dokumentation und idealerweise in ein Setup-Skript, das versioniert und reproduzierbar ist.
Für ein typisches Magento-Projekt mit mehreren Stores und Hyvä-Frontend sieht die Shared-Struktur auf dem Zielserver folgendermaßen aus. Kritisch ist dabei, dass der var/-Ordner mit seinen Unterverzeichnissen als Symlink angelegt wird, der aber auf einen Shared-Pfad zeigt, der vom Webserver-Nutzer beschreibbar ist. Berechtigungsprobleme in var/ oder pub/media/ nach dem Deploy sind fast immer auf fehlende oder falsche Symlinks zurückzuführen.
# Initial server setup — run once before first deployment
# Script: bin/setup-shared.sh
setup:shared:
stage: .pre
when: manual
script:
- ssh "$DEPLOY_USER@$DEPLOY_HOST" "bash -s" << 'ENDSSH'
set -euo pipefail
BASE="${DEPLOY_PATH}/shared"
# Create shared directories
mkdir -p "${BASE}/app/etc"
mkdir -p "${BASE}/pub/media"
mkdir -p "${BASE}/var/log"
mkdir -p "${BASE}/var/session"
mkdir -p "${BASE}/var/cache"
# Set correct ownership for web server user
chown -R www-data:www-data "${BASE}"
echo "Shared structure ready at ${BASE}"
ENDSSH
Der manuelle Setup-Job in GitLab ist eine bewusste Entscheidung: Er soll nicht automatisch bei jedem Deployment laufen, aber er soll versioniert und reproduzierbar sein. Wer ihn als manuellen Job in der Pipeline hält, stellt sicher, dass die Shared-Struktur immer auf dieselbe Weise angelegt wird – unabhängig davon, wer im Team die Aufgabe übernimmt.
4. env.php sicher auf den Server bringen
Die env.php enthält Datenbankpasswörter, den Crypt-Key und Session-Konfigurationen. Sie darf weder im Git-Repository noch als GitLab-CI-Artefakt auftauchen. Der sicherste Weg, env.php auf den Server zu bringen, ist ein einmaliger manueller Upload über eine verschlüsselte Verbindung beim initialen Server-Setup. Danach liegt die Datei dauerhaft im Shared-Verzeichnis und wird von keinem Deployment-Prozess überschrieben.
Alternativ kann env.php durch einen Skript-Schritt aus GitLab-CI-Variablen generiert werden. In diesem Fall speichert man die einzelnen Werte als Protected und Masked Variables in GitLab und schreibt ein Skript, das die Datei aus diesen Variablen zusammensetzt. Das ist aufwendiger, aber vollständig automatisierbar und vermeidet manuelle Server-Zugänge für reguläre Deployments. Für Teams mit mehreren Umgebungen ist dieser Ansatz sauberer, weil alle env.php-Werte zentral in GitLab verwaltet werden und Environment-Scopes verhindern, dass Staging-Secrets auf Production landen.
Unabhängig vom gewählten Ansatz muss der Deploy-Job prüfen, ob env.php im Shared-Verzeichnis vorhanden ist, bevor er den Symlink auf current umstellt. Ein Deployment auf eine Umgebung ohne env.php führt unweigerlich zu einem weißen Bildschirm oder einer Datenbankfehlermeldung im Shop.
5. config.php im Build-Artefakt oder im Shared-Ordner?
Die Entscheidung, ob config.php im Git-Repository oder im Shared-Ordner liegt, hat direkte Auswirkungen auf das Release-Modell. Liegt config.php im Repository, wird sie mit jedem Deployment ausgerollt. Das ist der sauberere Ansatz, weil Änderungen am Modulstatus nachvollziehbar versioniert werden und der Build-Artefakt vollständig reproduzierbar ist. Magento-Teams, die Konfigurationsänderungen über bin/magento config:set und anschließendes app:config:dump und Git-Commit vornehmen, arbeiten nach diesem Modell.
Liegt config.php im Shared-Ordner, kann sie direkt auf dem Server geändert werden, ohne einen neuen Build zu starten. Das ist praktischer für Teams, die häufig Store-Konfigurationen anpassen, hat aber den Nachteil, dass die Konfiguration nicht versioniert ist und bei einem Server-Neuaufbau manuell wiederhergestellt werden muss. Für Produktionssysteme ist das erste Modell fast immer vorzuziehen, weil es Auditing, Rollback und Reproduzierbarkeit ermöglicht.
# deploy.yml — Shared symlinks and env.php placement
deploy:production:
stage: deploy
environment:
name: production
url: https://shop.example.com
script:
- ssh "$DEPLOY_USER@$DEPLOY_HOST" "bash -s" << ENDSSH
set -euo pipefail
RELEASE="${DEPLOY_PATH}/releases/$(date +%Y%m%d-%H%M%S)"
SHARED="${DEPLOY_PATH}/shared"
CURRENT="${DEPLOY_PATH}/current"
# Verify shared structure exists before deploying
test -f "${SHARED}/app/etc/env.php" \
|| { echo "ERROR: env.php missing in shared"; exit 1; }
mkdir -p "${RELEASE}"
rsync -az --delete "${CI_PROJECT_DIR}/" "${RELEASE}/"
# Link env.php from shared (never from repository)
cp "${SHARED}/app/etc/env.php" "${RELEASE}/app/etc/env.php"
# Link persistent directories from shared
rm -rf "${RELEASE}/pub/media"
ln -sfn "${SHARED}/pub/media" "${RELEASE}/pub/media"
rm -rf "${RELEASE}/var/log"
ln -sfn "${SHARED}/var/log" "${RELEASE}/var/log"
rm -rf "${RELEASE}/var/session"
ln -sfn "${SHARED}/var/session" "${RELEASE}/var/session"
# Atomic switch to new release
ln -sfn "${RELEASE}" "${CURRENT}"
echo "Release switched to ${RELEASE}"
ENDSSH
only:
- tags
6. GitLab-Pipeline: Symlinks und Shared-Dateien im Deploy-Job
Der Deploy-Job in GitLab trägt die Verantwortung, das gebaute Artefakt auf den Server zu übertragen und die Shared-Pfade korrekt zu verknüpfen. Dieser Job muss idempotent sein – er soll auch dann korrekt funktionieren, wenn er wegen eines Fehlers erneut ausgeführt wird. Idempotenz erfordert, dass jede Operation ihren Zustand prüft, bevor sie Änderungen vornimmt. Ein ln -sfn ist bereits idempotent; ein cp überschreibt bei jedem Lauf, was für env.php korrekt ist.
Ein häufig übersehenes Detail: Wenn der Deploy-Job in einem Docker-Container auf dem GitLab-Runner läuft, kann er nicht direkt SSH-Verbindungen mit einem privaten Schlüssel aufbauen, ohne dass der Schlüssel vorher im Job verfügbar gemacht wird. Das geschieht über eine before_script-Sektion, die den SSH-Agenten startet und den Schlüssel aus einer GitLab-CI-Variable hinzufügt. Wer diesen Schritt vergisst oder unsauber implementiert, hinterlässt SSH-Schlüssel im Docker-Image-Layer.
Für Multi-Server-Deployments, bei denen mehrere Web-Nodes gleichzeitig aktualisiert werden müssen, wiederholt der Deploy-Job die Symlink-Operationen für jeden Server. Die Reihenfolge ist dabei nicht beliebig: Erst wenn alle Nodes das neue Release-Verzeichnis vollständig aufgebaut haben, wird der Symlink auf current umgestellt. So laufen alle Nodes exakt zur gleichen Zeit auf denselben Code.
7. Direkter Vergleich: Shared vs. nicht-Shared
Die Entscheidung, welche Dateien und Verzeichnisse in den Shared-Bereich gehören und welche mit jedem Release neu ausgerollt werden, ist eine der wichtigsten Architekturentscheidungen im Release-Modell. Falsch getroffene Entscheidungen führen zu Datenverlust oder zu Deployments, die bei jedem Lauf manuelle Korrekturen benötigen.
| Datei / Verzeichnis | Shared | Im Release-Artefakt | Begründung |
|---|---|---|---|
| app/etc/env.php | Ja | Nein | Enthält Secrets, umgebungsspezifisch |
| app/etc/config.php | Möglich | Bevorzugt | Modulstatus ist Teil des Releases |
| pub/media/ | Ja | Nein | Nutzer-Uploads, release-unabhängig |
| pub/static/ | Nein | Ja (Build-Artefakt) | Wird je Release neu gebaut |
| var/log/ | Ja | Nein | Logs überspannen mehrere Releases |
Die Tabelle zeigt eine klare Linie: Alles, was Secrets enthält, durch Nutzerinteraktion entsteht oder über Releases hinweg konsistent sein muss, gehört in den Shared-Bereich. Alles, was durch den Build-Prozess entsteht und zur Code-Version gehört, ist Teil des Release-Artefakts. Diese Trennung ist nicht nur sauberer, sondern ermöglicht auch Rollbacks, die nicht zu Datenverlust führen, weil die persistenten Daten niemals im Release-Verzeichnis liegen.
8. Typische Fehlerbilder bei env.php und Shared Configs
Das häufigste Fehlerbild in der Praxis ist eine fehlende oder leere env.php nach einem Deployment. Das passiert, wenn der Deploy-Job env.php aus dem Shared-Verzeichnis kopieren soll, das Shared-Verzeichnis aber nicht existiert oder die Datei fehlt. Magento gibt in diesem Fall keine verständliche Fehlermeldung aus – stattdessen erscheint eine leere Seite oder ein interner Serverfehler. Der Verify-Schritt in der Pipeline muss daher explizit prüfen, ob env.php vorhanden und syntaktisch gültig ist, bevor der Smoke Test auf die Shop-URL zugreift.
Ein zweites häufiges Fehlerbild entsteht beim erstmaligen Deployment auf eine neue Umgebung: Der Entwickler kopiert env.php ins Release-Verzeichnis statt ins Shared-Verzeichnis. Das erste Deployment funktioniert. Das zweite Deployment legt ein neues Release-Verzeichnis an, das keine env.php enthält, weil sie nur im ersten Release-Verzeichnis lag. Dieses Problem tritt immer dann auf, wenn die Shared-Struktur und der Deploy-Prozess nicht gemeinsam eingerichtet wurden.
Ein drittes Fehlerbild betrifft config.php und den Modulstatus. Wenn config.php im Shared-Ordner liegt und ein Deployment neue Module aktiviert, ohne die Shared-config.php zu aktualisieren, startet Magento nach dem Deployment mit einem inkonsistenten Modulstatus. Das äußert sich typischerweise in fehlenden Layoutblöcken, Fehlern im Admin-Panel oder nicht funktionierenden Extensions. Die Lösung ist, config.php entweder konsequent im Repository zu versionieren oder einen Post-Deploy-Schritt zu implementieren, der bin/magento app:config:import ausführt.
9. Rollback und Shared-Dateien: Was sich ändert, was bleibt
Der entscheidende Vorteil des Symlink-Release-Modells ist, dass ein Rollback auf einen früheren Release keine Daten zerstört. Da alle persistenten Daten im Shared-Bereich liegen und nicht im Release-Verzeichnis, passen die Daten zum alten Release genauso wie zum neuen. Ein Rollback ist semantisch identisch mit dem Umstellen des current-Symlinks auf ein älteres Release-Verzeichnis, gefolgt von einem Cache-Flush.
Die einzige Ausnahme sind Datenbankmigrationen. Wenn ein neuer Release Datenbankschema-Änderungen enthält, die durch bin/magento setup:upgrade ausgeführt wurden, ist die Datenbank nach dem Rollback möglicherweise nicht mehr vollständig kompatibel mit dem alten Code. Für diesen Fall muss der Rollback-Plan explizit die Datenbank-Kompatibilität adressieren – entweder durch rückwärtskompatible Schemaänderungen (Expand-Contract-Pattern) oder durch ein Datenbank-Backup vor dem Deployment.
# rollback.yml — Rollback to a specific previous release
rollback:production:
stage: rollback
when: manual
environment:
name: production
script:
- ssh "$DEPLOY_USER@$DEPLOY_HOST" "bash -s" << ENDSSH
set -euo pipefail
# List available releases for manual selection
echo "Available releases:"
ls -lt "${DEPLOY_PATH}/releases/" | head -10
# PREVIOUS_RELEASE must be passed as variable in manual trigger
TARGET="${DEPLOY_PATH}/releases/${ROLLBACK_TO}"
test -d "${TARGET}" || { echo "ERROR: Release not found: ${TARGET}"; exit 1; }
# Shared files remain unchanged — only the symlink switches
ln -sfn "${TARGET}" "${DEPLOY_PATH}/current"
cd "${DEPLOY_PATH}/current"
bin/magento cache:flush
echo "Rolled back to ${TARGET}"
ENDSSH
variables:
ROLLBACK_TO: "" # Set via GitLab manual job trigger
10. Zusammenfassung
Die korrekte Behandlung von env.php, config.php und Shared-Verzeichnissen im Release-Modell ist keine optionale Verbesserung, sondern die Grundlage für stabile Magento-Deployments. env.php gehört ausschließlich in den Shared-Bereich und darf weder im Repository noch im Build-Artefakt auftauchen. config.php sollte bevorzugt im Repository versioniert werden, damit der Modulstatus Teil des release-spezifischen Artefakts ist. Verzeichnisse wie pub/media/, var/log/ und var/session/ sind durch Symlinks mit dem Shared-Bereich zu verbinden, bevor der Symlink auf current umgestellt wird.
Der Deploy-Job in GitLab CI muss diese Schritte in der richtigen Reihenfolge ausführen und vor dem Umstellen des current-Symlinks prüfen, ob alle Shared-Dateien vorhanden sind. Ein Verify-Job nach dem Deployment stellt sicher, dass env.php korrekt eingebunden wurde und der Shop erreichbar ist. Rollbacks funktionieren im Symlink-Modell ohne Datenverlust, sofern die Datenbank-Kompatibilität berücksichtigt wird.
env.php und Shared Configs im Release-Modell — Das Wichtigste auf einen Blick
env.php
Liegt im Shared-Ordner, wird nie versioniert. Deploy-Job prüft Existenz vor dem Symlink-Wechsel. Generierung aus GitLab-Variablen ist die sauberste Lösung.
config.php
Bevorzugt im Repository versioniert – Modulstatus ist Teil des Releases. Post-Deploy app:config:import stellt Konsistenz sicher.
Shared-Struktur
pub/media, var/log, var/session als Symlinks auf Shared-Pfade. Einmalige Einrichtung, versioniertes Setup-Skript, reproduzierbar.
Rollback
Symlink auf alten Release umstellen, Cache flushen. Shared-Daten bleiben unberührt. Datenbank-Kompatibilität separat planen.