CI/CD
.yml
GitLab · Magento · DI Compile · Build-Prozess
setup:di:compile im Build-Prozess:
Timing, Caches und Generated Code

setup:di:compile ist einer der zeitintensivsten Schritte im Magento-Build-Prozess — und einer der am häufigsten falsch platzierten. Zu früh ausgeführt fehlt die vollständige Composer-Basis; zu spät ausgeführt verzögert es den Deploy. Dieser Artikel zeigt, wann und wie di:compile in einer GitLab-Pipeline am besten läuft.

14 Min. Lesezeit di:compile · generated/ · Artefakte · Caching · Fehlerdiagnose Magento 2.4 · PHP 8.4 · GitLab 16+

1. Was setup:di:compile tatsächlich tut

setup:di:compile ist der Magento-Befehl, der den Dependency-Injection-Framework kompiliert. Er analysiert alle Magento-Module, liest ihre di.xml-Konfigurationen, löst Abhängigkeiten auf und generiert PHP-Code, der zur Laufzeit der Anwendung verwendet wird. Das Ergebnis ist das generated/-Verzeichnis mit Proxy-Klassen, Factories, Interceptors und Preference-Mappings. Ohne diesen generierten Code würde Magento entweder deutlich langsamer laufen (weil Klassen dynamisch generiert werden) oder bei bestimmten DI-Konfigurationen gar nicht starten.

Der Prozess ist zeitintensiv: Auf einem typischen Magento-Projekt mit rund 150 aktiven Modulen dauert setup:di:compile zwischen zwei und fünf Minuten, abhängig von der Server-Leistung und der PHP-Version. In PHP 8.4 mit JIT ist er etwas schneller als in PHP 7.4, aber er ist und bleibt ein erheblicher Anteil der Gesamtbuild-Zeit. Das ist der erste Grund, warum er im CI/CD-Build-Prozess sorgfältig positioniert werden muss: einmal falsch platziert, verlängert er die Pipeline unnötig oder muss im schlimmsten Fall mehrfach ausgeführt werden.

Ein häufiges Missverständnis: setup:di:compile benötigt keine laufende Datenbank. Es liest nur PHP-Code und XML-Konfigurationen. Das macht es ideal für den Build-Job in der CI-Pipeline, der in einem isolierten Docker-Container ohne Datenbankverbindung läuft. Was es jedoch benötigt: eine vollständige vendor/-Installation. Es muss also nach composer install, aber vor dem Paketieren der Artefakte laufen.

2. Timing: wann in der Pipeline di:compile laufen muss

Die korrekte Reihenfolge in der Magento-Build-Pipeline ist eindeutig: zuerst composer install, dann setup:di:compile, dann der Frontend-Build (Tailwind CSS, NPM), dann setup:static-content:deploy (wenn im Build-Job), dann das Paketieren der Artefakte. di:compile muss nach composer install laufen, weil es Zugriff auf alle Klassen im vendor/-Verzeichnis benötigt. Fehlen Klassen, scheitert der Compile-Prozess mit einem ClassNotFoundException oder einem fehlerhaften DI-Graph.

setup:static-content:deploy kann — muss aber nicht — nach di:compile im Build-Job laufen. Wenn Static Content Deploy im Build-Job läuft und das pub/static/-Verzeichnis als Artefakt verpackt wird, entfällt dieser Schritt im Deploy-Job. Das spart Zeit auf dem Produktionsserver, erhöht aber die Artefakt-Größe. Viele Teams delegieren setup:static-content:deploy an den Deploy-Job, weil der Befehl Store-View-spezifische Daten benötigt, die vom Server abhängen. Für reine Magento-Setups ohne Store-View-spezifische Abhängigkeiten im Build-Kontext kann er aber zuverlässig im Build-Job laufen.

3. generated/ als Build-Artefakt behandeln

