CI/CD
.yml
GitLab · Magento · Zero Downtime · Runner · Rollback
Magento Zero-Downtime mit GitLab:
vom ersten Runner bis zum sicheren Rollback

Diese Anleitung deckt den vollständigen Weg ab: GitLab Runner einrichten, Pipeline Schritt für Schritt aufbauen, Release-Struktur auf dem Server anlegen, atomaren Symlink-Wechsel implementieren, Verify-Jobs konfigurieren und Rollback in Sekunden zuverlässig durchführen. Zero Downtime ist das Ergebnis dieser konkreten Entscheidungen.

25 Min. Lesezeit Runner · Pipeline · Release · Symlink · Verify · Rollback Magento 2.4 · GitLab 17+ · PHP 8.4 · Docker

1. Was Zero Downtime für Magento wirklich bedeutet

Der Begriff Zero Downtime wird in Deployment-Diskussionen oft als absolutes Ziel formuliert — ein Deployment, bei dem kein einziger Request fehlschlägt. In der Praxis bedeutet Zero Downtime für Magento etwas Nüancierteres: Die Zeit, in der der Shop für Nutzer nicht erreichbar ist, wird auf Null oder annäherndes Null reduziert. Das ist mit dem richtigen Prozess erreichbar, erfordert aber explizite Designentscheidungen an jedem Schritt des Deployments.

Was Zero Downtime konkret ermöglicht: Dateistand auf einen neuen Release wechseln, ohne dass der Webserver neu gestartet werden muss. Atomarer Symlink-Wechsel dauert Millisekunden. Requests, die während des Wechsels ankommen, werden entweder noch vom alten oder schon vom neuen Release bedient — keine Requests scheitern am Wechsel selbst. Was Zero Downtime einschränkt: Datenbankmigrationen mit exklusiven Tabellensperren, nicht rückwärtskompatible Schemavariationen und Magento-Caches, die nach dem Wechsel explizit geleert werden müssen. Für diese Fälle gibt es Strategien (Expand/Contract, kurze Maintenance-Fenster nur für die Datenbank), aber keine Universallösung.

Diese Anleitung baut den vollständigen Prozess auf, der Zero Downtime für den überwiegenden Teil von Magento-Deployments ermöglicht. Sie beginnt beim GitLab Runner und endet beim geübten Rollback — weil beides zusammen einen belastbaren Prozess ergibt, der auch unter Zeitdruck funktioniert.

2. Die Gesamtarchitektur auf einen Blick

Der Deployment-Prozess besteht aus fünf Schichten, die aufeinander aufbauen. Erste Schicht: das GitLab-Repository mit Protected Branches, Protected Tags und vollständig konfigurierten CI/CD-Variablen. Zweite Schicht: der GitLab Runner, der Pipelines ausführt, Build-Artefakte erzeugt und Deploy-Jobs startet. Dritte Schicht: die CI-Pipeline mit Build-, Test-, Package-, Deploy-, Verify- und Rollback-Stage. Vierte Schicht: die serverseitige Release-Struktur mit releases/, shared/ und current-Symlink. Fünfte Schicht: die Magento-spezifischen Schritte (Shared-Symlinks, setup:upgrade, cache:flush), die in der richtigen Reihenfolge ablaufen.

Jede dieser Schichten kann unabhängig verbessert und ausgetauscht werden, ohne die anderen zu brechen. Wer heute mit Shell-Skripten deployt, kann in Schicht drei zu Deployer wechseln, ohne Schicht eins, zwei oder vier zu verändern. Das macht den Prozess langfristig wartbar und für das Team erweiterbar.

3. GitLab Runner aufsetzen

Bevor eine Pipeline ausgeführt werden kann, muss ein Runner bereitstehen. Für Magento empfiehlt sich ein eigener Runner mit Docker-Executor auf einem dedizierten Linux-Host. Die Installation über das offizielle GitLab-Paketrepository und die anschließende Registrierung mit Docker als Executor geben die Kontrolle über Build-Umgebung und Sicherheit, die Shared Runner nicht bieten. Wichtig: Build-Runner und Deploy-Runner sollten separate Instanzen sein — der Deploy-Runner hat SSH-Zugang zum Produktionsserver und muss entsprechend strenger abgesichert sein.

