richtig in den Prozess einbauen
Static Content Deployment auf dem Produktionsserver auszuführen bedeutet: lange Wartungsfenster, serverabhängige Build-Ergebnisse und unkontrollierbare Laufzeiten. Der richtige Platz für SCD ist der CI-Build-Job – mit Hyvä Tailwind-Build, setup:di:compile und pub/static als kontrollierbares Artefakt, das auf jeden Server deployt werden kann.
Inhaltsverzeichnis
- 1. Was Static Content Deployment in Magento macht
- 2. Warum SCD in den CI-Build gehört, nicht auf den Server
- 3. Korrekte Reihenfolge: di:compile vor SCD
- 4. Hyvä Theme und Tailwind CSS in GitLab CI bauen
- 5. Was ins Artefakt gehört: pub/static und was nicht
- 6. var/view_preprocessed: löschen oder nicht?
- 7. SCD auf Server vs. SCD im CI-Build im Vergleich
- 8. Cache-Verhalten nach SCD-Deployment
- 9. Static Content beim Rollback
- 10. Zusammenfassung
- 11. FAQ
1. Was Static Content Deployment in Magento macht
Der Befehl bin/magento setup:static-content:deploy – kurz SCD – generiert alle statischen Dateien, die Magento für das Frontend benötigt: CSS, JavaScript, Fonts, Bilder, Less-kompilierte Stylesheets und Template-Dateien, die in pub/static abgelegt werden. Die Ausführung kann je nach Anzahl der Themes, Locales und Module mehrere Minuten dauern. Während des SCD-Laufs sind die statischen Dateien im Übergangszustand – teilweise die alten, teilweise die neuen Versionen – was zu fehlerhaften Browser-Darstellungen führen kann, wenn Benutzer die Seite genau in diesem Moment laden.
SCD ist keine optionale Operation. Jede Änderung an Frontend-Code, Theme-Dateien oder CSS erfordert einen erneuten SCD-Lauf. Wer SCD überspringt und hofft, dass Magento die Dateien on-demand generiert, wird feststellen, dass Magento im Production-Mode keine dynamische Kompilierung durchführt. Das ist ein häufiger Fehler beim ersten Zero-Downtime-Versuch: Der Deploy läuft durch, der Symlink wechselt, aber die Seite zeigt fehlendes CSS und defekte JS-Bundles, weil SCD nicht ausgeführt wurde.
Die entscheidende Frage ist nicht ob SCD ausgeführt werden muss, sondern wann und wo: im CI-Build-Job, der das Artefakt erzeugt, oder auf dem Server während des Deployments. Die Antwort hat weitreichende Konsequenzen für Deployment-Dauer, Reproduzierbarkeit und Wartungsfenster-Länge.
2. Warum SCD in den CI-Build gehört, nicht auf den Server
SCD auf dem Produktionsserver auszuführen hat drei fundamentale Nachteile. Erstens verlängert es das Wartungsfenster erheblich: Wenn SCD drei Minuten läuft und in dieser Zeit der Maintenance Mode aktiv ist oder die alten Static Files schon gelöscht wurden, sind drei Minuten Downtime. Zweitens ist das Ergebnis serverabhängig: Die PHP-Version, die installierten Extensions und die Less-Compiler-Version beeinflussen den SCD-Output. Auf einem anderen Server – zum Beispiel nach einer Migration – sieht der Output anders aus. Drittens kann SCD auf dem Server nicht einfach wiederholt werden, ohne ein weiteres Deployment durchzuführen.
Im CI-Build-Job dagegen läuft SCD in einer kontrollierten Docker-Umgebung mit definierten PHP- und Node-Versionen. Das Ergebnis ist reproduzierbar: Derselbe Commit erzeugt mit demselben Docker-Image dieselben statischen Dateien. Der Build-Job kann beliebig oft wiederholt werden, ohne den Server zu berühren. Und das pub/static-Verzeichnis ist Teil des Artefakts, das nach Staging und – per Promotion – nach Production übertragen wird. SCD läuft einmal, das Ergebnis wird mehrfach deployt.
3. Korrekte Reihenfolge: di:compile vor SCD
setup:di:compile muss vor setup:static-content:deploy laufen. Der DI-Compiler generiert den generated/-Ordner, der Interceptor-Klassen, Factory-Klassen und andere Code-Generierungsartefakte enthält. SCD verlässt sich auf diese generierten Klassen, um Template-Dateien korrekt zu resolven und Plugin-spezifische JavaScript-Konfigurationen zu ermitteln. Wenn SCD vor di:compile läuft, fehlen Teile der generierten Code-Basis, und das Static Content Deployment kann unvollständige oder fehlerhafte Dateien erzeugen.
Beim Hyvä Theme mit Tailwind CSS kommt eine dritte Abhängigkeit hinzu: Der Tailwind-Build muss laufen, bevor SCD ausgeführt wird, weil Hyvä die kompilierte CSS-Datei als reguläre Theme-Datei einbindet und SCD diese in pub/static verteilt. Die korrekte Reihenfolge im Build-Job lautet also: Composer Install, npm ci und Tailwind-Build, dann di:compile, dann SCD. Wer diese Reihenfolge nicht einhält, bekommt ein pub/static, das Verweise auf CSS enthält, die noch nicht kompiliert wurden – und damit einen defekten Build, der sich erst auf dem Server als Problem zeigt.
build:magento:
stage: build
image: php:8.4-cli
variables:
COMPOSER_CACHE_DIR: .cache/composer
NPM_CONFIG_CACHE: .cache/npm
cache:
key: "$CI_COMMIT_REF_SLUG"
paths:
- .cache/composer
- .cache/npm
before_script:
- apt-get update -qq && apt-get install -y -qq git unzip nodejs npm libzip-dev
- docker-php-ext-install zip pdo_mysql bcmath intl
- curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
script:
# Step 1: PHP dependencies
- composer install --no-dev --prefer-dist --no-interaction --quiet
# Step 2: Frontend build — Tailwind must run before SCD
- npm ci --prefix app/design/frontend/Mironsoft/default/web/tailwind
- npm run build --prefix app/design/frontend/Mironsoft/default/web/tailwind
# Step 3: DI compile — must run before SCD
- php bin/magento setup:di:compile --quiet
# Step 4: Static content — runs after DI and Tailwind are ready
- rm -rf var/view_preprocessed pub/static/frontend
- php bin/magento setup:static-content:deploy de_DE en_US \
-t Mironsoft/default \
--force \
--jobs $(nproc) \
--quiet
artifacts:
paths:
- vendor/
- generated/
- pub/static/
- app/etc/config.php
exclude:
- pub/static/**/*.map
expire_in: 7 days
only:
- main
- tags
4. Hyvä Theme und Tailwind CSS in GitLab CI bauen
Hyvä Themes setzen auf Tailwind CSS v4 mit einem CSS-First-Ansatz. Die tailwind.config.js und die Eingangs-CSS-Datei befinden sich unter app/design/frontend/Mironsoft/default/web/tailwind/. Der Build-Befehl – typischerweise npm run build – erzeugt die kompilierte CSS-Datei, die Magento dann in pub/static distribuiert. Im CI-Build-Job muss Node.js in der korrekten Version verfügbar sein – hier empfiehlt sich ein Docker-Image, das sowohl PHP 8.4 als auch Node 20+ enthält, oder ein Multi-Stage-Build-Ansatz mit separaten Images.
Ein wichtiger Punkt beim Hyvä-Build: Der Tailwind CSS Purge-Schritt analysiert alle PHP-Templates nach verwendeten CSS-Klassen. Wenn während des Purge-Laufs Templates fehlen – zum Beispiel weil nur ein Teil des Source-Codes im Build-Kontext vorhanden ist – werden CSS-Klassen aus dem finalen Bundle entfernt, die tatsächlich verwendet werden. Das führt zu fehlenden Styles auf der Seite, ohne dass ein Build-Fehler signalisiert wird. Im CI-Build-Job muss deshalb das vollständige Repository inklusive aller Templates im Workspace verfügbar sein, bevor der Tailwind-Build startet.
5. Was ins Artefakt gehört: pub/static und was nicht
Das Build-Artefakt für ein Magento-Deployment mit CI-SCD enthält: vendor/, generated/, pub/static/ und app/etc/config.php. Was explizit nicht ins Artefakt gehört: app/etc/env.php (enthält datenbankspezifische Credentials, kommt aus Shared-Verzeichnis), pub/media/ (user-generated content, im Shared-Verzeichnis), var/ (Logs, Sessions, Cache – serverabhängig), var/view_preprocessed/ (Zwischenergebnis von SCD, nicht für den Server gedacht).
Source Maps (*.map-Dateien) können aus dem Artefakt ausgeschlossen werden, wenn sie auf Production nicht gebraucht werden. Das reduziert die Artefaktgröße signifikant bei wenig Verlust. Für Debugging-Zwecke kann man Source Maps als separates Artefakt mit längerer Retention ablegen, das nur bei Bedarf heruntergeladen wird. Die app/etc/config.php enthält die Liste aktivierter Module und sollte Teil des Artefakts sein – sie ist umgebungsunabhängig und steuert, welche Module Magento beim Start berücksichtigt.
6. var/view_preprocessed: löschen oder nicht?
Das Verzeichnis var/view_preprocessed ist ein interner Cache-Ordner, den Magento während des SCD-Prozesses als Zwischenspeicher nutzt. Wenn man SCD im CI-Build-Job ausführt, sollte var/view_preprocessed auf dem Server nicht aus einem vorherigen Deployment übernommen werden. Der Inhalt ist spezifisch für den letzten lokalen SCD-Lauf und kann zu Inkonsistenzen führen, wenn er mit einem neuen pub/static-Stand gemischt wird.
Die empfohlene Strategie: Im Build-Job wird var/view_preprocessed vor dem SCD-Lauf gelöscht (rm -rf var/view_preprocessed), damit ein sauberer SCD-Lauf stattfindet. Das Verzeichnis kommt nicht ins Artefakt. Auf dem Zielserver liegt var/view_preprocessed im Shared-Verzeichnis, das zwischen Releases geteilt wird. Nach dem Release-Wechsel kann var/view_preprocessed auf dem Server entweder geleert werden oder so bleiben – da es nur ein Zwischencache ist und Magento es bei Bedarf neu befüllt, ist beides vertretbar. Für maximale Sauberkeit: nach dem Symlink-Wechsel leeren.
7. SCD auf Server vs. SCD im CI-Build im Vergleich
Die Entscheidung zwischen SCD auf dem Server und SCD im CI-Build ist eine der wichtigsten Architekturentscheidungen im Magento-Deployment-Prozess. Sie bestimmt die Downtime-Charakteristik des Deployments, die Reproduzierbarkeit und die Komplexität des Rollbacks.
| Kriterium | SCD auf dem Server | SCD im CI-Build-Job | Empfehlung |
|---|---|---|---|
| Wartungsfenster | Länger (SCD-Laufzeit auf Server) | Minimal (nur Symlink-Wechsel) | CI-Build |
| Reproduzierbarkeit | Serverabhängig | Kontrollierte CI-Umgebung | CI-Build |
| CPU-Last auf Production | Hoch während Deploy | Keine | CI-Build |
| Rollback von Static | SCD erneut ausführen nötig | Altes Release-Verzeichnis hat altes pub/static | CI-Build für Rollback besser |
| Artefaktgröße | Klein (kein pub/static) | Größer (pub/static enthalten) | Tradeoff akzeptabel |
Die größere Artefaktgröße bei CI-SCD ist der einzige reale Nachteil. Pub/static kann je nach Anzahl der Themes und Locales 50–300 MB groß werden. Das ist beherrschbar mit externem Artefakt-Storage und Source-Map-Ausschluss. Die Vorteile – minimales Wartungsfenster, reproduzierbarer Build, einfacher Rollback – überwiegen bei weitem.
8. Cache-Verhalten nach SCD-Deployment
Nach dem Symlink-Wechsel und dem Deployen eines neuen pub/static muss der Magento-Cache geleert werden. Der full_page-Cache (wenn Varnish oder Magento-internes FPC genutzt wird) enthält möglicherweise gecachte HTML-Seiten, die Referenzen auf Dateipfade des alten Static Contents enthalten. Wenn der neue Static Content unter neuen Hash-Pfaden liegt – Magento erzeugt versionsbasierte Pfade in pub/static/version[hash]/ – zeigen gecachte Seiten auf Pfade, die im neuen Release nicht mehr existieren.
Das cache:flush-Kommando nach dem Symlink-Wechsel ist deshalb nicht optional. Bei Varnish-Integration muss zusätzlich der Varnish-Cache geleert werden – entweder per varnishadm ban.url '.' oder über Magentos eingebaute Varnish-Invalidierung per PURGE-Requests. Wer einen CDN (Cloudflare, Fastly) vor Magento betreibt, muss nach dem Deployment ebenfalls die relevanten Pfade invalidieren, damit Nutzer nicht die gecachten statischen Dateien der alten Version erhalten.
9. Static Content beim Rollback
Einer der stärksten Argumente für CI-SCD ist das Rollback-Verhalten. Wenn jedes Release-Verzeichnis sein eigenes pub/static enthält – als Teil des Artefakts – ist ein Rollback auf ein beliebiges vorheriges Release trivial: Der Symlink zeigt auf das alte Release-Verzeichnis, und das alte pub/static ist automatisch aktiv. Kein erneuter SCD-Lauf, kein Wartungsfenster für die Rollback-Operation.
Wenn SCD hingegen auf dem Server läuft und pub/static ein Shared-Verzeichnis ist (das zwischen Releases geteilt wird), überschreibt jeder neue Deploy den Inhalt. Ein Rollback bedeutet dann, SCD erneut für das alte Release auszuführen – was Minuten dauert und während dieser Zeit inkonistente Static Files erzeugt. Mit CI-SCD und per-Release-pub/static ist Rollback eine Millisekunden-Operation: der ln -sfn-Befehl. Der Cache-Flush danach dauert Sekunden. Das ist die Rollback-Qualität, die Zero Downtime ermöglicht.
deploy:production:
stage: deploy
before_script:
- eval $(ssh-agent -s)
- ssh-add "$SSH_PRIVATE_KEY"
- mkdir -p ~/.ssh && echo "$SSH_KNOWN_HOSTS" > ~/.ssh/known_hosts
script:
- |
ssh "$DEPLOY_USER@$DEPLOY_HOST" bash -s <<'REMOTE'
set -euo pipefail
readonly RELEASE_ID="$(date +%Y%m%d-%H%M%S)"
readonly RELEASE_PATH="$DEPLOY_PATH/releases/$RELEASE_ID"
readonly SHARED="$DEPLOY_PATH/shared"
# Create new release directory and unpack artifact
mkdir -p "$RELEASE_PATH"
cd "$RELEASE_PATH"
tar -xzf "/tmp/$ARTIFACT_NAME" 2>/dev/null
# Link shared directories — env.php, media, logs, sessions
ln -sfn "$SHARED/app/etc/env.php" app/etc/env.php
rm -rf pub/media var/log var/session
ln -sfn "$SHARED/pub/media" pub/media
ln -sfn "$SHARED/var/log" var/log
ln -sfn "$SHARED/var/session" var/session
# Run schema upgrades if needed
bin/magento setup:upgrade --keep-generated --no-interaction
# Switch symlink atomically — pub/static already in release dir from CI build
ln -sfn "$RELEASE_PATH" "$DEPLOY_PATH/current"
# Flush cache — full_page cache must be cleared after static content switch
bin/magento cache:flush
echo "[OK] Release $RELEASE_ID deployed"
# Cleanup old releases — keep last 5
ls -1dt "$DEPLOY_PATH/releases"/*/ | tail -n +6 | xargs rm -rf
REMOTE
environment:
name: production
only:
- tags
when: manual
10. Zusammenfassung
Static Content Deployment in Magento gehört in den CI-Build-Job, nicht auf den Produktionsserver. Die Begründung ist technisch und operativ: Die Build-Umgebung ist kontrollierbar und reproduzierbar, die Deployment-Laufzeit auf dem Server wird minimal, und der Rollback ist eine Millisekunden-Operation, weil jedes Release sein eigenes pub/static mitbringt. Die korrekte Reihenfolge – Composer Install, Tailwind-Build, di:compile, SCD – ist nicht verhandelbar, weil jeder Schritt von den Ergebnissen des vorherigen abhängt.
Hyvä Themes mit Tailwind CSS verstärken dieses Argument: Der Tailwind-Build ist ein Node.js-Prozess, der auf einem PHP-Server nichts zu suchen hat. Er gehört in die CI-Pipeline, wo Node in der richtigen Version vorhanden ist und das Ergebnis als stabile Datei Teil des Artefakts wird. Wer diesen Prozess einmal sauber aufgebaut hat, deployt schneller, sicherer und mit einem klaren Rollback-Pfad – unabhängig davon, ob das Deployment auf Staging oder auf Production stattfindet.
Static Content Deployment in GitLab CI — Das Wichtigste auf einen Blick
Reihenfolge
Composer Install, Tailwind-Build, di:compile, dann SCD. Jeder Schritt abhängig vom vorherigen – Reihenfolge nicht änderbar.
Artefakt-Inhalt
vendor/, generated/, pub/static/, app/etc/config.php. Keine env.php, keine pub/media, kein var/view_preprocessed.
Hyvä + Tailwind
npm ci + npm run build vor di:compile und SCD. Node.js im Build-Docker-Image – nicht auf dem Server. Vollständige Templates für korrekten Purge.
Rollback-Vorteil
Per-Release pub/static ermöglicht Rollback per Symlink-Wechsel ohne SCD-Wiederholung. Cache flush danach – fertig.