Das generated/-Verzeichnis ist ein Artefakt des Build-Prozesses: Es wird aus Quellcode generiert und muss nicht in Git versioniert werden. In einer Symlink-Release-Struktur kommt das generated/-Verzeichnis aus dem Build-Artefakt und wird ins Release-Verzeichnis platziert. Es darf nicht im Shared-Verzeichnis liegen, weil verschiedene Releases möglicherweise unterschiedliche generierte Klassen haben. Jeder Release trägt sein eigenes generated/-Verzeichnis mit sich.

In der GitLab-Pipeline wird das generated/-Verzeichnis im artifacts-Block des Build-Jobs deklariert. GitLab speichert es nach dem Build-Job und stellt es im Deploy-Job zur Verfügung. Die Größe des Artefakts ist ein kritischer Faktor: Ein typisches generated/-Verzeichnis umfasst 50–150 MB an PHP-Dateien. Zusammen mit vendor/ (200–400 MB) und pub/static/ (optional, 100–500 MB) können Artefakte schnell 500 MB bis 1 GB groß werden. Große Artefakte verlangsamen den Upload nach dem Build-Job und den Download vor dem Deploy-Job erheblich.

4. Caching-Strategien für di:compile in GitLab

GitLab-Caches unterscheiden sich von Artefakten: Artefakte werden zwischen Jobs in derselben Pipeline weitergegeben. Caches werden zwischen verschiedenen Pipeline-Läufen wiederverwendet. Für di:compile ist ein direktes Caching des generated/-Verzeichnisses nicht empfehlenswert, weil die generierten Klassen exakt zum aktuellen Stand des vendor/- und App-Code passen müssen. Ein alter Cache aus einem anderen Commit kann zu subtilen DI-Fehlern führen, die schwer zu debuggen sind.

Was sich dagegen gut cachen lässt: Das vendor/-Verzeichnis aus composer install (Cache-Key: Hash der composer.lock). Wenn composer.lock sich nicht geändert hat, kann der Build-Job den Composer-Cache nutzen und composer install deutlich beschleunigen. Damit verkürzt sich die Gesamt-Build-Zeit für Commits ohne Dependency-Änderungen erheblich, weil der zeitintensive Download-Schritt entfällt. di:compile selbst läuft dann auf dem bereits vorhandenen vendor/-Verzeichnis, was die Gesamtzeit von typischerweise 8–12 Minuten auf 3–5 Minuten reduzieren kann.

5. Vollständiger Build-Job mit di:compile

Die folgende Konfiguration zeigt einen vollständigen Build-Job für Magento 2.4 mit PHP 8.4, der setup:di:compile korrekt positioniert, Caching für Composer nutzt und das generated/-Verzeichnis als Artefakt bereitstellt. Die Kommentare erläutern die Entscheidungen hinter der Reihenfolge.

# .gitlab-ci.yml — Magento build stage with setup:di:compile
stages:
  - build
  - test
  - package
  - deploy
  - verify

variables:
  GIT_STRATEGY: fetch
  COMPOSER_HOME: "/tmp/composer"
  COMPOSER_CACHE_DIR: ".cache/composer"
  PHP_MEMORY_LIMIT: "2G"