Tags steuern, welcher Job auf welchem Runner landet. Ein Job mit tags: [magento-build] läuft nur auf Build-Runnern, ein Job mit tags: [magento-deploy] nur auf Deploy-Runnern. run-untagged = false auf dem Deploy-Runner verhindert, dass unerwartete Jobs auf einem System landen, das SSH-Zugang zu Produktionsservern hat.

# Complete .gitlab-ci.yml for Magento Zero-Downtime Deployment
# Covers all stages: build, test, package, deploy, verify, rollback

stages:
  - build
  - test
  - package
  - deploy
  - verify
  - rollback

variables:
  GIT_STRATEGY: fetch
  COMPOSER_CACHE_DIR: ".cache/composer"
  NPM_CONFIG_CACHE: ".cache/npm"
  RELEASE_RETENTION: "5"

# Reusable SSH setup anchor
.ssh-setup: &ssh-setup
  - apt-get update -qq && apt-get install -y -qq openssh-client rsync
  - eval $(ssh-agent -s)
  - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
  - mkdir -p ~/.ssh
  - echo "$SSH_KNOWN_HOSTS" > ~/.ssh/known_hosts
  - chmod 600 ~/.ssh/known_hosts

build:magento:
  stage: build
  image: php:8.4-cli
  tags: [magento-build]
  cache:
    key: "composer-${CI_COMMIT_REF_SLUG}"
    paths: [".cache/composer/"]
  script:
    - apt-get update -qq && apt-get install -y -qq git unzip nodejs npm
    - composer install --no-dev --prefer-dist --no-interaction --quiet
    - npm ci --prefix app/design/frontend/Mironsoft/default/web/tailwind --silent
    - npm run build --prefix app/design/frontend/Mironsoft/default/web/tailwind --silent
    - php bin/magento setup:di:compile
  artifacts:
    paths: [vendor/, generated/, pub/static/]
    expire_in: 4 hours

4. Pipeline-Struktur und Stages

Die Pipeline-Stages folgen einer klaren Logik: Jede Stage hat eine einzige Verantwortung und übergibt das Ergebnis als Artefakt oder Zustand an die nächste Stage. build erzeugt das deploybare Artefakt. test führt Quality-Gates aus (PHPStan, PHPUnit, PHPCs). package schnürt das finale Deployment-Paket, falls es vom Build-Artefakt abweicht. deploy überträgt das Paket und wechselt den Symlink. verify bestätigt, dass der neue Stand funktioniert. rollback stellt den vorherigen Stand wieder her, wenn Verify fehlschlägt.

Die Reihenfolge ist nicht verhandelbar: Deploy darf nie vor erfolgreichem Build und Test stattfinden. Verify muss unmittelbar nach Deploy laufen. Rollback muss als Pipeline-Job existieren, nicht als manueller Notfallprozess. Wer diese Struktur einhält, hat einen Prozess, der vorhersagbar und im Team nachvollziehbar ist.

5. Build-Stage: reproduzierbares Artefakt erzeugen

Das Artefakt ist der Kern des Zero-Downtime-Ansatzes: Es wird einmal gebaut, auf Staging getestet und auf Production deployt — ohne Unterschied. Kein "auf Staging hat es funktioniert, auf Production gibt es das Problem mit dem Build-Environment". Das Artefakt enthält vendor/ (ohne Dev-Dependencies), generated/ (DI-Kompilierung), kompilierte Tailwind-CSS-Assets und alle anderen statischen Dateien, die für den Betrieb benötigt werden.

Was nicht ins Artefakt gehört: app/etc/env.php (liegt in shared/), pub/media/ (ist shared content), var/ (Laufzeitdaten), .git/ und alle Test- und Dev-Tools. Ein übersichtliches Artefakt ist schneller zu übertragen und macht die Grenze zwischen Build-Output und Server-Zustand explizit. Die Artefakt-Größe sollte regelmäßig überwacht werden — plötzliche Größensprünge weisen auf versehentlich inkludierte Dateien hin.

6. Server-Vorbereitung: Release-Struktur anlegen

Die Release-Struktur auf dem Zielserver ist Voraussetzung für alle Deployments und muss einmalig vorbereitet werden. Das Verzeichnis-Layout ist einfach und konsequent: /var/www/magento/releases/ für einzelne Release-Stände, /var/www/magento/shared/ für persistente Daten und /var/www/magento/current als Symlink, der auf den aktiven Release zeigt. Der Webserver (Nginx oder Apache) zeigt mit seinem Document-Root auf /var/www/magento/current/pub.

