CI/CD
.yml
GitLab · CI/CD · Magento · Rollback-Strategien
Rollback-Strategien für Magento
Dateien, Datenbank, Queue & Assets

Ein Magento-Rollback ist mehr als ein Symlink-Wechsel. Datenbank, Queue-Nachrichten, Static Content und Elasticsearch-Indizes folgen eigenen Regeln. Wer nur Dateien zurückrollt, riskiert Inkonsistenzen, die schwieriger zu beheben sind als das ursprüngliche Problem.

15 Min. Lesezeit Datei-Rollback · DB-Rollback · Queue · Static Content · Elasticsearch GitLab CI/CD · Magento 2 · Zero Downtime

1. Die vier Schichten eines Magento-Rollbacks

Ein Magento-System besteht aus vier unabhängigen Schichten, die beim Rollback unterschiedlich behandelt werden müssen. Die erste Schicht sind die Anwendungsdateien: PHP-Code, Hyvä-Templates, Composer-Abhängigkeiten, kompilierter DI-Code und Static Content. Diese Schicht ist per Symlink in Sekunden zurückrollbar. Die zweite Schicht ist die Datenbank: Magento-Konfiguration, Produktdaten, Bestellungen und alle Tabellen, die durch setup:upgrade verändert wurden. Diese Schicht ist nicht automatisch rollbackfähig.

Die dritte Schicht ist der Queue-Zustand: Nachrichten in RabbitMQ oder der MySQL-basierten Magento-Queue, die von Consumern des neuen Release eingestellt wurden und möglicherweise mit dem alten Code nicht verarbeitet werden können. Die vierte Schicht sind externe Dienste: Elasticsearch-Indizes, Redis-Cache-Inhalte und Varnish-Cache-Objekte. Diese Schicht ist oft die am wenigsten beachtete, kann aber nach einem Rollback zu schwer diagnostizierbaren Fehlern führen.

Die wichtigste Erkenntnis: Ein Rollback, der nur die Dateischicht berücksichtigt, ist für Releases ohne Datenbankmigrationen oder Queue-Änderungen ausreichend. Für Releases mit Datenbankmigrationen ist ein reiner Datei-Rollback gefährlich — der alte Code findet möglicherweise Tabellen oder Spalten, die er nicht erwartet, oder vermisst Spalten, die er braucht. Die Rollback-Strategie muss deshalb vor jedem Release explizit geplant werden.

2. Datei-Rollback: Symlink, Generated Code und Static Content

Der Datei-Rollback via Symlink-Wechsel ist der schnellste und sicherste Teil des Rollback-Prozesses. Das Release-Verzeichnis des vorherigen Stands ist vollständig vorhanden — mit Composer-Vendor, kompiliertem DI-Code und Static Content. Der Symlink-Wechsel ist atomar und dauert unter einer Sekunde. Cache-Flush danach ist Pflicht, weil Redis möglicherweise Objekte aus dem fehlerhaften Release gecacht hat.

Der generated/-Ordner enthält den von Magento kompilierten Dependency-Injection-Code. Dieser Code ist release-spezifisch und liegt im Release-Verzeichnis, nicht im shared-Bereich. Nach dem Rollback verwendet Magento automatisch den generierten Code des vorherigen Release — kein manueller Schritt notwendig. Gleiches gilt für pub/static/: Der Static Content des vorherigen Release liegt im vorherigen Release-Verzeichnis und wird nach dem Symlink-Wechsel wieder serviert.

#!/usr/bin/env bash
# scripts/rollback-files.sh — File-only rollback via symlink switch
# Safe for releases WITHOUT database migrations
set -euo pipefail

readonly APP_PATH="${DEPLOY_PATH:?}"
readonly ROLLBACK_TARGET="${1:-}"

ssh "${DEPLOY_USER}@${DEPLOY_HOST}" bash -s <<SSH
set -euo pipefail

# Auto-detect previous release if no target specified
if [[ -z "${ROLLBACK_TARGET}" ]]; then
  current="\$(basename \$(readlink -f ${APP_PATH}/current))"
  TARGET="\$(ls -1d ${APP_PATH}/releases/*/ \
    | sort -Vr | grep -v "\${current}/" | head -1 | xargs basename)"
