Zum Inhalt

Deployment, Migrationen & CI

Konfiguration

Die gesamte Konfiguration erfolgt über Umgebungsvariablen. Die vollständige Liste steht in .env.example.

Variable Pflicht Zweck
DATABASE_URL ja PostgreSQL-Verbindungszeichenkette
SESSION_SECRET ja Signaturschlüssel für Session-Cookies und Schlüssel der Adress-Zähler des Formular-Rate-Limits (openssl rand -hex 32). Ohne den Wert startet die Web-App nicht, unter 32 Zeichen gibt es eine Warnung. Der Worker braucht ihn nicht.
SETTINGS_ENCRYPTION_KEY ja, für Geheimnisse 64 Hex-Zeichen, verschlüsselt SMTP-, Resend-, Twilio-, Webhook- und VAPID-Geheimnisse
SETTINGS_ENCRYPTION_KEY_OLD nur beim Rotieren Bisheriger Schlüssel für pnpm db:rotate-secret-key, danach wieder entfernen
ADMIN_PASSWORD_HASH optional Bootstrap-Admin, nur solange kein Datenbank-Admin existiert
ADMIN_EMAIL optional E-Mail des Bootstrap-Admins, Standard admin@status.local
ADMIN_BOOTSTRAP_DISABLED_AFTER optional ISO-Datum, ab dem die Bootstrap-Anmeldung abgelehnt wird
APP_URL empfohlen Öffentliche Basis-URL für Links in E-Mails und Double Opt-in. Auch der Worker braucht sie.
NEXT_PUBLIC_STATUS_PAGE_URL optional Hat für Links Vorrang vor APP_URL. Reihenfolge: NEXT_PUBLIC_STATUS_PAGE_URL, APP_URL, http://localhost:<PORT>.
NEXT_PUBLIC_SITE_URL optional Basis-URL der Links im RSS-Feed, Standard http://localhost:3000. In Produktion setzen, APP_URL gilt hier nicht.
NEXT_PUBLIC_STATUS_PAGE_NAME optional Ersatzname, solange in den Einstellungen kein Seitenname gespeichert ist, und Vorgabe für db:seed. Der angezeigte Name kommt aus den Einstellungen.
TRUSTED_PROXY_IP_HEADER optional Woher die Client-IP kommt: auto (Standard), x-real-ip oder x-forwarded-for
TRUSTED_PROXY_COUNT optional Anzahl vertrauenswürdiger Proxys im Modus x-forwarded-for, Standard 1
AUTH_RATELIMIT_* optional Drosselung von Anmeldung und API-Tokens. AUTH_RATELIMIT_ENABLED=false schaltet sie ab.
PUBLIC_FORM_RATELIMIT_* optional Drosselung der öffentlichen Formulare: _ENABLED sowie _IP_, _EMAIL_ und _UNKNOWN_IP_ je mit MAX, WINDOW_MIN und LOCK_MIN. Standard 10/60/60, 1/10/10 und 100/60/60.
STATUS_PAGE_EMAIL_DRIVER optional capture oder log protokolliert E-Mails, statt sie zu senden, und meldet immer Erfolg (Tests)
STATUS_PAGE_SMS_DRIVER optional capture protokolliert SMS, statt sie zu senden (Tests)
LOG_LEVEL, LOG_FORMAT optional Schwelle des Loggers (debug bis error) und Ausgabe (json oder pretty)
NEXT_ALLOWED_DEV_ORIGINS nur Entwicklung Weitere Hosts für den Dev-Server, kommagetrennt

E-Mail-, SMS-, Webhook- und Push-Anbieter werden in der Admin-Oberfläche konfiguriert, nicht in Umgebungsvariablen. Ausnahme ist SETTINGS_ENCRYPTION_KEY, der in der Umgebung gesetzt werden muss.

Warning

.env.example setzt STATUS_PAGE_EMAIL_DRIVER=capture. Entfernen Sie die Zeile, wenn Sie die Datei als Vorlage für die Produktion nutzen. Sonst meldet jeder Versand Erfolg, ohne dass eine Mail rausgeht.

Danger

Der Verschlüsselungsschlüssel wird nicht in der Datenbank gespeichert. Geht er verloren, können verschlüsselte Geheimnisse nicht mehr gelesen werden und müssen neu eingegeben werden. Sichern Sie ihn zusammen mit dem Datenbank-Dump.

Geheimnisse verschlüsseln und Schlüssel rotieren

  • Wurde SETTINGS_ENCRYPTION_KEY erst nachträglich gesetzt, verschlüsselt pnpm db:encrypt-secrets einmalig die bisher im Klartext gespeicherten Geheimnisse.
  • Zum Rotieren den bisherigen Schlüssel als SETTINGS_ENCRYPTION_KEY_OLD und den neuen als SETTINGS_ENCRYPTION_KEY setzen, pnpm db:rotate-secret-key ausführen und SETTINGS_ENCRYPTION_KEY_OLD danach entfernen.
  • Gegen eine nicht lokale Datenbank laufen beide Skripte nur mit --yes-i-am-sure.

Docker

Datei Zweck
Dockerfile Image der Web-App. Startet next start -p 3100. Enthält einen HEALTHCHECK auf /api/health.
Dockerfile.worker Image des Workers. Gleiche Codebasis, ohne Build-Schritt und ohne Port. Läuft als zweite Anwendung.
docker-compose.yml Lokales PostgreSQL 16 (Port 5433) und optionaler Container für die E2E-Anwendung
docker build -t slatus .
docker run -p 3100:3100 --env-file .env slatus

Info