Im shared/-Verzeichnis existieren alle persistenten Dateien und Verzeichnisse bereits, bevor das erste Deployment stattfindet: app/etc/env.php mit der produktiven Konfiguration, pub/media/ mit dem aktuellen Medienstand und var/log/, var/session/ für Laufzeitdaten. Diese Inhalte werden nie überschrieben, sondern per Symlink in jeden Release eingehängt. Der erste Deployment-Job muss diese Struktur vorfinden — sonst schlägt er fehl.

# deploy:production — atomically switches the current symlink
deploy:production:
  stage: deploy
  image: debian:bookworm-slim
  tags: [magento-deploy]
  when: manual
  only:
    - tags
  environment:
    name: production
    url: https://shop.example.com
  before_script:
    - *ssh-setup
  script:
    - RELEASE=$(date +%Y%m%d-%H%M%S)
    - echo "Deploying release $RELEASE"
    # Transfer build artifact to new release directory
    - ssh "$DEPLOY_USER@$DEPLOY_HOST" "mkdir -p $DEPLOY_PATH/releases/$RELEASE"
    - rsync -az --delete
        --exclude='.git'
        --exclude='var/cache'
        ./ "$DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_PATH/releases/$RELEASE/"
    # Execute Magento release steps on server
    - |
      ssh "$DEPLOY_USER@$DEPLOY_HOST" bash << 'ENDSSH'
        set -euo pipefail
        DEPLOY_PATH="${DEPLOY_PATH}"
        RELEASE="${RELEASE}"
        RELEASE_PATH="$DEPLOY_PATH/releases/$RELEASE"

        # Link shared files and directories
        ln -sfn "$DEPLOY_PATH/shared/app/etc/env.php"  "$RELEASE_PATH/app/etc/env.php"
        ln -sfn "$DEPLOY_PATH/shared/pub/media"         "$RELEASE_PATH/pub/media"
        ln -sfn "$DEPLOY_PATH/shared/var/log"           "$RELEASE_PATH/var/log"
        ln -sfn "$DEPLOY_PATH/shared/var/session"       "$RELEASE_PATH/var/session"

        # Run setup:upgrade only if there are pending migrations
        cd "$RELEASE_PATH"
        php bin/magento setup:upgrade --keep-generated

        # Switch symlink atomically — zero downtime moment
        ln -sfn "$RELEASE_PATH" "$DEPLOY_PATH/current"

        # Flush cache after switch
        php bin/magento cache:flush

        # Cleanup old releases — keep RELEASE_RETENTION most recent
        ls -1dt "$DEPLOY_PATH/releases/"* \
          | tail -n +$((RELEASE_RETENTION + 1)) \
          | xargs rm -rf
        echo "[OK] Deployed and switched to $RELEASE"
      ENDSSH

7. Deploy-Stage: Symlink-Wechsel und Magento-Schritte

Der Deploy-Job überträgt das Artefakt auf den Server, führt die Magento-spezifischen Schritte aus und wechselt den current-Symlink. Die Reihenfolge ist exakt: Shared-Symlinks setzen, bevor irgendein Magento-Kommando ausgeführt wird, weil setup:upgrade auf app/etc/env.php angewiesen ist. Setup-Upgrade mit --keep-generated ausführen, damit die im Build erzeugte generated/-Konfiguration erhalten bleibt. Dann erst den Symlink wechseln und Cache flushen.

Die Release-Retention-Bereinigung am Ende des Deploy-Jobs entfernt alte Releases und hält den Speicherplatz unter Kontrolle. Der Wert sollte mindestens fünf betragen, damit mehrere Rollback-Schritte möglich sind. Der Deploy-Job ist auf when: manual gesetzt, damit kein automatisches Deployment auf Production stattfindet. Das Team entscheidet explizit, wann ein neuer Stand auf Production geht.

8. Verify-Stage: Deployment bestätigen

Der Verify-Job ist der letzte Qualitäts-Gate nach dem Deployment. Er prüft automatisch, ob der neue Release-Stand erreichbar und funktionsfähig ist. Mindestanforderungen: HTTP-200-Response auf dem Health-Endpoint, HTTP-200 auf der Startseite und Magento-Cache-Status ohne kritische Fehler. Diese drei Checks decken die häufigsten Deployment-Fehler ab: nicht gestarteter Webserver, fehlende Konfiguration und beschädigter Cache-Stand.