build:magento:
  stage: build
  image: php:8.4-cli
  before_script:
    # Install system dependencies for Magento build
    - apt-get update -qq && apt-get install -y -qq git unzip libzip-dev libicu-dev
    - docker-php-ext-install zip intl bcmath pdo_mysql
    # Inject Composer auth credentials — never hardcode in .gitlab-ci.yml
    - mkdir -p "$COMPOSER_HOME"
    - echo "$COMPOSER_AUTH" > "$COMPOSER_HOME/auth.json"
  script:
    # Step 1: Install PHP dependencies — must run before di:compile
    - composer install --no-dev --prefer-dist --no-interaction --no-progress \
        --optimize-autoloader
    # Step 2: DI compilation — runs after full vendor/ is available
    - php -d memory_limit="${PHP_MEMORY_LIMIT}" bin/magento setup:di:compile
    # Step 3: Static content deploy — runs after di:compile (uses generated classes)
    - php bin/magento setup:static-content:deploy de_DE en_US \
        -t Mironsoft/default --jobs=4 -f
    # Step 4: Frontend build (Tailwind CSS)
    - npm ci --prefix app/design/frontend/Mironsoft/default/web/tailwind
    - npm run build --prefix app/design/frontend/Mironsoft/default/web/tailwind
    # Remove auth credentials before artifact packaging
    - rm -f "$COMPOSER_HOME/auth.json"
  after_script:
    # Cleanup sensitive data even on failure
    - rm -f "/tmp/composer/auth.json"
  cache:
    # Cache vendor/ keyed by composer.lock hash — reuse on unchanged dependencies
    key:
      files:
        - composer.lock
    paths:
      - .cache/composer/
    policy: pull-push
  artifacts:
    paths:
      - vendor/
      - generated/
      - pub/static/
    expire_in: 4 hours
    # Exclude test files from artifact to reduce size
    exclude:
      - vendor/**/Test/**
      - vendor/**/Tests/**
      - vendor/**/*.md
  tags:
    - build
    - php84
    - docker

Zwei Details in dieser Konfiguration sind besonders wichtig: Erstens läuft setup:static-content:deploy nach setup:di:compile, nicht davor. Static Content Deploy erzeugt in manchen Magento-Versionen Zugriffe auf generierte Klassen (z.B. Übersetzungs-Klassen aus generierten Code-Strukturen). Zweitens ist --optimize-autoloader in composer install aktiviert: Das verbessert die Autoload-Performance in Production erheblich und sollte in Build-Jobs immer gesetzt sein. Der Parameter --jobs=4 bei static-content:deploy parallelisiert die Asset-Generierung und nutzt die verfügbaren CPU-Kerne des Build-Containers.

6. Häufige Compile-Fehler und ihre Ursachen

Der häufigste Fehler bei setup:di:compile in CI-Umgebungen ist ein Memory-Limit-Problem. Im Standard läuft php mit dem PHP-ini-Standardwert, der oft bei 128 MB oder 256 MB liegt. di:compile benötigt für große Magento-Installationen mit vielen Drittmodulen zwischen 512 MB und 2 GB RAM. Ohne explizites Memory-Limit schlägt der Prozess mit einem Allowed memory size exhausted-Fehler fehl. Die Lösung: php -d memory_limit=2G vor dem Befehl setzen oder in der php.ini des Docker-Images konfigurieren.

Der zweite häufige Fehler ist eine fehlende PHP-Extension im Build-Container. di:compile analysiert PHP-Klassen aus allen Modulen, darunter häufig solche, die Extensions wie intl, bcmath oder soap voraussetzen. Fehlt die Extension im Container, schlägt der Compile-Prozess mit einem Fatal error: Uncaught Error: Call to undefined function fehl. Die Lösung: im Build-Job-Image alle PHP-Extensions installieren, die Magento im Betrieb benötigt — nicht nur die, die Composer für die Installation braucht.

7. generated/ und Zero-Downtime-Deployment

In einer Symlink-Release-Struktur ist das generated/-Verzeichnis Teil des Release-Verzeichnisses. Beim atomaren Symlink-Wechsel wechselt der current-Symlink von einem Release-Verzeichnis zum nächsten. Das neue Release-Verzeichnis enthält das vollständige generated/-Verzeichnis aus dem Build-Artefakt. Damit ist sichergestellt, dass das neu aktive Release immer das zur Codeversion passende generierte Code-Set hat — kein manuelles Löschen und Neuerstellen von generated/ auf dem Server nötig.