Das Image lauscht auf Port 3100. Veröffentlichen Sie diesen Port, nicht 3000. Fehlt SESSION_SECRET, startet der Container nicht, und der HEALTHCHECK schlägt fehl.

Web-App und Worker werden aus demselben Repository deployt, mit identischer DATABASE_URL und identischem SETTINGS_ENCRYPTION_KEY. Betreiben Sie genau eine Worker-Instanz.

Reverse-Proxy

Die Rate-Limits zählen je Client-IP. Betreiben Sie SLAtus deshalb hinter einem vertrauenswürdigen Reverse-Proxy mit HTTPS, der die Client-IP weitergibt, und stellen Sie TRUSTED_PROXY_IP_HEADER passend ein:

  • Proxy setzt X-Real-IP (zum Beispiel nginx mit proxy_set_header X-Real-IP $remote_addr;): TRUSTED_PROXY_IP_HEADER=x-real-ip.
  • Proxy hängt nur an X-Forwarded-For an: TRUSTED_PROXY_IP_HEADER=x-forwarded-for und TRUSTED_PROXY_COUNT auf die Zahl der Proxys vor der App.
  • auto liest x-real-ip und sonst den letzten Eintrag von x-forwarded-for. Hinter einem reinen X-Forwarded-For-Proxy kann ein Client dann einen eigenen X-Real-IP-Header einschleusen.
  • Ohne Proxy füllt Next.js X-Forwarded-For nur, wenn der Header fehlt. Ein Client kann seine IP dann fälschen.
  • Setzt der Proxy keinen IP-Header, teilen sich alle Besucher die IP des Proxys. Die App loggt das in Produktion einmal als Warnung.

Details zur Erkennung stehen unter Authentifizierung & Sicherheit.

Migrationen

  • Migrationen sind nur vorwärts gerichtet. Jede Migration ist eine SQL-Datei in drizzle/, erzeugt mit pnpm db:generate aus lib/db/schema.ts.
  • Angewendet werden sie ausschließlich mit pnpm db:migrate (scripts/migrate.ts). Jede Migration läuft in einer eigenen Transaktion, weil PostgreSQL einen neuen Enum-Wert erst nach dem Commit verwenden kann.
  • Rollbacks sind dokumentiert, aber nicht automatisiert. Siehe docs/context/migrations.md im Repository.

Übliche Reihenfolge beim Deployment: Worker und Web-App anhalten (oder mit kurzer Pause deployen), pnpm db:migrate ausführen, die neue Version starten. Fehlende Template-Zeilen neuer Event-Typen legt der Worker beim Start an.

Lokale Entwicklung

cp .env.example .env          # SESSION_SECRET, SETTINGS_ENCRYPTION_KEY, ADMIN_PASSWORD_HASH setzen
pnpm install
pnpm db:up                    # PostgreSQL in Docker auf Port 5433
pnpm db:migrate
pnpm db:seed                  # Demodaten; Entwickler-Admin: admin@status.local / admin123
pnpm dev                      # http://localhost:3000
pnpm worker                   # zweites Terminal, nötig für Benachrichtigungen und Checks

Die Seed-Skripte löschen Daten. Sie laufen nur gegen einen lokalen Host oder eine Datenbank, deren Name auf _dev, _e2e oder _test endet. Für andere Ziele ist --yes-i-am-sure nötig. Alle db:*-Skripte lesen .env.

Mit Next.js 16 schreibt pnpm dev nach .next/dev und pnpm build nach .next, beide können parallel laufen. Ein zweiter next dev im selben Verzeichnis wird abgewiesen. Die E2E-Tests nutzen über NEXT_DIST_DIR ein eigenes Verzeichnis .next-e2e.

Qualitätstore

Befehl Prüft
pnpm typecheck TypeScript (tsc --noEmit)
pnpm lint ESLint
pnpm test Unit-Tests (Vitest)
pnpm test:integration Integrationstests gegen eine echte Datenbank
pnpm build Produktionsbuild
./scripts/agent-verify.sh Typecheck, Lint, Unit-Tests, db:migrate (nur wenn die Datenbank auf localhost:5433 erreichbar ist) und Build. Keine Integrationstests. E2E nur mit RUN_E2E=1.
pnpm test:e2e Playwright-End-to-End-Tests mit eigener Datenbank statuspage_e2e. Zwei Projekte: main mit allen Specs und danach i18n-de für die deutsche Oberfläche. Standard ist next dev, der Container e2e-app nur mit E2E_USE_DOCKER=1.

Continuous Integration

.github/workflows/ci.yml läuft bei Pushes auf main, bei release-*-Tags und bei Pull Requests. Ein neuer Push bricht den laufenden Lauf desselben Branches ab. Reine Dokumentationsänderungen (docs/**, .claude/**, **/*.md) lösen keinen CI-Lauf aus.

Der Workflow startet einen PostgreSQL-16-Dienst und führt nacheinander aus:

  1. pnpm audit --audit-level high (blockierend). Bekannte Ausnahmen stehen in pnpm-workspace.yaml unter auditConfig.ignoreGhsas, Sicherheits-Overrides unter overrides.
  2. pnpm db:migrate und pnpm db:seed
  3. ./scripts/agent-verify.sh
  4. pnpm test:integration (blockierend)
  5. die Playwright-Suite

Spiegelung: .github/workflows/mirror.yml veröffentlicht bei release-*-Tags den deploybaren Anwendungscode ohne Agent-Setup und Dokumentation in das Deploy-Repository Mach-IIDeV/slatus-web. Welche Dateien gespiegelt werden, legt scripts/app-files.txt fest. Der Workflow lässt sich auch manuell mit einem beliebigen Commit, Tag oder Branch starten.