CI/CD
.yml
GitLab · Magento Deployment · CI/CD · Zero Downtime
Komplette .gitlab-ci.yml für Magento
Build, Test, Package, Deploy, Verify

Eine .gitlab-ci.yml, die nur drei Jobs hat und keine Stages definiert, ist kein Deployment-Prozess – sie ist ein Skript, das irgendwann bricht. Wer Magento sauber deployt, braucht eine vollständige Pipeline-Struktur: reproduzierbare Build-Artefakte, definierte Freigaben, serverseitige Release-Verzeichnisse, kontrollierte Magento-Schritte und einen fachlich abgeschlossenen Verify-Job.

15 Min. Lesezeit Build · Test · Package · Deploy · Verify · Rollback Magento 2.4 · PHP 8.4 · GitLab 17+

1. Warum eine vollständige Pipeline-Struktur nötig ist

Eine .gitlab-ci.yml für Magento ist kein technisches Detail am Rande des Projekts, sondern das zentrale Dokument des gesamten Deployment-Prozesses. Sie definiert, was in welcher Reihenfolge passiert, wer eine Freigabe erteilt, welche Artefakte erzeugt werden und wie das System nach einem fehlgeschlagenen Deploy zurückgesetzt wird. Wer diese Datei als Sammlung loser Befehle behandelt, baut ein System, das bei jedem abweichenden Release-Fall versagt – nicht wegen der Technologie, sondern wegen fehlender Prozessstruktur.

In Magento-Projekten ist die Pipeline-Struktur besonders kritisch, weil mehrere voneinander abhängige Systeme beteiligt sind: Composer-Abhängigkeiten, generierter DI-Code, kompilierte Frontend-Assets, Datenbankmigrationen, Static Content, Shared-Dateien und Cache-Schichten. Wenn diese Schritte nicht in einer klaren Reihenfolge mit definierten Abhängigkeiten ausgeführt werden, entsteht ein Deployment, das zufällig funktioniert – aber nicht nachvollziehbar.

Das Ziel einer vollständigen .gitlab-ci.yml ist nicht, jeden möglichen Fall abzudecken, sondern den Standardpfad so explizit zu machen, dass Abweichungen sofort sichtbar werden. Eine Pipeline, die einen Fehler im Build-Job transparent meldet, ist wertvoller als eine Pipeline, die still bis zum Deploy-Job läuft und dann auf Production bricht.

2. Stages definieren: die logische Reihenfolge des Deployments

Die stages-Deklaration am Anfang der .gitlab-ci.yml ist mehr als Dokumentation – sie legt fest, in welcher Reihenfolge Jobs ausgeführt werden und welche Stage abgeschlossen sein muss, bevor die nächste beginnt. Für Magento empfiehlt sich eine Struktur aus mindestens sechs Stages: build, test, package, deploy, verify und rollback. Diese Reihenfolge spiegelt den tatsächlichen Ablauf des Deployments wider und macht ihn für das gesamte Team sichtbar.

Die variables-Sektion am Anfang der Datei definiert projektweite Einstellungen wie Cache-Verzeichnisse, Git-Strategie und Standardwerte. Diese Werte werden von allen Jobs geerbt und können auf Job-Ebene überschrieben werden. Eine klare Trennung zwischen Variablen, die in der YAML-Datei stehen dürfen, und Variablen, die als CI/CD-Variable in GitLab hinterlegt sein müssen, ist Pflicht – Secrets dürfen niemals in der YAML-Datei selbst stehen.

# .gitlab-ci.yml — Complete Magento pipeline structure
stages:
  - build
  - test
  - package
  - deploy
  - verify
  - rollback

# Global pipeline variables (non-secret)
variables:
  GIT_STRATEGY: fetch
  GIT_DEPTH: "10"
  COMPOSER_CACHE_DIR: ".cache/composer"
  NPM_CONFIG_CACHE: ".cache/npm"
  MAGENTO_LOCALE: "de_DE"
  RELEASE_RETENTION: "5"
  DEPLOY_TIMEOUT: "300"

