Mayday Hetzner Staging Deploy

Overview

Mayday-Rhynern-Webseite (Next.js + Payload CMS) von Railway auf Hetzner umziehen, als Staging unter staging.mayday-rhynern.de. Produktive mayday-rhynern.de (alte Wordpress-Site) bleibt unangetastet — Cutover separat.

Problem Frame

Railway wird abgeloest (AWS-First-Policy, Mayday explizit als Hetzner-Projekt geplant). Staging-Deploy heute Abend als kontrollierter erster Schritt, ohne Produktion zu gefaehrden.

Scope Boundaries

  • In Scope: Staging-Subdomain, App + Postgres + R2-Anbindung + Cloudflared-Tunnel, basic Smoke-Test
  • Out of Scope (heute): Daten-Migration aus Railway (Conny baut Test-Inhalte zur Not neu), Domain-Cut auf mayday-rhynern.de, MailerSend-Production-Config, Stripe-Live-Keys, Cloudflare-Access-Layer

Hosting-Entscheidung

Bare Docker + Cloudflared-Tunnel auf bestehendem av-tools-shared-01 (cx23, 46.225.16.231). Kein Coolify, kein neuer Server. Pattern analog Stirling/Uptime-Kuma laut av-tools.

Cutoff-Kriterien

  • 21:30 Hard-Stop — wenn nicht durch, letzte funktionierende Stelle dokumentieren, weiter morgen
  • Postgres-Container kommt nicht hoch → abbrechen, ist Hauptpfad
  • Next.js-Build schlaegt auf Server fehl → Image lokal bauen + via Docker Hub / GHCR pushen als Plan B (kostet zusaetzliche 30 min, nicht heute)

Implementation Units

  • Unit 1: Repo-Pfad in Vault korrigieren + R2-Setup pruefen

Goal: Vault-Eintrag und R2-Bucket-Status klar fuer den Deploy.

Files:

  • Modify: intern/projekte/mayday-webrelaunch/_index.md (Repo-URL ergaenzen: marvin-khl/mayday-rhynern)
  • Modify: intern/kunden/mayday.md falls noetig

Approach: Cloudflare-MCP nutzen um R2-Bucket-Status zu checken (existiert mayday-media-Bucket schon? Credentials in 1Password?). Wenn nicht existiert: anlegen.

Verification: R2-Bucket-Name + Access/Secret-Key + Endpoint stehen bereit fuer Env-Vars.

  • Unit 2: Postgres-Container auf av-tools-shared-01

Goal: Postgres 16 mit persistentem Volume und einem mayday-DB-User auf 127.0.0.1:5432 (lokal-only).

Files (auf Server):

  • Create: /opt/mayday-db/docker-compose.yml
  • Create: /opt/mayday-db/.env (DB-Passwort, 0600 root)

Approach: Eigener Service-Ordner (nicht in mayday-app gemischt, damit Postgres-Restart die App nicht zwingt). postgres:16-alpine, bind 127.0.0.1:5432, named volume mayday_pgdata.

Verification: docker exec mayday-db psql -U mayday -c "SELECT 1" gibt 1 zurueck.

  • Unit 3: Mayday-App-Container bauen + deployen

Goal: Next.js-Standalone-Image aus dem Repo gebaut, App-Container laeuft auf 127.0.0.1:3010, schreibt in den Postgres-Container.

Files (lokal):

  • ~/source/Kunden/mayday/Dockerfile (existiert, multi-stage build)

Files (auf Server):

  • Create: /opt/mayday/docker-compose.yml
  • Create: /opt/mayday/.env (alle Runtime-Env-Vars laut .env.example, 0600 root)
  • Repo geklont nach /opt/mayday/source/

Approach: Build direkt auf dem Server (kein Registry-Push noetig, Pattern wie av-tools). NEXT_PUBLIC_SITE_URL=https://staging.mayday-rhynern.de als Build-Arg. DATABASE_URL zeigt auf den Postgres-Container via docker-network. PAYLOAD_SECRET + REVALIDATE_SECRET frisch generiert. PREVIEW_PASSWORD setzen damit Site nur fuer Berechtigte sichtbar.

Verification: curl http://127.0.0.1:3010/login auf dem Server gibt 200 + HTML mit Payload-Login.

  • Unit 4: Cloudflared-Ingress + DNS fuer staging.mayday-rhynern.de

Goal: https://staging.mayday-rhynern.de zeigt auf den App-Container ueber den existierenden av-tools-Tunnel.

Files (auf Server):

  • Modify: /etc/cloudflared/config.yml (neue Ingress-Regel staging.mayday-rhynern.dehttp://localhost:3010)

Files (Cloudflare):

  • DNS-CNAME staging.mayday-rhynern.de<av-tools-tunnel-id>.cfargotunnel.com

Approach: Mayday-Rhynern-Zone in Marvins Cloudflare-Account erstmal pruefen (vermutlich da, weil mayday-rhynern.de live laeuft auf Wordpress). CNAME via Cloudflare-MCP execute setzen (DNS-Update, nicht Tunnel-Route weil Tunnel ist im agenticventures.de-Zone-Setup aber Tunnel kann auch andere Zonen bedienen wenn der Account Zugriff hat).

Verification: curl -I https://staging.mayday-rhynern.de/login → 200, dann im Browser Login-Seite mit Payload-Branding.

  • Unit 5: Initial-Admin anlegen + Conny Bescheid sagen

Goal: Conny kann sich in Payload einloggen und Test-Content pflegen.

Approach: Beim ersten Admin-Login (Payload-First-User-Flow) Admin-User mit Marvin-Email + frisches Passwort. Conny-Account dann als zweiter User mit ihrer Mail. Kurze Mail an Conny mit Login-URL + Preview-Passwort.

Verification: Conny kommt rein und kann ein Test-Event anlegen.

Vault-Doku-Updates nach Deploy

  • intern/capabilities/hetzner/av-tools.md — Services-Tabelle ergaenzen (mayday auf Port 3010)
  • intern/projekte/mayday-webrelaunch/_index.md — Historie + Naechste Meilensteine aktualisieren (Hetzner-Deploy ✓, Domain-Cut auf prod ist naechster Schritt)

Risks & Dependencies

RiskMitigation
Docker-Build auf cx23 (4 GB RAM) zu langsam / OOMWenn fehlschlaegt, Image lokal bauen + via GHCR push. Nicht heute Abend, dann morgen
R2-Bucket nicht existiert / Keys verlorenLokal media-Folder Fallback im Dockerfile, R2 nachziehbar
MailerSend-Key nicht greifbarStaging kann ohne Email-Versand laufen — Buchungen werden eingelegt, Bestaetigungs-Mails koennen erstmal blocken
Falsche Zone-Permission CloudflareDNS via UI als Fallback, Tunnel-Ingress geht trotzdem

Sources