Der Verify-Job läuft mit when: on_success direkt nach dem Deploy-Job und ist keine optionale Ergänzung. Wenn Verify fehlschlägt, signalisiert das der Pipeline, dass etwas schiefgelaufen ist, und gibt dem Team die Möglichkeit, sofort den Rollback-Job auszulösen. Das Zeitfenster zwischen Deployment-Fehler und Wiederherstellung des vorherigen Standes beträgt damit Sekunden bis wenige Minuten — nicht Stunden.

# verify:production — confirms new release is healthy
verify:production:
  stage: verify
  image: alpine:3.19
  tags: [magento-build]
  when: on_success
  needs: ["deploy:production"]
  before_script:
    - apk add --no-cache curl openssh-client
    - eval $(ssh-agent -s)
    - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
    - mkdir -p ~/.ssh && echo "$SSH_KNOWN_HOSTS" > ~/.ssh/known_hosts
  script:
    # Health endpoint must return HTTP 200
    - |
      for i in 1 2 3; do
        STATUS=$(curl --silent --output /dev/null --write-out "%{http_code}" \
          --max-time 10 "https://shop.example.com/health")
        if [ "$STATUS" = "200" ]; then
          echo "[OK] Health check passed (attempt $i)"
          break
        fi
        echo "[WARN] Health check returned $STATUS (attempt $i/3)"
        sleep 5
      done
      [ "$STATUS" = "200" ] || { echo "[FAIL] Health check failed"; exit 1; }
    # Homepage must respond
    - curl --fail --silent --output /dev/null --max-time 20
        "https://shop.example.com/"
    # Magento cache and module status via SSH
    - ssh "$DEPLOY_USER@$DEPLOY_HOST"
        "cd $DEPLOY_PATH/current && php bin/magento cache:status"
  allow_failure: false

9. Rollback-Stage: sicher zurückrollen

Der Rollback-Job ist der wichtigste Job, der am seltensten ausgeführt wird — und der deshalb am gründlichsten getestet werden muss, bevor er gebraucht wird. Er schaltet den current-Symlink auf den vorherigen Release-Stand zurück. Der Webserver antwortet danach wieder mit dem vorherigen Magento-Stand. Cache flushen stellt sicher, dass keine alten Cache-Einträge des neuen (fehlerhaften) Releases sichtbar bleiben.

Rollback hat Grenzen: Datenbankmigrationen, die mit dem neuen Release ausgeführt wurden, werden nicht rückgängig gemacht. Wenn die Migrationen des neuen Releases nicht rückwärtskompatibel sind, muss die Datenbank separat zurückgesetzt werden — ein Prozess, der von der normalen Rollback-Routine getrennt dokumentiert und geübt werden muss. Für Teams, die Expand/Contract-Migrationen verwenden, ist dieser Sonderfall selten. Für alle anderen ist er das Thema, das die Rollback-Planung dominieren sollte.

Nach jedem Rollback muss der Verify-Job erneut ausgeführt werden, um zu bestätigen, dass der vorherige Stand tatsächlich wieder funktioniert. Ein Rollback auf einen Stand, der selbst ein Problem hatte, ist kein Fortschritt. Das Team sollte bei jedem Rollback sofort die Ursache im fehlgeschlagenen Release untersuchen und die Staging-Pipeline benutzen, bevor ein neues Deployment auf Production versucht wird.

Mironsoft

Magento Zero-Downtime, GitLab CI/CD und Deployment-Infrastruktur

Zero-Downtime-Deployment für Magento mit GitLab aufbauen?

Wir bauen den vollständigen Zero-Downtime-Prozess auf: GitLab Runner einrichten, Pipeline strukturieren, Release-Struktur implementieren, Verify-Jobs konfigurieren und Rollback-Pfad dokumentieren und üben.

Runner und Pipeline

Self-hosted Runner einrichten, Pipeline-Stages strukturieren und Artefakte definieren

Release-Architektur

Server-Struktur, Shared-Pfade, Symlink-Wechsel und Release-Rotation implementieren

Verify und Rollback

Automatische Verify-Jobs konfigurieren und Rollback-Prozess dokumentieren und üben

10. Zusammenfassung

