Search by

ppag / contao-autoupdate-bundle

partner-partner

Daily security and patch-level updates for Contao 5.3/5.7 installations, with backup, health check and rollback.

Package info

gitlab.com/pp_cto_modules/contao-autoupdater

Issues

Type:contao-bundle

pkg:composer/ppag/contao-autoupdate-bundle

Statistics

Installs: 66

Dependents: 0

Suggesters: 0

Stars: 0

1.3.0 2026-10-15 08:42 UTC

This package is auto-updated.

Last update: 2026-10-15 06:43:06 UTC


README

Tägliche Sicherheits- und Patch-Updates für Contao 5.3 und 5.7, mit Backup, Health-Check und Rollback. Jede Installation aktualisiert sich selbst (als ihr eigener System-User) und schreibt einen JSON-Report, den ein separater Orchestrator einsammelt und per Mail zusammenfasst.

Installation

composer require ppag/contao-autoupdate-bundle:^1.0

Das Bundle aktualisiert sich selbst innerhalb der Major-Version (siehe Selbst-Update). Eine fest gepinnte Version (1.0.4) verhindert das, der Report weist dann darauf hin.

Voraussetzungen:

  • Contao 5.3 oder 5.7, PHP ≥ 8.1, ext-curl
  • Composer: Ist der Composer auf dem Server älter als 2.8 (z. B. 2.2 aus dem Debian/Ubuntu-Paket), lädt der Autoupdater beim ersten Lauf eine aktuelle composer.phar ins Datenverzeichnis (Prüfsumme wird geprüft) und aktualisiert sie wöchentlich. Dafür braucht der Server ausgehenden Zugriff auf getcomposer.org.
  • proc_open darf nicht in disable_functions stehen
  • ein System-Cron, der contao:cron jede Minute als Site-User aufruft:

    * * * * * /usr/bin/php /pfad/zur/installation/vendor/bin/contao-console contao:cron
    

Konfiguration (.env.local)

AUTOUPDATE_MODE=check              # off | check | security | patch (Standard: check)
AUTOUPDATE_SITE_KEY=               # optional, Standard: Domain aus /home/www-<kunde>/<domain>/...
AUTOUPDATE_HOUR=6                  # optional 0–23, Stunde des Update-Fensters (Standard 6 = 06:00–06:59)
AUTOUPDATE_SLOT=                   # optional 0–59, überschreibt den Hash-Slot
AUTOUPDATE_CHECK_URLS=             # optional, Standard: /,/contao/login (z. B. /,/kontakt,/contao/login)
AUTOUPDATE_BASE_URL=               # optional, Standard: https://<AUTOUPDATE_SITE_KEY>
AUTOUPDATE_DATA_DIR=               # optional, Standard: <root>/shared/autoupdater (lokal: var/autoupdate)
AUTOUPDATE_COMPOSER=               # optional, eigener Composer (ab 2.8, sonst wird composer.phar geladen)
AUTOUPDATE_SIZE_TOLERANCE=0.3      # optional, erlaubte Grössenabweichung im Health-Check
AUTOUPDATE_KEEP_REPORTS=30         # optional, Anzahl Tagesreports
AUTOUPDATE_HTTP_AUTH=              # optional "user:passwort" für Seiten mit Server-Auth (sonst Health-Check über den Kernel)
AUTOUPDATE_VERIFY_TLS=             # optional, Standard: 1; 0 nur lokal (selbstsignierte Zertifikate)
AUTOUPDATE_ACCEPT_404=             # optional, Standard: 1; 0 = 404 aus dem Kernel nicht als Ausgangslage akzeptieren
AUTOUPDATE_SELF_UPDATE=on          # on | early | off (Standard: on), siehe Selbst-Update
AUTOUPDATE_SELF_UPDATE_DELAY=3     # optional, Wartezeit in Tagen für "on"