# Reusable SSH setup anchor
.ssh_setup: &ssh_setup
  before_script:
    - eval $(ssh-agent -s)
    - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
    - mkdir -p ~/.ssh && chmod 700 ~/.ssh
    - echo "$SSH_KNOWN_HOSTS" >> ~/.ssh/known_hosts
    - chmod 644 ~/.ssh/known_hosts

Die include-Direktive erlaubt es, die Pipeline auf mehrere Dateien aufzuteilen und gemeinsame Job-Templates in eigenen Dateien zu pflegen. Für Magento-Projekte mit mehreren Umgebungen bietet sich eine Trennung in eine gemeinsame Basis und umgebungsspezifische Overrides an. Anchors und YAML-Merging (<<: *anchor) reduzieren Wiederholungen innerhalb einer Datei, lösen aber das Problem der dateiübergreifenden Wiederverwendung nicht – dafür ist include zuständig.

3. Der Build-Job: Artefakte statt Serverzustand

Der Build-Job ist das Herzstück der reproduzierbaren Pipeline. Er führt alle Schritte aus, die notwendig sind, um aus dem Repository-Inhalt ein vollständiges, deployfähiges Artefakt zu erzeugen – ohne Abhängigkeit vom Zustand eines bestimmten Servers. Das bedeutet: composer install mit --no-dev und --prefer-dist, der Frontend-Build mit npm und Tailwind CSS, sowie setup:di:compile für die Magento Dependency Injection.

Artefakte werden über die artifacts-Sektion des Jobs definiert. GitLab übergibt diese Dateien automatisch an nachfolgende Jobs in der Pipeline. Die Auflistung der Artefakt-Pfade muss präzise sein: zu wenige Pfade bedeuten, dass nachfolgende Jobs auf fehlende Dateien treffen; zu viele bedeuten, dass unnötig große Artefakte zwischen Jobs übertragen werden. Das Ablaufdatum (expire_in) verhindert, dass alte Artefakte den Speicher des GitLab-Servers belasten.

# Build stage — produces all deployment artifacts from source
build:magento:
  stage: build
  image: php:8.4-cli
  cache:
    key: "${CI_COMMIT_REF_SLUG}-composer"
    paths:
      - .cache/composer/
    policy: pull-push
  before_script:
    - apt-get update -qq && apt-get install -y -qq git unzip libzip-dev
    - docker-php-ext-install zip
    - curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
    - node --version && npm --version
  script:
    # Install PHP dependencies without dev packages
    - composer install --no-dev --prefer-dist --no-interaction --optimize-autoloader
    # Build Tailwind CSS assets
    - npm ci --prefix app/design/frontend/Mironsoft/default/web/tailwind
    - npm run build --prefix app/design/frontend/Mironsoft/default/web/tailwind
    # Compile Magento DI (generates interceptors and factories)
    - bin/magento setup:di:compile
    # Deploy static content for configured locales
    - bin/magento setup:static-content:deploy "$MAGENTO_LOCALE" -f --jobs=4
  artifacts:
    name: "magento-build-${CI_COMMIT_SHORT_SHA}"
    paths:
      - vendor/
      - generated/
      - pub/static/
      - app/etc/config.php
    exclude:
      - vendor/**/.git
    expire_in: 1 day
  only:
    - main
    - /^release\/.*/

4. Der Test-Job: Qualitätssicherung vor dem Deployment

Der Test-Job läuft nach dem Build-Job und hat Zugriff auf dessen Artefakte. Er führt statische Code-Analysen und automatische Tests aus, die auf dem gebauten Code – nicht auf dem Repository-Rohzustand – basieren. Das ist wichtig, weil manche Fehler erst nach setup:di:compile sichtbar werden: falsch typisierte Constructor-Parameter, fehlende Klassen in generated/ oder Plugin-Konflikte, die erst zur Compile-Zeit aufgedeckt werden.

Die wichtigsten Test-Jobs für Magento sind PHPStan mit Magento-spezifischen Regeln, PHPCS mit dem Magento-Coding-Standard und Unit-Tests mit PHPUnit. PHPStan auf Level 6 oder höher findet Typ-Fehler, die in einem typisierten PHP-8.4-Projekt keine Ausnahme mehr sein dürfen. PHPCS stellt sicher, dass neuer Code den Magento-Standards entspricht und keine offensichtlichen Qualitätsprobleme eingebracht werden. All diese Checks sollten mit einem Non-Zero-Exit-Code fehlschlagen, damit die Pipeline bei Verstößen blockiert wird.

