CI/CD
.yml
GitLab · Magento Deployment · Build-Artefakte · Reproduzierbarkeit
Warum man nicht auf Production buildet:
Artefakte, Reproduzierbarkeit und Geschwindigkeit

Ein Build-Job auf Production ist kein Deployment – er ist ein unkontrolliertes Experiment auf dem Live-System. Artefakte aus isolierten CI-Jobs bringen Reproduzierbarkeit, Geschwindigkeit und Sicherheit gleichzeitig.

12 Min. Lesezeit Artefakte · Build-Stage · Composer · DI-Compile · Cache Magento 2.4 · PHP 8.4 · GitLab CI/CD

1. Das Problem: Build auf Production als verstecktes Risiko

Wer composer install auf dem Produktionsserver ausführt, vermischt zwei grundlegend verschiedene Aufgaben: den Build-Prozess und das Deployment. Der Build erzeugt Abhängigkeiten, kompiliert Code und generiert Assets. Das Deployment überträgt einen definierten Zustand auf den Server. Wenn beides zusammenfällt, entsteht ein gefährlicher Graubereich: Der Serverzustand während des Builds ist weder vollständig alt noch vollständig neu. Laufende PHP-FPM-Prozesse greifen auf Dateien zu, die gerade durch Composer überschrieben werden.

Das Risiko ist nicht nur theoretisch. Ein composer install auf Production dauert abhängig vom Netzwerk und dem Server typischerweise zwischen 30 Sekunden und mehreren Minuten. In dieser Zeit können Requests auf halbfertige vendor/-Verzeichnisse treffen. Magento reagiert darauf mit fatalen Fehlern, die sich nicht immer sofort zeigen. Ein setup:di:compile auf Production ist noch riskanter: Er regeneriert den gesamten generierten Code, während die Anwendung antwortet. Das Ergebnis sind 500er, Race Conditions und schwer debugbare Zustandsfehler.

Der Kern des Problems liegt in der fehlenden Trennung von Build-Umgebung und Produktionsumgebung. Production sollte niemals Composer, npm oder Node installiert haben müssen. Wenn diese Tools auf Production vorhanden sind, ist das ein Zeichen dafür, dass der Build-Prozess nicht sauber isoliert ist. Die Lösung ist konsequente Artefakt-Orientierung im GitLab-Workflow.

2. Was ein Build-Artefakt wirklich bedeutet

Ein Build-Artefakt ist das vollständige, unveränderliche Ergebnis eines Build-Prozesses. Es enthält alles, was für den Betrieb der Anwendung benötigt wird: vendor/, generated/, pub/static/ und app/etc/config.php. Es enthält explizit nicht, was umgebungsspezifisch ist: app/etc/env.php, Datenbankverbindungen, API-Keys. Diese Trennung ist der Kern des Artefakt-Ansatzes.

Ein Artefakt ist genau dann korrekt gebaut, wenn es ohne Zugriff auf den Zielserver und ohne Kenntnis der Produktionskonfiguration erstellt werden kann. Das bedeutet: Der Build-Job in GitLab CI läuft in einem Docker-Container mit definierten PHP- und Node-Versionen, installiert Abhängigkeiten aus gesperrten Versionen (composer.lock, package-lock.json) und kompiliert alle generierten Dateien. Das fertige Artefakt wird als GitLab-Artefakt gespeichert und von nachfolgenden Jobs – Test und Deploy – konsumiert.

3. Reproduzierbarkeit: Derselbe Build, jederzeit

Reproduzierbarkeit bedeutet, dass derselbe Commit heute und in sechs Monaten dasselbe Artefakt erzeugt. Das setzt voraus, dass der Build-Job deterministisch ist: festgelegte PHP-Version, festgelegte Composer-Version, composer.lock committet, package-lock.json committet. Wenn der Build auf einem nicht versionierten Server läuft, wo PHP-Patches eingespielt werden und Composer-Versionen stillschweigend wechseln, ist Reproduzierbarkeit nicht erreichbar.

In der Praxis zeigt sich Reproduzierbarkeit im Debugging. Wenn ein Production-Problem auf einen bestimmten Release zurückgeführt werden soll, muss das genaue Artefakt dieses Releases verfügbar oder reproduzierbar sein. Mit einem sauber isolierten Build-Job kann das Artefakt jederzeit aus dem Commit neu erzeugt werden. Mit einem Build auf Production ist das Artefakt das Ergebnis des Serverzustands zum Zeitpunkt des Builds – und dieser Zustand ist nicht versioniert und nicht reproduzierbar.