Magento Zero-Downtime-Deployments mit GitLab sind das Ergebnis konsequent umgesetzter Designentscheidungen auf jeder Ebene des Prozesses. Ein eigener GitLab Runner mit Docker-Executor gibt Kontrolle über Build-Umgebung und Sicherheit. Eine klar strukturierte Pipeline mit Build-, Test-, Deploy-, Verify- und Rollback-Stage macht den Prozess nachvollziehbar und teamfähig. Ein reproduzierbares Artefakt aus der Build-Stage stellt sicher, dass Staging und Production identische Stände deployen. Die Release-Struktur mit releases/, shared/ und current-Symlink macht den Symlink-Wechsel atomar und Rollback trivial.

Der Verify-Job nach dem Deployment ist kein optionaler Luxus, sondern der automatische Beweis, dass der neue Stand funktioniert. Der Rollback-Job ist kein Notfallwerkzeug, das improvisiert wird, wenn es brennt — er ist ein regelmäßig geübter Prozess, der auf Staging bewiesen ist, bevor er auf Production gebraucht wird. Dieser Prozess ist aufzubauen und zu pflegen. Aber er ist es, der den Unterschied zwischen einem Deployment-Prozess macht, der das Team in Angst versetzt, und einem, der dem Team Sicherheit gibt.

Magento Zero-Downtime mit GitLab — Das Wichtigste auf einen Blick

Runner und Artefakt

Eigener Runner mit Docker-Executor. Reproduzierbares Artefakt aus Build-Stage. Nie auf Production bauen — immer deployen.

Release-Struktur

releases/, shared/ und current-Symlink. Shared-Pfade für env.php, pub/media, var/log. Atomarer Symlink-Wechsel = kein Downtime-Moment.

Pipeline-Stages

build → test → deploy → verify → rollback. Jede Stage hat eine Verantwortung. Deploy nur auf manuelle Freigabe für Production.

Rollback ist Pflicht

Als Pipeline-Job, nicht als manueller Notfall. Auf Staging üben. Nach jedem Rollback Verify ausführen und Ursache analysieren.

11. FAQ: Magento Zero-Downtime mit GitLab

1Kann Magento wirklich ohne Unterbrechung deployt werden?
Für den überwiegenden Teil ja. Der Symlink-Wechsel ist atomar. Datenbankmigrationen mit Sperren können Unterbrechungen verursachen — dafür gibt es Expand/Contract-Strategien.
2Wie lange dauert ein typisches Deployment?
Build: 2–5 Min. Deploy: 1–2 Min. Verify: 30 Sek. Gesamtprozess: 5–10 Minuten von Trigger bis grünem Verify-Job.
3Was passiert, wenn setup:upgrade fehlschlägt?
Deploy-Job schlägt fehl, bevor der Symlink gewechselt wird. Bisheriger Stand bleibt aktiv. Keine sichtbaren Auswirkungen auf Production.
4Wie viele Releases auf dem Server behalten?
Mindestens fünf. Erlaubt mehrere Rollback-Schritte. Minimum drei, wenn Speicher begrenzt, schränkt aber Rollback-Spielraum ein.
5Staging vs. Production in diesem Prozess?
Gleiche Artefakte, gleiche Pipeline-Logik. Nur CI/CD-Variablen unterscheiden sich. Staging automatisch, Production manuell freigegeben.
6Wie behandle ich pub/media beim Deployment?
pub/media liegt in shared/, wird nie deployt. In jeden Release wird ein Symlink auf shared/pub/media gesetzt. Medien überdauern alle Releases.
7Mehrere Server (Web + Cron) deployen?
Ja, Deploy-Job per SSH parallel auf mehrere Server. Deployer unterstützt parallele Hosts nativ für koordinierten Wechsel.
8Was tun, wenn der Rollback-Job selbst fehlschlägt?
Manuell per SSH: ln -sfn /var/www/magento/releases/VORHERIGER_RELEASE /var/www/magento/current, dann cache:flush. Diese Prozedur muss dokumentiert und bekannt sein.
9Datenbank-Backups in den Deployment-Prozess integrieren?
Als erster Schritt des Deploy-Jobs oder als separater Job davor. Backup-Timestamp im Release-Verzeichnis oder als Pipeline-Variable hinterlegen.
10Muss ein Hotfix den gesamten Build-Prozess durchlaufen?
Ja. Reproduzierbarkeit und Verifikation gelten auch für Hotfixes. Build dauert wenige Minuten und ist kein echtes Hindernis. Direktes Server-Patching überschreibt das nächste Deployment.