5. Der Package-Job: Artefakt versiegeln und signieren

Der Package-Job bündelt die Build-Artefakte zu einem einzelnen, versiegelten Paket, das in den Deploy-Job übertragen wird. Für Magento empfiehlt sich ein tar.gz-Archiv, das alle notwendigen Verzeichnisse enthält und mit einer Checksumme (SHA-256) versehen wird. Diese Checksumme ermöglicht es, die Integrität des Pakets vor dem Deployment zu verifizieren und sie im Nachhinein nachzuweisen.

Im Package-Job werden außerdem Metadaten erzeugt: Release-ID (Timestamp-basiert oder aus dem Git-Tag), Commit-SHA, Branch-Name und Pipeline-ID. Diese Metadaten werden in einer release.json-Datei im Paket abgelegt und ermöglichen es, auf dem Server jederzeit nachzuvollziehen, aus welchem Pipeline-Lauf ein bestimmter Release stammt. Das ist bei Rollbacks und Incident-Analysen unverzichtbar.

6. Der Deploy-Job: Release-Wechsel auf dem Zielserver

Der Deploy-Job überträgt das Paket auf den Zielserver und wechselt den aktuellen Release-Symlink. Er ist bewusst als when: manual für Production konfiguriert – das erzwingt eine explizite Freigabe durch eine autorisierte Person, bevor Code auf der produktiven Umgebung aktiviert wird. Für Staging kann der Job automatisch laufen, sobald Test und Package erfolgreich abgeschlossen sind.

Der eigentliche Deployment-Prozess folgt dem Symlink-Modell: Das neue Release wird in ein timestampbasiertes Verzeichnis unter releases/ entpackt, Shared-Dateien (env.php, pub/media, var/log) werden als Symlinks eingehängt, und erst wenn alle Vorbereitungen abgeschlossen sind, wird der current-Symlink atomar auf das neue Release umgestellt. Die Webserver-Konfiguration zeigt immer auf current – der Symlink-Wechsel ist die eigentliche Zero-Downtime-Operation.