# Reproducible Magento build job — pinned tools, locked dependencies
build:magento:
  stage: build
  # Pinned Docker image ensures same PHP version across all builds
  image: php:8.4-cli-alpine
  variables:
    COMPOSER_VERSION: "2.7"
  before_script:
    - apk add --no-cache git unzip nodejs npm bash
    # Install pinned Composer version for reproducibility
    - curl -sS https://getcomposer.org/installer | php -- \
        --install-dir=/usr/local/bin \
        --filename=composer \
        --version=$COMPOSER_VERSION
    - composer --version
  script:
    # composer.lock must be committed — ensures same dependency versions
    - composer install --no-dev --prefer-dist --no-interaction \
        --optimize-autoloader --no-scripts
    - composer run-script post-install-cmd --no-interaction
    # package-lock.json must be committed — ensures same npm versions
    - npm ci --prefix app/design/frontend/Mironsoft/default/web/tailwind
    - npm run build --prefix app/design/frontend/Mironsoft/default/web/tailwind
    # Generate DI and static content in CI, not on production
    - php bin/magento setup:di:compile
    - php bin/magento setup:static-content:deploy de_DE -f --jobs=4
  artifacts:
    name: "magento-$CI_COMMIT_SHORT_SHA"
    paths:
      - vendor/
      - generated/
      - pub/static/
      - app/etc/config.php
    exclude:
      - vendor/**/.git
      - vendor/**/test/**
      - vendor/**/tests/**
    expire_in: 3 days

4. Geschwindigkeit: Warum Artefakte schneller sind

Ein häufiges Missverständnis: Der Build auf Production sei schneller, weil der Server schon vorhanden sei. Das Gegenteil ist wahr. Ein Build auf Production konkurriert mit laufenden PHP-Prozessen um CPU und I/O. Composer-Downloads hängen von der Netzwerkverbindung des Servers ab. Der DI-Compile-Schritt blockiert für Minuten die Generierung von Code, der für laufende Requests benötigt wird.

Ein GitLab-Build-Job läuft parallel zu anderen Pipeline-Jobs, nutzt dedizierte CI-Ressourcen und profitiert vom Composer-Cache zwischen Runs. Wenn sich composer.lock nicht geändert hat, lädt der Build-Job keine einzige Abhängigkeit herunter – sie sind alle gecacht. Das Artefakt wird einmal gebaut und kann auf beliebig viele Server deployt werden, ohne den Build-Prozess zu wiederholen. Bei Multi-Server-Setups ist dieser Unterschied besonders deutlich: Ein Build auf drei Production-Servern kostet dreimal die Build-Zeit, ein Artefakt wird einmal gebaut und dreimal deployt.

5. Aufbau eines Magento-Artefakts in GitLab

Das Magento-Artefakt besteht aus vier Kernkomponenten. vendor/ enthält alle PHP-Abhängigkeiten nach dem composer install. generated/ enthält den Dependency-Injection-generierten Code aus dem setup:di:compile. pub/static/ enthält die deployte statische Assets aller aktiven Themes. app/etc/config.php enthält die Modul-Aktivierungsliste, die von setup:upgrade generiert und committed werden sollte.

Was nicht ins Artefakt gehört: app/etc/env.php mit Datenbankverbindungen und Secrets, pub/media/ mit hochgeladenen Medien, var/ mit Logs und Sessions. Diese Dateien leben im shared/-Verzeichnis auf dem Server und werden per Symlink in jeden Release eingebunden. Die scharfe Trennung zwischen dem, was im Artefakt ist, und dem, was auf dem Server liegt, ist die Voraussetzung für einen sicheren Symlink-Switch und für reproduzierbare Deployments.

6. Composer-Cache und Artefakt-Größe kontrollieren

Ohne Cache-Konfiguration lädt jeder Build-Job alle Composer-Pakete erneut herunter. Das kostet Zeit und Bandbreite. GitLab-Pipeline-Caches lösen dieses Problem: Der .cache/composer/-Ordner wird zwischen Builds zwischengespeichert, sodass sich ändernde Pakete nur einmal heruntergeladen werden. Der Cache-Key sollte an $CI_COMMIT_REF_SLUG oder den Hash von composer.lock gebunden sein, damit Cache-Inkompatibilitäten nach Dependency-Updates vermieden werden.

Die Artefakt-Größe ist ein weiterer Kontrollpunkt. Ein unkomprimiertes vendor/-Verzeichnis kann bei großen Magento-Installationen 500 MB oder mehr umfassen. Die exclude-Direktive in der Artefakt-Konfiguration entfernt Test-Verzeichnisse und .git-Unterordner aus Paketen, was die Größe typischerweise um 20–30 % reduziert. Die expire_in-Einstellung steuert, wie lange das Artefakt in GitLab gespeichert bleibt – drei Tage sind für die meisten Workflows ausreichend.

# Composer and npm cache configuration for faster builds
variables:
  COMPOSER_CACHE_DIR: "$CI_PROJECT_DIR/.cache/composer"
  NPM_CONFIG_CACHE: "$CI_PROJECT_DIR/.cache/npm"

cache:
  # Separate cache per branch to avoid cross-branch conflicts
  key:
    files:
      - composer.lock
      - app/design/frontend/Mironsoft/default/web/tailwind/package-lock.json
  paths:
    - .cache/composer/
    - .cache/npm/
  policy: pull-push

# Artifact size reduction: exclude test directories and git metadata
build:magento:
  artifacts:
    paths:
      - vendor/
      - generated/
      - pub/static/
      - app/etc/config.php
    exclude:
      # Remove vendor test suites — not needed in production
      - "vendor/**/Test/**"
      - "vendor/**/Tests/**"
      - "vendor/**/.git/**"
      - "vendor/**/docs/**"
    expire_in: 3 days
    when: on_success

7. Deploy-Job ohne Build-Tools auf dem Server

Das Ziel ist ein Produktionsserver, auf dem kein Composer, kein npm und kein Node installiert ist. Der Deploy-Job benötigt nur SSH-Zugang, rsync für die Dateiübertragung und die Magento-CLI (bin/magento) für Deploy-spezifische Schritte wie cache:flush. Der Build-Job hat alles vorbereitet; der Deploy-Job überträgt nur und verlinkt.

Dieser Ansatz hat einen wichtigen Sicherheitsvorteil: Ein Server ohne Build-Tools ist schwerer angreifbar als ein Server mit installierten Entwicklertools. Ein Angreifer, der Zugang zu einem Server mit Composer erlangt, kann deutlich mehr Schaden anrichten als auf einem Server, der nur PHP-FPM und Nginx betreibt. Die Trennung von Build und Deploy ist also nicht nur eine Frage der Reproduzierbarkeit, sondern auch eine Sicherheitsmaßnahme.

8. Typische Fehler beim Artefakt-Ansatz

Der häufigste Fehler beim Umstieg auf den Artefakt-Ansatz ist das Vergessen von app/etc/config.php im Artefakt. Diese Datei wird von setup:upgrade generiert und enthält die Liste der aktivierten Module. Ohne sie kann Magento nach dem Deploy nicht starten. Ein zweiter typischer Fehler: app/etc/env.php landet versehentlich im Artefakt und wird auf alle Umgebungen deployt – inklusive Production-Datenbankverbindungen, die auf Staging nichts zu suchen haben.

Ein dritter Fehler betrifft die expire_in-Einstellung. Wenn das Artefakt abläuft, bevor der Deploy-Job es konsumiert, schlägt der Deploy mit einem kryptischen "artifact not found"-Fehler fehl. Die Ablaufzeit muss so gewählt sein, dass auch bei manuellen Freigaben und Wartezeiten das Artefakt noch verfügbar ist. Für Workflows mit manueller Production-Freigabe sind drei bis fünf Tage ein realistischer Wert.

9. Build auf Production vs. Artefakt-Ansatz im Vergleich

Der Vergleich zwischen Build auf Production und dem Artefakt-Ansatz zeigt, dass es keine Grauzone gibt. Build auf Production ist in professionellen Magento-Deployments kein akzeptabler Kompromiss, sondern ein Anti-Pattern, das durch den Artefakt-Ansatz vollständig ersetzt werden kann.

Kriterium Build auf Production Artefakt-Ansatz (GitLab CI) Vorteil
Downtime-Risiko Hoch (Race Conditions) Minimal (atomarer Switch) Stabile Antwortzeiten während Deploy
Reproduzierbarkeit Serverzustandsabhängig Vollständig deterministisch Gleicher Commit = gleicher Build
Build-Dauer Variabel, belastet Server Gecacht, isoliert Kein Einfluss auf Production-Last
Server-Anforderungen Composer, npm, Node nötig Nur PHP-FPM + Nginx Kleinere Angriffsfläche
Multi-Server-Deploy Build × Anzahl Server Build einmal, N-mal deployt Linear skalierbar

Die wichtigste Erkenntnis aus dem Vergleich: Der Artefakt-Ansatz ist nicht komplexer als Build auf Production – er ist sauberer. Die initiale Einrichtung des Build-Jobs und der Artefakt-Konfiguration kostet einige Stunden. Im laufenden Betrieb spart sie Stunden pro Deploy und verhindert Vorfälle, die bei Build auf Production regelmäßig auftreten.

10. Zusammenfassung

Der Kern der Argumentation gegen Build auf Production ist klar: Es vermischt zwei getrennte Prozesse, erzeugt Race Conditions, ist nicht reproduzierbar und erfordert Build-Tools auf dem Produktionsserver. Der Artefakt-Ansatz mit GitLab CI trennt Build und Deploy sauber: Der Build läuft einmal in einem isolierten Container mit definierten Versionen, das Artefakt wird in GitLab gespeichert, und der Deploy-Job überträgt das Artefakt atomar auf den Server.

Für Magento ist diese Trennung besonders wichtig, weil der DI-Compile-Schritt und die Static-Content-Generierung zeitaufwändig sind und auf Production unkontrollierte Seiteneffekte haben. Ein sauber aufgebautes GitLab-Artefakt enthält alle generierten Dateien und braucht auf Production nur noch deployt und per Symlink aktiviert zu werden. Das ist der Unterschied zwischen einem Deployment-Prozess, der zufällig funktioniert, und einem, der reproduzierbar zuverlässig ist.

Build-Artefakte für Magento — Das Wichtigste auf einen Blick

Artefakt-Inhalt

vendor/, generated/, pub/static/, app/etc/config.php – nicht env.php, nicht pub/media/, nicht var/.

Reproduzierbarkeit

Gepinntes Docker-Image, composer.lock und package-lock.json committet, deterministischer Build ohne Servereinfluss.

Geschwindigkeit

Composer-Cache zwischen Builds, DI-Compile einmalig im CI, kein Build-Overhead auf Production.

Sicherheit

Kein Composer, npm oder Node auf Production – kleinere Angriffsfläche, klare Trennung von Build- und Laufzeitumgebung.

11. FAQ: Warum man nicht auf Production buildet

1Größtes Risiko beim Build auf Production?
Race Conditions: Requests treffen auf halbfertige vendor/-Verzeichnisse während Composer-Install läuft. Führt zu fatalen Fehlern, die sich nicht sofort zeigen.
2Composer auf Production notwendig?
Nein. Das Artefakt enthält alle Abhängigkeiten. Composer läuft nur im CI-Build-Job, nicht auf Production.
3Was ist mit app/etc/env.php?
Gehört nicht ins Artefakt. Liegt im shared/-Verzeichnis auf dem Server, wird per Symlink eingebunden. So bleiben Secrets umgebungsspezifisch.
4Wie groß wird ein Magento-Artefakt?
300–800 MB unkomprimiert. Mit exclude-Direktiven für Test-Verzeichnisse und .git-Ordner 20–30 % Reduktion möglich.
5expire_in wie lange setzen?
Mindestens so lange, wie zwischen Build und Deploy vergehen kann. Bei manuellen Freigaben mindestens 3 Tage.
6Dasselbe Artefakt auf mehrere Server?
Ja. Einmal gebaut, beliebig oft deployt. Kein Wiederholung des Build-Prozesses für jeden Server.
7Unterschied Composer-Cache vs. Artefakt?
Cache beschleunigt zukünftige Builds. Artefakt ist das Ergebnis des Build-Jobs und wird vom Deploy-Job konsumiert. Beides unabhängig voneinander.
8setup:di:compile immer im Build-Job?
Ja. Ohne DI-Compile sind Requests signifikant langsamer. DI-Compile gehört immer ins Artefakt, nie auf Production.
9env.php aus Artefakt ausschließen?
env.php in .gitignore aufnehmen und nie committen. In der artifacts-Konfiguration per exclude-Direktive explizit ausschließen.
10Mindest-Artefakt für Magento?
vendor/, generated/, pub/static/ und app/etc/config.php. Ohne eines dieser Verzeichnisse startet Magento nicht oder arbeitet mit Degraded Performance.