else
  TARGET="${ROLLBACK_TARGET}"
fi

echo "[ROLLBACK] Target release: \${TARGET}"
test -d "${APP_PATH}/releases/\${TARGET}" \
  || { echo "[FAIL] Release \${TARGET} not found"; exit 1; }

# Brief maintenance window for cache consistency
cd "${APP_PATH}/current"
bin/magento maintenance:enable --ip="${ALLOWED_IP:-127.0.0.1}" 2>/dev/null || true

# Atomic symlink switch — zero-downtime file rollback
ln -sfn "${APP_PATH}/releases/\${TARGET}" "${APP_PATH}/current"
echo "[OK] Switched to \${TARGET}"

# Full cache flush — mandatory after any rollback
cd "${APP_PATH}/current"
bin/magento cache:flush
echo "[OK] Cache flushed"

# Restart queue consumers to pick up new application path
supervisorctl restart magento-consumer:* 2>/dev/null \
  || systemctl restart magento-queue-consumer 2>/dev/null \
  || echo "[WARN] Could not restart queue consumers — manual restart required"

bin/magento maintenance:disable 2>/dev/null || true
echo "[OK] File rollback complete — now running \${TARGET}"
SSH

3. Datenbank-Rollback: Backup-Strategie und Expand-Contract

Der Datenbank-Rollback ist der schwierigste Teil eines Magento-Rollbacks. Magento-Datenbankmigrationen sind in der Standardimplementierung nicht reversibel — es gibt kein setup:downgrade. Wer nach einem Release zurückrollen will, das Datenbankmigrationen enthielt, hat drei Optionen: Ein vollständiges DB-Backup wiederherstellen (zeitaufwendig, Datenverlust seit dem Backup), ein Expand-Contract-Muster anwenden, oder die Migration manuell rückgängig machen.

Das Expand-Contract-Muster ist die eleganteste Lösung: Vor dem eigentlichen Release wird eine kompatible Datenbankänderung deployt, die sowohl der alte als auch der neue Code verarbeiten kann. Erst wenn der neue Code stabil läuft und kein Rollback mehr nötig ist, wird der "Contract" deployt, der die Übergangskonstrukte entfernt. Dieses Muster erfordert Disziplin bei der Entwicklung, macht aber Datenbankmigrationen vollständig rollbackfähig ohne Backup-Wiederherstellung.

# scripts/db-backup.sh — Create a timestamped database backup before deployment
# Run this BEFORE any release that includes database migrations
#!/usr/bin/env bash
set -euo pipefail

readonly BACKUP_DIR="${DEPLOY_PATH}/shared/backups/db"
readonly TIMESTAMP="$(date +%Y%m%d-%H%M%S)"
readonly BACKUP_FILE="${BACKUP_DIR}/${CI_COMMIT_TAG:-manual}-${TIMESTAMP}.sql.gz"

ssh "${DEPLOY_USER}@${DEPLOY_HOST}" bash -s <<SSH
set -euo pipefail
mkdir -p "${BACKUP_DIR}"

echo "[BACKUP] Creating database backup: ${BACKUP_FILE}"
mysqldump \
  --single-transaction \
  --quick \
  --lock-tables=false \
  --routines \
  --triggers \
  "${MAGENTO_DB_NAME}" \
  | gzip -6 > "${BACKUP_FILE}"

backup_size="\$(du -sh "${BACKUP_FILE}" | cut -f1)"
echo "[OK] Backup created: ${BACKUP_FILE} (\${backup_size})"