Der Site-Key bestimmt den Slot (Minute innerhalb von AUTOUPDATE_HOUR), den Namen im Report und die Health-Check-URL (https://<site-key>). Liegt die Installation nach der Server-Konvention unter /home/www-<kunde>/<domain>/ (auch mit releases/<n> oder current), wird die Domain automatisch verwendet. Nur bei abweichendem Pfad AUTOUPDATE_SITE_KEY setzen, und AUTOUPDATE_BASE_URL nur, wenn die Seite nicht unter https://<domain> erreichbar ist (z. B. mit www.).

ModusVerhalten
offnichts prüfen, nur einen Report skipped schreiben
checkAdvisories und Contao-Patches melden, nichts ändern
securitynur Sicherheitsfixes einspielen
patchSicherheitsfixes + Contao-Patch-Updates (z. B. 5.3.38 → 5.3.39)

Seiten, die per Git/Deployer ausgerollt werden, im Modus check betreiben oder die lock_diff aus dem Report ins Repository übernehmen. Sonst dreht das nächste Deployment das Update zurück.

Reports, Tagesstatus und Backups liegen ausserhalb des Releases in <root>/shared/autoupdater, damit sie ein Deployment überleben. <root> ist /home/www-<kunde>/<domain> (wie PP_*_ROOT) bzw. das Verzeichnis über current oder releases/<n>. Nur ausserhalb dieser Struktur (lokal) bleibt es bei var/autoupdate im Projekt. Das Verzeichnis wird bei Bedarf angelegt. Beim ersten Lauf werden state.json, self-update.json, latest.json und die Tagesreports aus dem alten var/autoupdate übernommen.

Ablauf

  1. Der Cronjob läuft jede Minute (nur im CLI-Scope, nie über den Webcron) und wird nur innerhalb von AUTOUPDATE_HOUR aktiv (Standard 06:00–06:59, Server-Zeitzone von PHP, z. B. Europe/Zurich).
  2. Ab dem Slot der Installation (crc32(AUTOUPDATE_SITE_KEY) % 60) startet er einmal pro Tag ppag:autoupdate:run als separaten Prozess.
  3. Der Command prüft mit composer audit und composer outdated "contao/*" --patch-only.
  4. Gibt es etwas einzuspielen: Health-Check vorher → Backup (composer.json/.lock + contao:backup:create) → Maintenance-Mode → composer update → Migrations-Check → cache:clear/cache:warmup → Maintenance-Mode aus → Health-Check nachher.
  5. Neue Datenbank-Schemaänderungen werden nie automatisch ausgeführt: Rollback, Status rolled_back.
  6. Schlägt der Health-Check fehl (Status ≥ 500, fehlender <title>, Grössenabweichung oder neue CRITICAL/ERROR-Einträge in var/logs/prod-*.log), wird zurückgerollt. Gezählt werden nur Log-Einträge ab dem Moment, in dem der neue Code live ist. Einträge zu Dateien des alten, durch cache:clear gelöschten Containers (var/cache/prod/Container<hash>/) stammen von Prozessen, die vor dem Update gestartet wurden (Messenger-Worker, contao:cron), und werden ignoriert. Die Worker werden mit messenger:stop-workers neu gestartet.
  7. Ist die Seite über HTTP nicht erreichbar (HTTP-Auth ohne AUTOUPDATE_HTTP_AUTH, IP-Sperre, DNS), läuft der Health-Check über den Contao-Kernel: Jede URL wird in einem eigenen PHP-Prozess wie über public/index.php abgearbeitet, ohne Webserver und ohne HTTP-Cache. Ein 404 aus dem Kernel gilt dabei als gültige Ausgangslage (z. B. unveröffentlichte Seiten auf Staging), nach dem Update wird dann die 404-Seite verglichen (abschaltbar mit AUTOUPDATE_ACCEPT_404=0). Nur wenn auch das fehlschlägt (Status 0, 5xx oder ein anderer 4xx), wird nichts aktualisiert.

Selbst-Update

Neue Versionen des Bundles werden gestaffelt ausgerollt, damit ein fehlerhaftes oder kompromittiertes Release nicht am nächsten Morgen auf allen Installationen läuft:

AUTOUPDATE_SELF_UPDATEVerhalten
earlyTestgruppe (z. B. eigene Seiten): neue Version beim nächsten Lauf
onalle anderen: erst AUTOUPDATE_SELF_UPDATE_DELAY Tage (Standard 3), nachdem die Installation die Version zum ersten Mal gesehen hat
offnie, z. B. als Notbremse
  • Die Wartezeit zählt ab der ersten Sichtung (self-update.json im Datenverzeichnis), nicht ab dem Release-Datum: Das Datum eines Git-Tags lässt sich fälschen. Ein fehlerhaftes Release in dieser Zeit zurückziehen (Tag löschen bzw. eine korrigierte Version taggen).
  • Nur stabile Versionen derselben Major-Version, nie ein Downgrade. Nur das Bundle selbst wird aktualisiert, nicht seine Abhängigkeiten (z. B. Contao).
  • Nur in den Modi security und patch. Im Modus check steht eine neue Version nur im Report.
  • Das Selbst-Update kommt vor dem Paket-Update, damit ein Fix im Bundle nicht von einem scheiternden Paket-Update blockiert wird. Nach einem erfolgreichen Selbst-Update läuft das Paket-Update in einem neuen Prozess mit dem neuen Code (--after-self-update). Rollt das Selbst-Update zurück, folgt das Paket-Update mit dem bisherigen Code.
  • Der Report enthält das Ergebnis des Selbst-Updates in self_update (status, backup, rollback, ...). Der Gesamtstatus ist der schlechtere von beiden: Ein zurückgerolltes Selbst-Update bleibt sichtbar.
  • Gleicher Ablauf wie jedes Update: Backup, Maintenance-Mode, Health-Check, bei Problemen Rollback. Der laufende Prozess arbeitet mit dem alten Code weiter, eine kaputte neue Version wird also vom alten Code zurückgerollt.
  • Startet der neue Prozess mit der neuen Version nicht, steht failed im Report. Startet die neue Version am nächsten Tag gar nicht mehr, fehlt der Tagesreport. Beides meldet der Orchestrator als kritisch.

Neue Features also einfach taggen: Die Testgruppe hat sie am nächsten Tag, alle anderen nach der Wartezeit.

Report

<root>/shared/autoupdater/latest.json und <root>/shared/autoupdater/reports/YYYY-MM-DD.json (atomar geschrieben, Rechte 0644). Der Orchestrator (www-data) braucht Leserechte auf diesen Pfad, inklusive der übergeordneten Verzeichnisse.

status: ok | updated | skipped | action_required | rolled_back | failed

self_update (nur wenn eine neuere Version existiert): installed, available (neueste Version), target (Version, die jetzt installiert werden darf, sonst null) und due_at (ab wann available installiert wird). Wurde das Selbst-Update ausgeführt, zusätzlich status, updates, backup, health_problems, rollback, notes und errors.

Weitere Dateien in <root>/shared/autoupdater/: state.json (Tagesstatus), run.log (Ausgabe des letzten Cron-Laufs), self-update.json (erste Sichtung neuer Bundle-Versionen), backup/<datum>/ (composer.json/.lock vor dem Update). Datenbank-Backups landen in var/backups/.

Manuell ausführen

vendor/bin/contao-console ppag:autoupdate:run --mode=check --no-report   # nur anzeigen
vendor/bin/contao-console ppag:autoupdate:run                            # wie der Cron, mit Report

Entwicklung

composer install
vendor/bin/phpunit

Lokal in einer Contao-Installation testen (Path-Repository mit Symlink), in deren composer.json:

"repositories": [
    { "type": "path", "url": "../../_modules/contao-autoupdater", "options": { "symlink": true } }
],
"require": {
    "ppag/contao-autoupdate-bundle": "@dev"
}

Lizenz

MIT