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_KEYerst nachträglich gesetzt, verschlüsseltpnpm db:encrypt-secretseinmalig die bisher im Klartext gespeicherten Geheimnisse. - Zum Rotieren den bisherigen Schlüssel als
SETTINGS_ENCRYPTION_KEY_OLDund den neuen alsSETTINGS_ENCRYPTION_KEYsetzen,pnpm db:rotate-secret-keyausführen undSETTINGS_ENCRYPTION_KEY_OLDdanach 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 mitproxy_set_header X-Real-IP $remote_addr;):TRUSTED_PROXY_IP_HEADER=x-real-ip. - Proxy hängt nur an
X-Forwarded-Foran:TRUSTED_PROXY_IP_HEADER=x-forwarded-forundTRUSTED_PROXY_COUNTauf die Zahl der Proxys vor der App. autoliestx-real-ipund sonst den letzten Eintrag vonx-forwarded-for. Hinter einem reinenX-Forwarded-For-Proxy kann ein Client dann einen eigenenX-Real-IP-Header einschleusen.- Ohne Proxy füllt Next.js
X-Forwarded-Fornur, 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 mitpnpm db:generateauslib/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.mdim 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:
pnpm audit --audit-level high(blockierend). Bekannte Ausnahmen stehen inpnpm-workspace.yamlunterauditConfig.ignoreGhsas, Sicherheits-Overrides unteroverrides.pnpm db:migrateundpnpm db:seed./scripts/agent-verify.shpnpm test:integration(blockierend)- 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.