# Keep only last 10 DB backups
ls -1t "${BACKUP_DIR}"/*.sql.gz 2>/dev/null | tail -n +11 | xargs rm -f 2>/dev/null || true
echo "[OK] Old backups cleaned up"
SSH

4. Queue-Zustand beim Rollback: Was mit RabbitMQ-Nachrichten passiert

Die Queue ist die am häufigsten vergessene Rollback-Schicht. Wenn ein neues Release für kurze Zeit aktiv war und dabei Nachrichten in RabbitMQ oder die Magento-MySQL-Queue eingestellt hat, müssen diese Nachrichten nach dem Rollback verarbeitet werden — vom alten Code. Das ist möglich, wenn das Nachrichtenformat kompatibel ist. Es ist problematisch, wenn das neue Release neue Nachrichtentypen oder veränderte Payload-Strukturen eingeführt hat.

Der praktische Umgang: Vor dem Rollback die Queue-Consumer stoppen, den Symlink wechseln, den Cache flushen und die Consumer mit dem alten Code neu starten. Nachrichten, die mit dem alten Code nicht verarbeitet werden können, landen in der Dead-Letter-Queue oder der Fehlertabelle. Diese müssen nach dem Rollback manuell bewertet werden. In Produktionssystemen mit hohem Queue-Durchsatz kann die Entscheidung auch lauten, die betroffene Queue kurzfristig zu leeren — mit dem Risiko, dass wenige Nachrichten verloren gehen — statt inkonsistente Nachrichten verarbeiten zu lassen.

5. Static Assets und Elasticsearch-Index nach dem Rollback

Static Content liegt im Release-Verzeichnis und wechselt automatisch mit dem Symlink. Das bedeutet: Nach dem Rollback serviert Nginx sofort den Static Content des vorherigen Release — ohne manuellen Eingriff, ohne Cache-Warmup. Browser-seitig gecachte Assets des fehlerhaften Release sind ein kurzzeitiges Problem, das sich durch Cache-Busting-Mechanismen (Asset-Hashes in Dateinamen) von selbst löst.

Der Elasticsearch-Index hingegen ist nicht release-spezifisch. Er wird nicht zurückgerollt, wenn der Symlink wechselt. Wenn das neue Release Änderungen am Index-Mapping oder an den Indexierungs-Routinen hatte, kann der Index nach dem Rollback inkompatibel mit dem alten Code sein. In diesem Fall muss eine vollständige Reindexierung durchgeführt werden: bin/magento indexer:reindex catalogsearch_fulltext. Bei großen Katalogen dauert das mehrere Minuten bis Stunden — ein wichtiger Faktor bei der Rollback-Planung.

6. Entscheidungsbaum: Welche Rollback-Strategie passt?

Die richtige Rollback-Strategie hängt davon ab, was das Release verändert hat. Vor jedem Release sollte diese Entscheidung bewusst getroffen und dokumentiert werden — nicht erst wenn der Rollback nötig ist. Die folgende Logik hilft bei der Einordnung:

Wenn das Release keine Datenbankmigrationen und keine Queue-Änderungen enthält, ist der reine Datei-Rollback via Symlink ausreichend. Wenn das Release kompatible Datenbankmigrationen (additiv, ohne Spalten zu entfernen) enthält, ist der Datei-Rollback mit anschließendem DB-Backup als Sicherheitsnetz ausreichend. Wenn das Release inkompatible Datenbankmigrationen (Spalten entfernen, Typen ändern, Breaking Changes) enthält, muss vor dem Deployment ein vollständiges DB-Backup gemacht werden, und der Rollback umfasst Datei-Rollback plus DB-Restore.

# .gitlab-ci.yml — Database backup job before migration-heavy releases
db:backup:before-migrate:
  stage: deploy
  script:
    - ./scripts/db-backup.sh
  rules:
    # Only run when the release is tagged as containing DB migrations
    # Set CONTAINS_DB_MIGRATIONS=true in GitLab pipeline variables when needed
    - if: $CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/ && $CONTAINS_DB_MIGRATIONS == "true"
      when: always
  # This job must succeed before the deploy job runs
  allow_failure: false

rollback:with-db-restore:
  stage: rollback
  script:
    - |
      # Full rollback: files + database restore
      BACKUP_FILE="${DEPLOY_PATH}/shared/backups/db/${RESTORE_BACKUP_FILE}"
      ssh "${DEPLOY_USER}@${DEPLOY_HOST}" bash -s <<'SSH'
      set -euo pipefail
      # Step 1: stop consumers
      supervisorctl stop magento-consumer:* 2>/dev/null || true
      # Step 2: file rollback
      ln -sfn "${DEPLOY_PATH}/releases/${ROLLBACK_TARGET}" "${DEPLOY_PATH}/current"
      # Step 3: database restore
      zcat "${BACKUP_FILE}" | mysql "${MAGENTO_DB_NAME}"
      echo "[OK] Database restored from ${BACKUP_FILE}"
      # Step 4: cache and search reindex
      cd "${DEPLOY_PATH}/current"
      bin/magento cache:flush
      bin/magento indexer:reindex
      # Step 5: restart consumers
      supervisorctl start magento-consumer:* 2>/dev/null || true
      SSH
  rules:
    - if: $CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/
      when: manual
      allow_failure: true

7. Rollback-Strategien in der GitLab-Pipeline abbilden

Die GitLab-Pipeline bildet die verschiedenen Rollback-Strategien als separate manuelle Jobs ab. Es gibt mindestens zwei Jobs: einen für den einfachen Datei-Rollback und einen für den vollständigen Rollback mit DB-Restore. Welcher Job ausgelöst wird, entscheidet das Deployment-Team im Störfall — basierend auf der vorab dokumentierten Information, ob das Release Datenbankmigrationen enthielt.

Diese Information muss in der Deployment-Dokumentation oder als Pipeline-Variable pro Release festgehalten sein. Eine praktische Konvention: Releases mit Datenbankmigrationen werden mit einem -migrate-Suffix oder einer Pipeline-Variable CONTAINS_DB_MIGRATIONS=true gekennzeichnet. Der Rollback-Job für DB-Releases ist dann nur sichtbar, wenn diese Variable gesetzt ist. Das verhindert versehentliche DB-Rollbacks für Releases, die nur Code-Änderungen enthalten.

8. Rollback-Szenarien im Vergleich

Unterschiedliche Release-Typen erfordern unterschiedliche Rollback-Strategien. Der Überblick zeigt, welche Strategie für welches Szenario geeignet ist und welche Nebeneffekte berücksichtigt werden müssen.

Release-Typ Rollback-Strategie Dauer Datenverlustrisiko
Nur Code/Templates Symlink + Cache-Flush < 2 Minuten Keines
Additive DB-Migration Symlink + Cache + Backup als Netz < 3 Minuten Gering (neue Daten in neuer Spalte)
Breaking DB-Migration Symlink + DB-Restore 30–90 Minuten Alle Daten seit Backup verloren
Queue-Format-Änderung Symlink + Queue leeren + Consumer restart < 5 Minuten Offene Queue-Nachrichten verloren
ES-Index-Änderung Symlink + Reindexierung 10 Min. – mehrere Stunden Keines (Index wird neu aufgebaut)

9. Zusammenfassung

Ein vollständiger Rollback für Magento berücksichtigt vier Schichten: Dateien, Datenbank, Queue und externe Dienste. Der Datei-Rollback via Symlink ist der einfachste und schnellste Teil und für Releases ohne Datenbankmigrationen vollständig ausreichend. Für Releases mit additiven Migrationen ist ein DB-Backup als Sicherheitsnetz sinnvoll. Für Releases mit Breaking Changes in der Datenbank ist ein vollständiger DB-Restore die einzige sichere Option — mit dem Risiko von Datenverlust seit dem Backup.

Die wichtigste Praxis ist die Rollback-Klassifizierung vor jedem Release: Was enthält dieses Release, und welche Rollback-Strategie ist deshalb nötig? Diese Information muss dokumentiert und für das gesamte Team sichtbar sein. GitLab-Pipeline-Variablen bieten eine pragmatische Möglichkeit, diese Klassifizierung maschinenlesbar zu machen und die richtigen Rollback-Jobs zu aktivieren.

Rollback-Strategien für Magento — Das Wichtigste auf einen Blick

Vier Schichten

Dateien (Symlink), Datenbank (Backup/Restore), Queue (Stop/Restart) und externe Dienste (Cache, ES-Index). Jede Schicht hat eigene Rollback-Anforderungen.

Entscheidung vor dem Release

Rollback-Strategie vor jedem Release klassifizieren. Enthält es DB-Migrationen? Queue-Änderungen? Diese Antwort bestimmt, welche Schritte im Störfall nötig sind.

DB-Backup als Pflicht

Vor jedem Release mit Datenbankmigrationen ein vollständiges DB-Backup anlegen. Ohne Backup kein echter Rollback-Pfad für DB-Änderungen.

Queue und ES separat

Queue-Consumer nach Rollback immer neu starten. Elasticsearch bei Index-Änderungen vollständig reindexieren. Beide Schritte aus dem Rollback-Skript automatisieren.

10. Typische Fehler bei Rollback-Strategien

Der gefährlichste Fehler ist ein Datei-Rollback bei einem Release, das Breaking-DB-Migrationen enthielt. Das Ergebnis: Der alte Code läuft mit der neuen Datenbankstruktur. Magento versucht, auf Spalten zuzugreifen, die entfernt wurden, oder ignoriert Spalten, die noch nicht da sein sollten. Die Fehler sind subtil und manchmal erst unter Last sichtbar — wenn zum Beispiel ein Checkout-Prozess auf eine nicht mehr existierende Spalte in sales_order zugreift.

Ein zweiter Fehler ist das Rollback ohne anschließenden Cache-Flush. Redis hält Objekte im Cache, die mit dem alten Code möglicherweise inkompatibel sind — insbesondere wenn neue Cache-Tags oder veränderte Objekt-Strukturen im fehlerhaften Release eingeführt wurden. Der Cache-Flush nach dem Rollback ist immer Pflicht, auch wenn er für Releases ohne Caching-Änderungen scheinbar unnötig ist. Die Kosten eines Cache-Flushes (kurzer Performance-Einbruch) sind minimal gegenüber den Kosten inkonsistenter Cache-Daten.

11. FAQ: Rollback-Strategien für Magento

1Nach DB-Migration einfach Symlink zurückschalten?
Nur bei additiven Migrationen. Bei Breaking Changes muss DB-Restore durchgeführt werden — sonst läuft alter Code mit inkompatibler DB-Struktur.
2Was ist das Expand-Contract-Muster?
Expand: kompatible DB-Änderungen deployen, alter Code läuft weiter. Contract: alte Spalten erst entfernen wenn neues Release stabil ist. Macht Rollback ohne DB-Restore möglich.
3Wie lange dauert ein DB-Restore?
Abhängig von DB-Größe. 5–20 GB typischer Shop: 30–90 Minuten mit mysqldump. Percona XtraBackup deutlich schneller.
4Was passiert mit Bestellungen während des Fehlers?
Bei DB-Restore verloren. Unvermeidbar. Backup-Zeitpunkt möglichst nah am Deploy-Zeitpunkt. Bestellungen aus Fehlerfenster manuell nachtragen.
5DB-Migrationen im Release erkennen?
Code-Review der db_schema.xml-Änderungen + git diff. Als Pipeline-Variable CONTAINS_DB_MIGRATIONS=true explizit setzen.
6Nach Rollback immer reindexieren?
Nur wenn das zurückgegrollte Release Elasticsearch-Mapping oder Indexierungs-Routinen geändert hat. Sonst nicht notwendig.
7Was passiert mit offenen Sessions?
Sessions in shared/var/session bleiben erhalten. Wenn neues Release Session-Strukturen verändert hat, bei Problemen Session-Verzeichnis leeren.
8Rollback ohne DB-Backup erzwingen?
Nein. DB-Backup-Job als needs:-Voraussetzung des Deploy-Jobs konfigurieren. GitLab verhindert Deploy ohne vorherigen Backup.
9Queue-Nachrichten beim Rollback verloren?
Ja, wenn Queue geleert wird. Bewusste Entscheidung: Kontrolliierter Verlust weniger Nachrichten besser als inkonsistente Verarbeitung.
10Rollback-Strategie pro Release dokumentieren?
Im CHANGELOG und GitLab Release Object explizit vermerken. Als Pipeline-Variable setzen. Die Information muss vor dem Rollback zugänglich sein, nicht erst danach.