Build-Artefakte sinnvoll trennen
Wer in Magento-Deployments Media, statische Assets und Build-Artefakte in denselben Topf wirft, macht Rollbacks gefährlich und Deployments langsam. Jede dieser drei Kategorien hat einen anderen Lebenszyklus, eine andere Ownership und eine andere Deployment-Strategie – das muss sich in der Pipeline-Struktur widerspiegeln.
Inhaltsverzeichnis
- 1. Die drei Kategorien von Dateien in Magento
- 2. pub/media: persistent und release-unabhängig
- 3. pub/static: Build-Artefakt, kein Shared-Pfad
- 4. vendor/ und generated/: Release-gebundene Artefakte
- 5. Shared-Symlinks korrekt setzen
- 6. .gitignore und .gitattributes für saubere Artefakte
- 7. rsync-Excludes beim Deployment
- 8. Auswirkungen auf den Rollback-Pfad
- 9. Deployment-Ansätze im Vergleich
- 10. Zusammenfassung
- 11. FAQ
1. Die drei Kategorien von Dateien in Magento
Magento verwaltet intern drei fundamental unterschiedliche Kategorien von Dateien, die in Deployment-Prozessen häufig fälschlicherweise gleich behandelt werden. Die erste Kategorie sind Build-Artefakte: Dateien, die aus dem Source-Code erzeugt werden und bei jedem Release potenziell anders aussehen – vendor/, generated/, pub/static/. Die zweite Kategorie sind persistente Shared-Dateien: Dateien, die zwischen Releases geteilt werden, weil sie Laufzeitdaten enthalten, die nicht release-spezifisch sind – pub/media, var/log, var/session, app/etc/env.php. Die dritte Kategorie sind Konfigurationsdateien: Dateien wie app/etc/config.php, die versioniert sind, aber umgebungsspezifische Varianten haben können.
Der Fehler liegt darin, dass Teams alle drei Kategorien im gleichen Deployment-Modell behandeln. Wer pub/media als Teil des Build-Artefakts behandelt, transferiert bei jedem Deploy möglicherweise hunderte GB an Produktbildern – unnötig, langsam und gefährlich, weil ein Deployment dabei Bilder überschreiben kann, die nach dem letzten Build hochgeladen wurden. Wer pub/static als Shared-Pfad behandelt, riskiert, dass bei einem Rollback veraltete statische Dateien weiter ausgeliefert werden, die nicht zum alten Code passen.
Die klare Trennung dieser drei Kategorien ist deshalb keine organisatorische Empfehlung, sondern eine technische Notwendigkeit für Deployments, die zuverlässig, schnell und rollback-fähig sind. Jede Kategorie hat ihre eigene Deployment-Strategie, und diese Strategien müssen unabhängig voneinander funktionieren.
2. pub/media: persistent und release-unabhängig
pub/media ist das Verzeichnis, in dem Magento alle vom Admin hochgeladenen Bilder und Dateien speichert: Produktbilder, Kategorie-Banner, CMS-Mediendateien und dynamisch erzeugte Bildvarianten. Diese Dateien haben einen Lebenszyklus, der völlig unabhängig vom Code-Release ist. Sie entstehen durch Admin-Aktionen und bleiben persistent über alle Releases hinweg. Ein Rollback des Codes darf niemals ein Rollback der Media-Dateien bedeuten.
Die korrekte Implementierung für pub/media ist ein Shared-Symlink: Das Verzeichnis liegt physisch in shared/pub/media/ auf dem Server und wird in jedes Release-Verzeichnis als Symlink eingehängt. Bei einem Rollback wird der Code-Symlink zurückgestellt, der Media-Symlink zeigt aber weiterhin auf dieselben Dateien. Das ist die einzig sichere Strategie – sie trennt den Lebenszyklus der Media-Dateien vollständig vom Code-Lebenszyklus.
# Deploy job: properly separate shared paths from build artifacts
deploy:production:
stage: deploy
script:
- |
RELEASE_ID=$(date +%Y%m%d-%H%M%S)
RELEASE_PATH="${DEPLOY_PATH}/releases/${RELEASE_ID}"
# Create release directory structure
ssh "${DEPLOY_USER}@${DEPLOY_HOST}" "mkdir -p ${RELEASE_PATH}"
# Transfer build artifacts only — exclude persistent runtime data
rsync -az --delete \
--exclude="pub/media" \
--exclude="var/log" \
--exclude="var/session" \
--exclude="var/cache" \
--exclude="var/tmp" \
--exclude="app/etc/env.php" \
./ "${DEPLOY_USER}@${DEPLOY_HOST}:${RELEASE_PATH}/"
# Link shared persistent paths into new release directory
ssh "${DEPLOY_USER}@${DEPLOY_HOST}" bash << 'REMOTE'
set -euo pipefail
RELEASE="${RELEASE_PATH}"
SHARED="${DEPLOY_PATH}/shared"
# Persistent user-generated content — never part of build artifact
ln -sfn "${SHARED}/pub/media" "${RELEASE}/pub/media"
# Runtime directories — persist across releases
ln -sfn "${SHARED}/var/log" "${RELEASE}/var/log"
ln -sfn "${SHARED}/var/session" "${RELEASE}/var/session"
# Environment-specific configuration — not in repository
ln -sfn "${SHARED}/app/etc/env.php" "${RELEASE}/app/etc/env.php"
cp "${SHARED}/app/etc/config.php" "${RELEASE}/app/etc/config.php"
REMOTE
3. pub/static: Build-Artefakt, kein Shared-Pfad
pub/static ist semantisch das genaue Gegenteil von pub/media: Es ist ein reines Build-Artefakt, das vollständig aus dem Source-Code erzeugt wird und bei jedem Release potenziell völlig anders aussieht. CSS-Dateien ändern sich mit jedem Frontend-Release, JavaScript-Bundles ändern sich mit jedem Modul-Update, und die Verzeichnisstruktur in pub/static spiegelt exakt den Zustand des Codes zum Zeitpunkt des Builds wider.
Wer pub/static als Shared-Pfad behandelt – also über alle Releases hinweg teilt – riskiert, dass nach einem Rollback der alte Code zusammen mit den neuen statischen Dateien ausgeliefert wird. CSS-Klassen, die der neue Build erzeugt hat, sind im alten Template möglicherweise nicht referenziert, oder umgekehrt: Templates des alten Codes erwarten CSS-Klassen, die im alten Build existierten, aber im neuen Build umbenannt wurden. Das führt zu Layout-Fehlern, die schwer zu debuggen sind. pub/static gehört deshalb in das Release-Verzeichnis, nicht in den Shared-Pfad.
4. vendor/ und generated/: Release-gebundene Artefakte
Sowohl vendor/ als auch generated/ sind release-gebundene Artefakte: Sie gehören exakt zu dem Code-Zustand, aus dem sie erzeugt wurden. vendor/ enthält die Composer-Abhängigkeiten in den Versionen, die in composer.lock festgelegt sind. generated/ enthält den DI-Code, der aus dem Zusammenspiel von vendor/ und app/code erzeugt wurde. Beide Verzeichnisse müssen als Teil des Build-Artefakts behandelt werden und zusammen mit dem Code in das Release-Verzeichnis transferiert werden.
Ein häufiger Fehler: vendor/ wird als Shared-Pfad behandelt, um den Deployment-Transfer zu beschleunigen. Das funktioniert, solange Composer-Abhängigkeiten sich nicht ändern, bricht aber beim ersten Deployment, bei dem ein Modul hinzukommt oder eine Version geändert wird – und zwar für alle Releases gleichzeitig, weil der Shared-vendor-Pfad von allen Releases gleichzeitig verwendet wird. Das macht den Rollback unmöglich, weil der alte Code auf die neue vendor/-Version trifft.
5. Shared-Symlinks korrekt setzen
Das Setzen von Shared-Symlinks ist ein kritischer Schritt im Deployment-Prozess, der in der richtigen Reihenfolge erfolgen muss: nach dem Übertragen des Artefakts, aber vor dem Umschalten des current-Symlinks. Die Reihenfolge ist wichtig, weil ein partiell verlinkter Release-Pfad – manche Shared-Symlinks gesetzt, andere noch nicht – inkonsistente Zustände erzeugt, die zu schwer zu diagnostizierenden Fehlern führen.
Die Shared-Verzeichnisse auf dem Server müssen beim ersten Deployment manuell angelegt und mit initialen Daten befüllt werden. Die app/etc/env.php muss vor dem ersten Deployment in shared/app/etc/ abgelegt werden – sie enthält Datenbankverbindungsdaten, Redis-Konfiguration und andere umgebungsspezifische Einstellungen, die niemals im Repository stehen dürfen. Ein Skript, das diese Initialisierung einmalig ausführt, sollte Teil der Server-Provisioning-Dokumentation sein.
# Server initialization script (run once during initial setup)
# This creates the shared directory structure for the first time
.init_shared_structure: &init_shared
script:
- |
ssh "${DEPLOY_USER}@${DEPLOY_HOST}" bash << 'REMOTE'
set -euo pipefail
BASE="${DEPLOY_PATH}"
SHARED="${BASE}/shared"
# Create base directory structure
mkdir -p "${BASE}/releases"
mkdir -p "${SHARED}/pub/media"
mkdir -p "${SHARED}/var/log"
mkdir -p "${SHARED}/var/session"
mkdir -p "${SHARED}/app/etc"
# Set correct permissions for web-accessible directories
chmod 775 "${SHARED}/pub/media"
chmod 770 "${SHARED}/var/log"
chmod 770 "${SHARED}/var/session"
# Verify that env.php exists before first deployment
if [ ! -f "${SHARED}/app/etc/env.php" ]; then
echo "ERROR: ${SHARED}/app/etc/env.php does not exist."
echo "Copy env.php to shared directory before first deployment."
exit 1
fi
echo "Shared directory structure initialized successfully"
ls -la "${SHARED}/"
REMOTE
6. .gitignore und .gitattributes für saubere Artefakte
Die .gitignore-Datei des Repositories muss alle Build-Artefakte und Laufzeitdaten ausschließen: vendor/, generated/, pub/static/, var/, pub/media. Diese Verzeichnisse haben im Repository nichts zu suchen – sie werden entweder in der Build-Stage erzeugt (vendor, generated, pub/static) oder sind Laufzeitdaten, die persistent auf dem Server liegen (pub/media, var/). Ein Repository, das vendor/ enthält, verletzt das Prinzip der reproduzierbaren Builds und verlangsamt jeden Git-Prozess erheblich.
Die .gitattributes-Datei ermöglicht es, das Verhalten von git archive zu steuern und verhindert, dass Testdaten, Entwicklungstools und CI-spezifische Konfigurationen in das Repository-Export-Archiv gelangen. Für Magento-Projekte empfiehlt sich, alle Dateien auszuschließen, die nicht für den Betrieb des Shops benötigt werden: Tests, Entwicklungstools, .github-Ordner, .editorconfig und ähnliche Dateien.
7. rsync-Excludes beim Deployment
Der rsync-Befehl im Deploy-Job muss explizit alle Verzeichnisse ausschließen, die entweder als Shared-Pfade eingehängt werden oder die nicht aus dem Build-Artefakt stammen. Die wichtigsten Excludes sind pub/media, var/log, var/session, var/cache, var/tmp und app/etc/env.php. Ohne diese Excludes würde rsync beim ersten Deployment ein leeres pub/media-Verzeichnis übertragen, das dann den Shared-Symlink überdeckt.
Die rsync-Option --delete stellt sicher, dass Dateien, die im Build-Artefakt nicht mehr vorhanden sind, auch auf dem Server gelöscht werden. Das ist für den Wechsel zwischen Releases wichtig – andernfalls akkumulieren sich Dateien aus alten Releases im neuen Release-Verzeichnis. Mit den korrekten Excludes ist --delete sicher einsetzbar, ohne persistente Daten zu gefährden.
8. Auswirkungen auf den Rollback-Pfad
Die saubere Trennung der drei Dateikategorien hat direkte Auswirkungen auf den Rollback-Pfad. Wenn pub/media als Shared-Pfad korrekt implementiert ist, verliert ein Rollback keine Media-Dateien – Bilder, die nach dem letzten Code-Release hochgeladen wurden, sind nach dem Rollback weiterhin verfügbar. Wenn pub/static als Release-Artefakt korrekt im Release-Verzeichnis liegt, zeigt der Code nach dem Rollback wieder die statischen Dateien aus dem alten Build.
Ein Rollback ist dann technisch simpel: Den current-Symlink auf das vorangegangene Release-Verzeichnis setzen, den Cache leeren und den Verify-Job ausführen. Weder Media-Dateien noch Datenbankdaten sind betroffen – nur der Code und die ihm zugehörigen Build-Artefakte wechseln. Das ist das Kernversprechen des Release-Modells mit Shared-Symlinks: Rollback ist eine Operation, die in Sekunden ausgeführt werden kann, ohne Datenverlust und ohne manuelle Eingriffe.
9. Deployment-Ansätze im Vergleich
Wie eine falsche Kategorisierung von Magento-Dateien die Deployment-Zuverlässigkeit beeinträchtigt, zeigt sich besonders deutlich im direkten Vergleich zwischen einem flachen Deployment ohne Trennung und einem Release-Modell mit korrekten Shared-Symlinks.
| Verzeichnis | Falscher Ansatz | Korrekter Ansatz | Rollback-Auswirkung |
|---|---|---|---|
| pub/media | Teil des Build-Artefakts | Shared-Symlink | Ohne Shared: Bilder nach Deploy-Datum verloren |
| pub/static | Shared-Pfad (alle Releases teilen) | Release-Artefakt im Release-Dir | Shared: Rollback lässt alten Code mit neuem CSS laufen |
| vendor/ | Shared-Pfad (Übertragung vermeiden) | Release-Artefakt im Release-Dir | Shared: Composer-Update betrifft alle Releases gleichzeitig |
| generated/ | Auf Server regeneriert | Build-Job, als Artefakt | Server-Regenerierung: Fehler erst auf Production sichtbar |
| app/etc/env.php | Im Repository eingecheckt | Shared-Symlink aus shared/ | Im Repo: Secrets exponiert, Environment-Mix möglich |
Die Tabelle macht deutlich, dass jede falsche Kategorisierung eine andere Art von Problem erzeugt. Pub/media als Build-Artefakt führt zu Datenverlust. pub/static als Shared-Pfad führt zu Layout-Inkonsistenzen. vendor/ als Shared-Pfad macht Rollbacks unmöglich. generated/ auf dem Server führt zu späten Fehlererkennungen. Die korrekten Ansätze lösen alle diese Probleme gleichzeitig – sie sind nicht unabhängig voneinander, sondern ergänzen sich zu einem kohärenten Deployment-Modell.
10. Zusammenfassung
Die sinnvolle Trennung von Magento Media, pub/static und Build-Artefakten basiert auf der Erkenntnis, dass diese drei Kategorien unterschiedliche Lebenszyklen haben. pub/media ist persistent und release-unabhängig – Shared-Symlink. pub/static ist ein Build-Artefakt, das zum Code gehört – Teil des Release-Verzeichnisses. vendor/ und generated/ sind release-gebundene Build-Artefakte – ebenfalls Teil des Release-Verzeichnisses. app/etc/env.php ist eine umgebungsspezifische Konfiguration – Shared-Symlink.
Wer diese Trennung konsequent umsetzt, erhält Rollbacks, die in Sekunden ausführbar sind, Deployments, die keine Media-Dateien unnötig übertragen, und eine klare Verantwortung für jede Datei im System. Die Build-Stage erzeugt nur, was aus dem Code hervorgeht. Der Deploy-Job transferiert nur, was zum Release gehört. Shared-Symlinks verbinden den Release mit dem, was persistent bleiben muss. So entsteht ein Deployment-Modell, das unter Produktionsdruck zuverlässig funktioniert.
Magento Dateikategorien — Das Wichtigste auf einen Blick
pub/media — Shared
Persistent über alle Releases. Shared-Symlink aus shared/pub/media/. Rollback berührt Media-Dateien nie – kein Datenverlust durch Code-Wechsel.
pub/static — Release-Artefakt
Gebunden an den Build-Zustand des Codes. Liegt im Release-Verzeichnis, nicht im Shared-Pfad. Rollback stellt alten CSS-Stand wieder her.
vendor/ + generated/ — Release
Exakt zu einem Code-Zustand gehörend. Als Build-Artefakt in das Release-Verzeichnis übertragen. Shared wäre ein Rollback-Blocker.
env.php — Shared Konfiguration
Umgebungsspezifisch, nie im Repository. Shared-Symlink aus shared/app/etc/. Alle Releases nutzen dieselbe Umgebungskonfiguration.