Ein kritischer Punkt: Auf dem Produktionsserver darf das generated/-Verzeichnis nicht im Shared-Verzeichnis liegen. Wenn zwei Releases gleichzeitig aktiv sein könnten (was im atomaren Symlink-Switch nicht der Fall ist, aber in anderen Deployment-Modellen vorkommt), würden sich verschiedene Versionen des generierten Codes gegenseitig überschreiben. Im Symlink-Modell ist dieses Problem strukturell ausgeschlossen: Jedes Release-Verzeichnis trägt sein eigenes generated/-Verzeichnis, und der aktive Symlink zeigt immer auf genau eines davon.

8. Vergleich: di:compile im Build vs. auf dem Server

Es gibt zwei Ansätze, wo setup:di:compile im Deployment-Prozess ausgeführt wird. Der Build-in-CI-Ansatz führt es einmalig im Build-Job aus und verteilt das Ergebnis über das Artefakt. Der On-Server-Ansatz führt es auf jedem Zielserver direkt vor dem Symlink-Wechsel aus. Beide haben Vor- und Nachteile.

Aspekt Build in CI (empfohlen) Auf dem Server
Reproduzierbarkeit Einmalig, konsistent für alle Server Abhängig von Server-PHP-Version und -Extensions
Deploy-Dauer Kürzer (kein Compile auf Server) Länger (2–5 Min. Compile pro Server)
Fehler-Erkennung Im Build-Job — vor dem Deploy Erst auf dem Server — zu spät
Artefakt-Größe Größer (generated/ enthalten) Kleiner (kein generated/ im Artefakt)
Multi-Server-Deploy Identisches generated/ auf allen Servern Compile läuft auf jedem Server separat

Für nahezu alle produktiven Magento-Setups ist der Build-in-CI-Ansatz vorzuziehen: Fehler in der DI-Konfiguration werden bereits im Build-Job entdeckt, bevor ein Deploy überhaupt startet. Bei Multi-Server-Setups (Web-Cluster) läuft der Compile-Prozess genau einmal, statt auf jedem Server einzeln. Das verkürzt das Deployment-Fenster erheblich. Der einzige stichhaltige Grund für On-Server-Compilation: wenn der Build-Container eine wesentlich andere PHP-Umgebung hat als der Produktionsserver — was ein separates Problem ist und durch eine konsistente PHP-Umgebung in CI und Production behoben werden sollte.

9. Typische Fehler im Build-Prozess

Ein verbreiteter Fehler ist das Ausführen von setup:di:compile ohne vorheriges composer install oder mit einer unvollständigen Vendor-Installation. Wenn composer install mit --no-dev ausgeführt wird, fehlen Development-only-Abhängigkeiten — das ist gewollt. Wenn es aber ohne --no-plugins ausgeführt wird und ein Plugin scheitert, kann die Vendor-Installation unvollständig sein, was di:compile mit kryptischen Fehler beendet. Lösung: immer erst prüfen, ob composer install sauber durchgelaufen ist, bevor di:compile startet. Der Exit-Code von composer install signalisiert das zuverlässig.

Ein zweiter Fehler ist das Cachen des generated/-Verzeichnisses über Pipeline-Runs hinweg mit einem zu generischen Cache-Key. Wenn der Cache-Key nicht den aktuellen Commit-Hash oder den Hash aller Module enthält, kann ein veraltetes generated/-Verzeichnis aus dem Cache verwendet werden, das nicht zum aktuellen Code passt. Das kann dazu führen, dass Magento auf dem Server mit einem Interceptor-Set startet, das zu einer veralteten Klassen-Hierarchie gehört — ein subtiler Fehler, der oft erst bei bestimmten Benutzeraktionen sichtbar wird.

10. Zusammenfassung