# Deploy stage — transfers artifact and switches release symlink
deploy:production:
  stage: deploy
  <<: *ssh_setup
  environment:
    name: production
    url: https://shop.example.com
  script:
    - |
      # Calculate release identifier from pipeline timestamp
      RELEASE_ID=$(date +%Y%m%d-%H%M%S)
      RELEASE_PATH="${DEPLOY_PATH}/releases/${RELEASE_ID}"

      # Transfer build artifact to release directory
      ssh "${DEPLOY_USER}@${DEPLOY_HOST}" "mkdir -p ${RELEASE_PATH}"
      rsync -az --delete \
        --exclude=".git" \
        --exclude="var/cache" \
        ./ "${DEPLOY_USER}@${DEPLOY_HOST}:${RELEASE_PATH}/"

      # Link shared paths and activate release on server
      ssh "${DEPLOY_USER}@${DEPLOY_HOST}" bash << 'REMOTE'
        set -euo pipefail
        cd "${RELEASE_PATH}"

        # Link persistent shared files into new release
        ln -sfn "${DEPLOY_PATH}/shared/app/etc/env.php" app/etc/env.php
        ln -sfn "${DEPLOY_PATH}/shared/app/etc/config.php" app/etc/config.php
        ln -sfn "${DEPLOY_PATH}/shared/pub/media" pub/media
        ln -sfn "${DEPLOY_PATH}/shared/var/log" var/log
        ln -sfn "${DEPLOY_PATH}/shared/var/session" var/session

        # Run database migrations (only if schema changes exist)
        bin/magento setup:upgrade --keep-generated

        # Flush all cache types before activating
        bin/magento cache:flush

        # Atomic symlink switch — this is the zero-downtime moment
        ln -sfn "${RELEASE_PATH}" "${DEPLOY_PATH}/current"

        # Remove releases exceeding retention limit
        ls -dt "${DEPLOY_PATH}"/releases/*/ | tail -n +$((RELEASE_RETENTION + 1)) | xargs rm -rf
      REMOTE
  when: manual
  only:
    - main
    - tags

7. Der Verify-Job: fachlicher Abschluss der Pipeline

Der Verify-Job schließt die Pipeline fachlich ab. Ein Deploy-Job, der mit Exit-Code 0 endet, bedeutet nur, dass die SSH-Befehle erfolgreich ausgeführt wurden – nicht, dass der Shop tatsächlich korrekt funktioniert. Der Verify-Job führt HTTP-Checks gegen den Live-Shop aus, prüft den Cache-Status auf dem Server und verifiziert, dass kritische Seiten ohne Fehler antworten. Wenn der Verify-Job fehlschlägt, ist das ein Signal, dass sofort der Rollback-Job ausgeführt werden muss.

Mindestens drei Prüfungen sollte der Verify-Job enthalten: Ein HTTP-200-Check gegen den Health-Endpoint, ein Check der Startseite und ein Blick in das Magento-Fehlerlog auf dem Server. Erweitert werden kann der Job durch Checks gegen kritische Kategorieseiten, den Checkout-Flow und die Admin-Oberfläche. Je mehr Checks im Verify-Job vorhanden sind, desto früher wird ein Problem nach einem Deployment erkannt – bevor Kunden es melden.

8. Rollback: der geübte Rückweg

Der Rollback-Job ist kein Notfallplan, sondern ein regulärer Bestandteil der Pipeline. Er wird als when: manual konfiguriert und kann jederzeit nach einem Deploy-Job ausgeführt werden – idealerweise innerhalb von Sekunden, nicht Minuten. Der Rollback-Mechanismus ist simpel: Der current-Symlink wird auf das vorangegangene Release-Verzeichnis zurückgestellt, der Cache wird geleert, und der Verify-Job läuft erneut.

Das Rollback-Modell funktioniert jedoch nur dann zuverlässig, wenn Datenbankmigrationen abwärtskompatibel sind – ein Rollback des Codes bei einer inkompatiblen Datenbank-Schemaänderung ist kein echtes Rollback. Deshalb sollte setup:upgrade in der Pipeline immer das Expand-Contract-Muster einhalten: Neue Spalten werden zuerst optional, alte Spalten erst in einem späteren Release entfernt. So bleibt der Rollback-Pfad für Dateiänderungen immer offen.

9. Pipeline-Muster im Vergleich

Der Unterschied zwischen einer improvisierten und einer strukturierten .gitlab-ci.yml für Magento zeigt sich besonders deutlich, wenn eine Pipeline zum ersten Mal unter ungewohnten Bedingungen läuft – ein neuer Entwickler, ein neuer Server oder ein abweichender Branch. Strukturierte Pipelines sind explizit genug, um in diesen Situationen zuverlässig zu funktionieren.

Aspekt Improvisierte Pipeline Strukturierte Pipeline Auswirkung
Stage-Reihenfolge Build und Deploy in einem Job Explizite stages-Deklaration Sichtbare Abhängigkeiten und Freigabepunkte
Artefakte Kein artifact, Build auf Server Artefakt aus Build-Job, übergeben Reproduzierbarkeit und Trennung Build/Deploy
Freigabe Production Automatisch bei jedem Push when: manual für Production Kontrollierter Deploy, kein versehentlicher Release
Verify-Schritt Keiner vorhanden HTTP-Check + Cache-Check + Log-Check Fehler erkannt bevor Kunden betroffen sind
Rollback Manuell, kein geübter Pfad Rollback-Job, when: manual Rückweg in Sekunden, nicht Stunden

Die Tabelle zeigt, dass die Unterschiede nicht in der Komplexität einzelner Befehle liegen, sondern in der Explizitheit des Prozesses. Eine strukturierte .gitlab-ci.yml kostet mehr Arbeit beim initialen Design, aber sie verhindert die hektischen Notoperationen, die bei improvisierten Pipelines regelmäßig anfallen. Der Return on Investment liegt darin, dass jeder Deploy planbar und nachvollziehbar abläuft – nicht nur der erste.

10. Zusammenfassung

Eine komplette .gitlab-ci.yml für Magento besteht aus sechs Stages, die den tatsächlichen Ablauf des Deployments widerspiegeln: Build erzeugt reproduzierbare Artefakte aus dem Repository. Test prüft den gebauten Code gegen statische Analysen und Unit-Tests. Package bündelt und versiegelt das Artefakt. Deploy überträgt das Paket, wechselt den Symlink und führt Magento-spezifische Schritte aus. Verify prüft den Shop nach dem Deployment. Rollback stellt den vorigen Zustand wieder her, wenn ein Problem erkannt wird.

Die wichtigste Praxisregel: Nichts, was auf Production läuft, darf in der Pipeline implizit bleiben. Jede Entscheidung – welche Artefakte erzeugt werden, wann ein Deployment freigegeben wird, welche Checks nach dem Deploy laufen – muss in der YAML-Datei explizit stehen. Nur dann ist die Pipeline ein zuverlässiger Deployment-Standard und kein fragiles Konstrukt, das nur unter optimalen Bedingungen funktioniert.

Komplette .gitlab-ci.yml für Magento — Das Wichtigste auf einen Blick

Stage-Reihenfolge

build → test → package → deploy → verify → rollback. Jede Stage muss abgeschlossen sein, bevor die nächste beginnt. Keine Shortcuts in Production.

Artefakt-Prinzip

Der Build-Job erzeugt einmal ein vollständiges Artefakt. Alle nachfolgenden Jobs verwenden dieses Artefakt – kein zweiter Build auf dem Server.

Freigabe & Rollback

Production-Deploy immer als when: manual. Rollback-Job muss geübt und in Sekunden ausführbar sein – nicht improvisiert im Störfall.

Verify als Pflicht

Ohne Verify-Job ist die Pipeline technisch, aber nicht fachlich abgeschlossen. HTTP-Check, Cache-Check und Log-Check sind Minimalanforderungen.

11. FAQ: Komplette .gitlab-ci.yml für Magento

1Wie viele Stages braucht eine Magento-Pipeline mindestens?
Mindestens vier: build, deploy, verify und rollback. Für produktive Shops ist die Erweiterung um test und package der empfohlene Standard – sechs Stages insgesamt.
2Warum kein Build auf dem Produktionsserver?
Ein Fehler während des Builds hinterlässt den Shop in einem inkonsistenten Zustand. Vorgebaute Artefakte werden atomar aktiviert – kein Risiko durch halbfertige Builds auf Production.
3Was bewirkt when: manual?
Der Job wartet auf explizite Bestätigung eines autorisierten Benutzers. Kein automatischer Deploy auf Production – das ist die wichtigste Sicherheitsschranke in der Pipeline.
4Wie lange Artefakte aufbewahren?
Build-Artefakte: 1 day. Gepackte Release-Artefakte: 7 days für manuelle Rollbacks auf ältere Versionen. Mehr belastet den GitLab-Speicher unnötig.
5Kann der Rollback-Job Datenbank-Migrationen rückgängig machen?
Nicht automatisch. Datei-Rollback: Symlink zurücksetzen. DB-Migration rückgängig: erfordert explizites Down-Skript. Expand-Contract-Muster bei Migrationen einhalten.
6Was prüft ein minimaler Verify-Job?
HTTP-200-Check der Startseite, Check eines Health-Endpoints und Cache-Status via bin/magento cache:status. Minimum – nicht Maximum.
7setup:di:compile im Build-Job oder auf dem Server?
Im Build-Job. Generierter Code wird als Artefakt übertragen. Kein Compile auf Production – weniger Deployment-Zeit, keine Compile-Fehler auf dem Live-Server.
8Wo Secrets sicher speichern?
Als GitLab CI/CD Variables mit Protected und Masked Flags. Niemals in der .gitlab-ci.yml selbst – Secrets in YAML sind in der gesamten CI-History einsehbar.
9Automatisch oder manuell für Staging?
Staging kann automatisch deployen – schnelles Feedback für das Team. Production immer manuell – eine bewusste Freigabe verhindert Versehen.
10Wie viele alte Releases auf dem Server behalten?
Mindestens 3, besser 5. Das ermöglicht mehrere Rollback-Schritte. Mehr als 10 ist selten sinnvoll und belastet Speicher und Inode-Tabelle des Servers.