setup:di:compile gehört in den Build-Job der GitLab-Pipeline, nach composer install und vor der Artefakt-Paketierung. Das generierten Code-Verzeichnis wird als Build-Artefakt behandelt und mit dem Release-Verzeichnis auf den Server gebracht — nicht manuell auf dem Server regeneriert. Compile-Fehler werden so im Build-Job entdeckt, bevor ein Deploy überhaupt startet. Memory-Limit explizit setzen (php -d memory_limit=2G), alle benötigten PHP-Extensions im Build-Container installieren und --optimize-autoloader für die Composer-Installation aktivieren.

Caching des generated/-Verzeichnisses zwischen Pipeline-Runs ist nicht sinnvoll, weil das Verzeichnis exakt zum aktuellen Commit-Stand passen muss. Composer-Cache hingegen ist sinnvoll und verkürzt Build-Zeiten für Commits ohne Dependency-Änderungen erheblich. Mit dieser Konfiguration ist setup:di:compile ein zuverlässiger, reproduzierbarer Bestandteil des Build-Prozesses — und nicht eine Fehlerquelle auf dem Produktionsserver.

setup:di:compile im Build-Prozess — Das Wichtigste auf einen Blick

Reihenfolge

composer install → di:compile → static-content:deploy → Frontend-Build. Nie di:compile vor composer install ausführen.

Memory-Limit

php -d memory_limit=2G bin/magento setup:di:compile — ohne explizites Limit schlägt di:compile bei großen Setups fehl.

generated/ als Artefakt

Im Artefakt-Block deklarieren und mit dem Release-Verzeichnis deployen. Nie im Shared-Verzeichnis. Nie zwischen Pipeline-Runs cachen.

Fehler-Erkennung

DI-Kompilierungsfehler im Build-Job — vor dem Deploy. Nie erst auf dem Server entdecken. Build-Job schlägt fehl, kein Deploy startet.

11. FAQ: setup:di:compile im GitLab Build-Prozess

1Warum nach composer install?
di:compile analysiert alle Klassen in vendor/. Fehlen diese, scheitert der Prozess mit ClassNotFoundException. Immer erst composer install abschließen.
2Wie viel Memory braucht di:compile?
512 MB bis 2 GB je nach Modulanzahl. Explizit mit php -d memory_limit=2G setzen. Nicht auf php.ini-Defaults verlassen.
3generated/ zwischen Runs cachen?
Nicht empfohlen. Veralteter Cache aus anderem Commit führt zu subtilen DI-Fehlern. Nur Composer-Cache (Lock-File-Hash als Key) sinnvoll cachen.
4static-content:deploy vor oder nach di:compile?
Nach di:compile. Static Content Deploy kann auf generierte Klassen zugreifen. Reihenfolge: composer install → di:compile → static-content:deploy.
5Warum Compile im CI besser als auf dem Server?
Fehler werden vor dem Deploy erkannt. Bei Multi-Server-Setups läuft Compile einmal statt auf jedem Server. Reproduzierbar und unabhängig vom Server-PHP.
6generated/ im Shared-Verzeichnis?
Nein. Verschiedene Releases müssen eigene generated/-Verzeichnisse haben. Rollback würde sonst aktuellen generated/-Code überschreiben und DI-Fehler verursachen.
7Wie lange dauert di:compile in CI?
2–5 Minuten für Standard-Magento mit 100–150 Modulen. Größte Zeitfresser: Module-Discovery und Interceptor-Generation für viele Plugin-Ketten.
8Welche PHP-Extensions für di:compile?
Mindestens zip, intl, bcmath, pdo_mysql. Bestes Vorgehen: Docker-Image mit denselben Extensions wie Produktionsserver verwenden.
9Was macht --optimize-autoloader?
Erstellt optimierte Classmap statt PSR-4-Lookups. Reduziert Dateisystem-Zugriffe pro Request erheblich. In Build-Jobs immer aktivieren.
10di:compile parallel für mehrere Stores?
Nein. Globaler Prozess der gesamten Magento-Installation. Berücksichtigt alle Websites und Stores. Einmalig ausführen — Parallelisierung nicht vorgesehen.