Theoretischer Server Umzug #32

Open
opened 2026-08-22 11:54:57 +02:00 by admin · 4 comments
Owner

Eventueller Umzug auf eine eigene Hardware

Eventueller Umzug auf eine eigene Hardware
Author
Owner

Angedachte Specs:
Dell Optiplex 5090 Micro | Intel Core i5-10500T 2.30 GHz | 16GB |
Prozessor
Prozessor i5-10500T
Prozessorfamilie Intel® Core™ i5
Anzahl Prozessorkerne 6
Prozessor-Taktfrequenz 2,3 GHz
Prozessor-Boost-Frequenz 3,8 GHz

Arbeitsspeicher
Speicherkapazität 16 GB
Interner Speichertyp DDR4-SDRAM
Speichertaktfrequenz 3200 MHz
RAM-Speicher maximal 64 GB
Speicherlayout 1 x 16 GB
Speicherkartensteckplätze 2x DIMM

Angedachte Specs: Dell Optiplex 5090 Micro | Intel Core i5-10500T 2.30 GHz | 16GB | Prozessor Prozessor i5-10500T Prozessorfamilie Intel® Core™ i5 Anzahl Prozessorkerne 6 Prozessor-Taktfrequenz 2,3 GHz Prozessor-Boost-Frequenz 3,8 GHz Arbeitsspeicher Speicherkapazität 16 GB Interner Speichertyp DDR4-SDRAM Speichertaktfrequenz 3200 MHz RAM-Speicher maximal 64 GB Speicherlayout 1 x 16 GB Speicherkartensteckplätze 2x DIMM
Author
Owner

Anleitung: Worauf beim Umzug auf einen eigenständigen Server zu achten ist

Bestandsaufnahme per SSH auf 192.168.1.222 (2026-08-22): 4-Kern-NAS (i5-6500), 15 GB RAM, Disk 86 % voll (32 GB frei), ~45 Container von anderen Projekten (Immich, Paperless, Gitea selbst, GlitchTip, pflege/gon/stllager, …) teilen sich die Kerne mit den 3 PawFeed-Replicas. Das war schon der Auslöser für die Replica-Skalierung (#26) — ein eigener Server für PawFeed löst das grundsätzlicher.

Die gute Nachricht zuerst: fast der ganze Zustand liegt schon extern

PawFeed selbst ist praktisch zustandslos. Auf dem NAS liegen nur:

  • pawfeed-N-Container (Next.js Standalone, node server.js)
  • pawfeed-redis (Feed-Cache, Rate-Limits, Wartungsmodus-Flag, DAU/MAU-Zählung)

Alles andere ist bereits Cloud/extern und muss nicht migriert werden, solange die ENV-Werte gleich bleiben:

  • Postgres-DB → Supabase Cloud (nicht auf dem NAS)
  • Datei-/Avatar-Storage → Supabase Storage
  • Video → Mux
  • Auth → Clerk
  • Zahlungen → Stripe (LIVE-Mode!)

Das bedeutet: keine DB-Migration, keine Storage-Kopie, keine Downtime-kritische Datenwanderung für den größten Teil des Systems.

Der eine Punkt, der wirklich Denkarbeit braucht: Netzwerk-Kopplung an NPM

Aktuell hängen NPM (Nginx Proxy Manager) und die PawFeed-Container im selben Docker-Bridge-Netzwerk (nginx_default) auf derselben Maschine — NPM erreicht die Replicas per Container-DNS-Name (pawfeed-1:3000 usw., siehe docker/npm-upstream.conf). Sobald PawFeed auf anderer physischer Hardware läuft, existiert dieses gemeinsame Docker-Netzwerk nicht mehr — NPM kann die Container nicht mehr per Docker-DNS erreichen.

Zwei Optionen:

Option A (empfohlen): NPM bleibt der zentrale Reverse Proxy auf dem alten NAS.

  • Kein DNS-Wechsel nötig (Cloudflare zeigt weiter auf dieselbe DDNS-Adresse/öffentliche IP desselben Routers)
  • Bestehendes Let's-Encrypt-Zertifikat bleibt gültig
  • Änderung: docker/npm-upstream.conf-Äquivalent (/data/nginx/custom/http_top.conf auf dem NPM-Volume) bekommt statt Docker-DNS-Namen die LAN-IP des neuen Servers + gemappte Ports, z. B.:
    upstream pawfeed_upstream {
        least_conn;
        server 192.168.1.XXX:3001 max_fails=3 fail_timeout=30s;
        server 192.168.1.XXX:3002 max_fails=3 fail_timeout=30s;
        server 192.168.1.XXX:3003 max_fails=3 fail_timeout=30s;
    }
    
  • Voraussetzung: docker-compose.yml auf dem neuen Server muss die 3 Replica-Ports auf den Host mappen (ports: "3001:3000" etc.) — aktuell macht das compose-File das nicht, weil es sich auf das gemeinsame Docker-Netzwerk verlässt.
  • Nachteil: alter NAS bleibt Single Point of Failure für den TLS-Terminierungspunkt.

Option B: Eigenständiges NGINX (oder Caddy/Traefik) direkt auf dem neuen Server.

  • Voll unabhängig vom alten NAS, eigenes Let's-Encrypt-Zertifikat für pawfeed.org
  • Erfordert: Router-Portweiterleitung 80/443 von der alten NAS-LAN-IP auf die neue Server-LAN-IP umstellen
  • Mehr Aufwand (neues Zertifikat, neue Firewall-Regeln), aber sauberer Schnitt — keine Restabhängigkeit vom NAS mehr für PawFeed.

Empfehlung: mit Option A starten (kleinerer Blast-Radius, reversibel), Option B als Folgeschritt, sobald der neue Server sich bewährt hat.

Checkliste nach Bereich

DNS / Domain

  • Nichts zu tun, falls Option A gewählt wird — pawfeed.org zeigt weiterhin auf dieselbe DDNS-Adresse/denselben Router.
  • Bei Option B: Router-Portforwarding 80/443 auf neue Server-IP umstellen, neues Zertifikat ausstellen (Certbot/acme.sh), Cloudflare-Einträge bleiben unverändert (Domain, nicht Server, wird aufgelöst).

Secrets / .env

  • docker/.env (15 Variablen: Supabase, Clerk, Stripe, Mux, Sentry/GlitchTip, SMTP, ADMIN_SECRET, ADMIN_OWNER_ID, CRON_SECRET, REDIS_PASSWORD) sicher auf den neuen Server übertragen — nicht per Git/FTP, sondern scp über SSH oder Passwort-Manager. Werte bleiben identisch (App-Ebene, nicht Server-Ebene) — kein Rotieren nötig, außer man will es bewusst.
  • Nach Transfer: temporäre Kopien löschen, chmod 600 docker/.env auf dem neuen Server.

Redis-Daten

  • Rate-Limits, Wartungsmodus-Flag, DAU/MAU-Sets, Stats-Cache: alles TTL-basiert/ephemer — unkritisch, darf leer starten.
  • Feed-Sorted-Sets (src/lib/feed-helpers.ts) sind der einzige Redis-Inhalt mit echtem UX-Wert — es gibt keinen Backfill-Mechanismus, der sie aus Postgres neu aufbaut (Feed wird nur beim fanOutPost bei neuen Posts befüllt). Bei einem Kaltstart auf leerem Redis sehen alle Nutzer beim Cutover kurzzeitig leere Feeds, bis neue Posts reinkommen.
    • Empfehlung: Docker-Volume redis_data klonen statt neu zu starten — sauberster Weg:
      # auf dem alten NAS: Redis kurz stoppen (Konsistenz), Volume sichern
      docker stop pawfeed-redis
      docker run --rm -v pawfeed_redis_data:/from -v /tmp:/to alpine \
        tar czf /to/redis-data.tar.gz -C /from .
      scp /tmp/redis-data.tar.gz <neuer-server>:/tmp/
      # auf dem neuen Server: Volume vorab anlegen, entpacken
      docker volume create pawfeed_redis_data
      docker run --rm -v pawfeed_redis_data:/to -v /tmp:/from alpine \
        tar xzf /from/redis-data.tar.gz -C /to
      
    • Downtime-Fenster dafür ist klein (Redis-Stop nur für den tar-Export), passt gut ins Cutover-Wartungsfenster.

Datenbank / Storage / Video / Auth / Zahlungen

  • Kein Handlungsbedarf — alles extern (Supabase, Mux, Clerk, Stripe). Läuft vom neuen Server aus identisch, solange die ENV-Werte stimmen und der neue Server Internetzugang hat.
  • Supabase-Connection-Pool-Limit im Auge behalten: aktuell DB_POOL_MAX=10 × 3 Replicas = 30 Verbindungen, weit unter dem Supabase-Limit (200). Bleibt unverändert, unabhängig vom Server.

Webhooks (Mux, Stripe, Clerk)

  • Zeigen alle auf https://pawfeed.org/api/webhooks/...domain-basiert, nicht server-basiert. Bei Option A (Domain bleibt gleich) ist hier nichts zu tun. Bei Option B ebenfalls nichts zu tun, solange die Domain gleich bleibt (nur das Zertifikat/der Terminierungspunkt wechselt).

Monitoring (GlitchTip)

  • GlitchTip läuft selbst-gehostet auf dem alten NAS (glitchtip-web, -worker, -postgres, -redis), komplett unabhängig vom PawFeed-Compose-Stack, erreichbar über glitchtip.pawfeed.org.
  • Entscheidung nötig: GlitchTip auf dem alten NAS belassen (PawFeed auf dem neuen Server sendet Events einfach per HTTPS über die öffentliche Domain dorthin — funktioniert ohne Änderung) oder ebenfalls auf den neuen Server umziehen. Für den ersten Schritt: auf dem alten NAS belassen, spätere Konsolidierung ist ein separates Thema.

Cron-Jobs

  • 3 Einträge in daniels Crontab auf dem alten NAS: trim-feeds (4 Uhr), anniversaries (8 Uhr), backup-db.sh (3 Uhr). Müssen auf dem neuen Server neu angelegt werden (crontab -e, Skripte aus docker/ mitnehmen).
  • Akuter Fund, unabhängig vom Umzug: run-cron.sh ruft aktuell http://localhost:4563 — diesen Host-Port gibt es seit der 3-Replica-Umstellung (#26, 2026-08-16) nicht mehr (kein Port-Mapping mehr im compose-File). Cron-Log zeigt seit 2026-08-18 durchgehend HTTP 000trim-feeds und anniversaries laufen seit 4 Tagen ins Leere. Sollte so oder so (auch ohne Umzug) auf https://pawfeed.org/api/cron/... umgestellt werden. Beim Server-Umzug gleich mit erledigen.
  • backup-db.sh (täglicher pg_dump der Supabase-DB, 14 Tage Retention) läuft unabhängig vom App-Server — kann 1:1 auf den neuen Server umziehen, sobald das DIRECT_URL-Secret dort liegt.

Hardware-Dimensionierung

  • Aktueller Engpass war reine CPU-Kontention mit ~45 fremden Containern auf 4 Kernen (siehe #26-Lasttest: harte Decke bei ~60–65 Req/Sek). Ein eigenständiger Server sollte mindestens 4, besser 6–8 Kerne exklusiv für PawFeed bereitstellen (Puffer für eine 4. oder 5. Replica, wenn's weiter wächst).
  • RAM: 3 Node-Prozesse + Redis + Docker-Build-Overhead — mit 8 GB dediziert komfortabel, 16 GB falls später mehr Replicas dazukommen.
  • Disk: reichlich Marge einplanen (alter NAS steht bei 86 % voll — das war nie PawFeed selbst, PawFeed-Verzeichnis ist nur ~1,5 GB, aber es zeigt, wie eng geteilte Hardware werden kann).
  • Docker ≥ 29.x / Compose ≥ v5.x auf dem neuen Server installieren (aktuelle Versionen auf dem NAS, zum Vergleich).

Deploy-Workflow

  • docker/rolling-deploy.sh, start.sh, docker-compose.yml unverändert vom Repo übernehmen (ggf. ports:-Mapping ergänzen, siehe Option A oben).
  • SSH-Key-Zugriff auf Gitea (ssh://git@192.168.1.222:2222/admin/petfeed.git) für den neuen Server einrichten, falls Deploys künftig per git pull statt scp laufen sollen.
## Anleitung: Worauf beim Umzug auf einen eigenständigen Server zu achten ist Bestandsaufnahme per SSH auf `192.168.1.222` (2026-08-22): 4-Kern-NAS (i5-6500), 15 GB RAM, Disk **86 % voll** (32 GB frei), ~45 Container von anderen Projekten (Immich, Paperless, Gitea selbst, GlitchTip, pflege/gon/stllager, …) teilen sich die Kerne mit den 3 PawFeed-Replicas. Das war schon der Auslöser für die Replica-Skalierung (#26) — ein eigener Server für PawFeed löst das grundsätzlicher. ### Die gute Nachricht zuerst: fast der ganze Zustand liegt schon extern PawFeed selbst ist praktisch zustandslos. Auf dem NAS liegen nur: - 3× `pawfeed-N`-Container (Next.js Standalone, `node server.js`) - 1× `pawfeed-redis` (Feed-Cache, Rate-Limits, Wartungsmodus-Flag, DAU/MAU-Zählung) Alles andere ist bereits Cloud/extern und muss **nicht** migriert werden, solange die ENV-Werte gleich bleiben: - **Postgres-DB** → Supabase Cloud (nicht auf dem NAS) - **Datei-/Avatar-Storage** → Supabase Storage - **Video** → Mux - **Auth** → Clerk - **Zahlungen** → Stripe (LIVE-Mode!) Das bedeutet: keine DB-Migration, keine Storage-Kopie, keine Downtime-kritische Datenwanderung für den größten Teil des Systems. ### Der eine Punkt, der wirklich Denkarbeit braucht: Netzwerk-Kopplung an NPM Aktuell hängen NPM (Nginx Proxy Manager) und die PawFeed-Container im **selben Docker-Bridge-Netzwerk** (`nginx_default`) auf derselben Maschine — NPM erreicht die Replicas per Container-DNS-Name (`pawfeed-1:3000` usw., siehe `docker/npm-upstream.conf`). Sobald PawFeed auf **anderer physischer Hardware** läuft, existiert dieses gemeinsame Docker-Netzwerk nicht mehr — NPM kann die Container nicht mehr per Docker-DNS erreichen. Zwei Optionen: **Option A (empfohlen): NPM bleibt der zentrale Reverse Proxy auf dem alten NAS.** - Kein DNS-Wechsel nötig (Cloudflare zeigt weiter auf dieselbe DDNS-Adresse/öffentliche IP desselben Routers) - Bestehendes Let's-Encrypt-Zertifikat bleibt gültig - Änderung: `docker/npm-upstream.conf`-Äquivalent (`/data/nginx/custom/http_top.conf` auf dem NPM-Volume) bekommt statt Docker-DNS-Namen die **LAN-IP des neuen Servers + gemappte Ports**, z. B.: ```nginx upstream pawfeed_upstream { least_conn; server 192.168.1.XXX:3001 max_fails=3 fail_timeout=30s; server 192.168.1.XXX:3002 max_fails=3 fail_timeout=30s; server 192.168.1.XXX:3003 max_fails=3 fail_timeout=30s; } ``` - Voraussetzung: `docker-compose.yml` auf dem neuen Server muss die 3 Replica-Ports **auf den Host mappen** (`ports: "3001:3000"` etc.) — aktuell macht das compose-File das nicht, weil es sich auf das gemeinsame Docker-Netzwerk verlässt. - Nachteil: alter NAS bleibt Single Point of Failure für den TLS-Terminierungspunkt. **Option B: Eigenständiges NGINX (oder Caddy/Traefik) direkt auf dem neuen Server.** - Voll unabhängig vom alten NAS, eigenes Let's-Encrypt-Zertifikat für `pawfeed.org` - Erfordert: Router-Portweiterleitung 80/443 von der alten NAS-LAN-IP auf die neue Server-LAN-IP umstellen - Mehr Aufwand (neues Zertifikat, neue Firewall-Regeln), aber sauberer Schnitt — keine Restabhängigkeit vom NAS mehr für PawFeed. Empfehlung: mit **Option A** starten (kleinerer Blast-Radius, reversibel), **Option B** als Folgeschritt, sobald der neue Server sich bewährt hat. ### Checkliste nach Bereich **DNS / Domain** - [ ] Nichts zu tun, *falls* Option A gewählt wird — `pawfeed.org` zeigt weiterhin auf dieselbe DDNS-Adresse/denselben Router. - [ ] Bei Option B: Router-Portforwarding 80/443 auf neue Server-IP umstellen, neues Zertifikat ausstellen (Certbot/acme.sh), Cloudflare-Einträge bleiben unverändert (Domain, nicht Server, wird aufgelöst). **Secrets / `.env`** - [ ] `docker/.env` (15 Variablen: Supabase, Clerk, Stripe, Mux, Sentry/GlitchTip, SMTP, `ADMIN_SECRET`, `ADMIN_OWNER_ID`, `CRON_SECRET`, `REDIS_PASSWORD`) sicher auf den neuen Server übertragen — **nicht per Git/FTP**, sondern `scp` über SSH oder Passwort-Manager. Werte bleiben identisch (App-Ebene, nicht Server-Ebene) — kein Rotieren nötig, außer man will es bewusst. - [ ] Nach Transfer: temporäre Kopien löschen, `chmod 600 docker/.env` auf dem neuen Server. **Redis-Daten** - [ ] Rate-Limits, Wartungsmodus-Flag, DAU/MAU-Sets, Stats-Cache: alles TTL-basiert/ephemer — **unkritisch, darf leer starten.** - [ ] Feed-Sorted-Sets (`src/lib/feed-helpers.ts`) sind der einzige Redis-Inhalt mit echtem UX-Wert — es gibt **keinen Backfill-Mechanismus**, der sie aus Postgres neu aufbaut (Feed wird nur beim `fanOutPost` bei neuen Posts befüllt). Bei einem Kaltstart auf leerem Redis sehen alle Nutzer beim Cutover kurzzeitig leere Feeds, bis neue Posts reinkommen. - Empfehlung: **Docker-Volume `redis_data` klonen** statt neu zu starten — sauberster Weg: ```bash # auf dem alten NAS: Redis kurz stoppen (Konsistenz), Volume sichern docker stop pawfeed-redis docker run --rm -v pawfeed_redis_data:/from -v /tmp:/to alpine \ tar czf /to/redis-data.tar.gz -C /from . scp /tmp/redis-data.tar.gz <neuer-server>:/tmp/ # auf dem neuen Server: Volume vorab anlegen, entpacken docker volume create pawfeed_redis_data docker run --rm -v pawfeed_redis_data:/to -v /tmp:/from alpine \ tar xzf /from/redis-data.tar.gz -C /to ``` - Downtime-Fenster dafür ist klein (Redis-Stop nur für den `tar`-Export), passt gut ins Cutover-Wartungsfenster. **Datenbank / Storage / Video / Auth / Zahlungen** - [ ] Kein Handlungsbedarf — alles extern (Supabase, Mux, Clerk, Stripe). Läuft vom neuen Server aus identisch, solange die ENV-Werte stimmen und der neue Server Internetzugang hat. - [ ] Supabase-Connection-Pool-Limit im Auge behalten: aktuell `DB_POOL_MAX=10` × 3 Replicas = 30 Verbindungen, weit unter dem Supabase-Limit (200). Bleibt unverändert, unabhängig vom Server. **Webhooks (Mux, Stripe, Clerk)** - [ ] Zeigen alle auf `https://pawfeed.org/api/webhooks/...` — **domain-basiert, nicht server-basiert.** Bei Option A (Domain bleibt gleich) ist hier nichts zu tun. Bei Option B ebenfalls nichts zu tun, solange die Domain gleich bleibt (nur das Zertifikat/der Terminierungspunkt wechselt). **Monitoring (GlitchTip)** - [ ] GlitchTip läuft selbst-gehostet auf dem *alten* NAS (`glitchtip-web`, `-worker`, `-postgres`, `-redis`), komplett unabhängig vom PawFeed-Compose-Stack, erreichbar über `glitchtip.pawfeed.org`. - [ ] Entscheidung nötig: GlitchTip auf dem alten NAS belassen (PawFeed auf dem neuen Server sendet Events einfach per HTTPS über die öffentliche Domain dorthin — funktioniert ohne Änderung) **oder** ebenfalls auf den neuen Server umziehen. Für den ersten Schritt: **auf dem alten NAS belassen**, spätere Konsolidierung ist ein separates Thema. **Cron-Jobs** - [ ] 3 Einträge in `daniel`s Crontab auf dem alten NAS: `trim-feeds` (4 Uhr), `anniversaries` (8 Uhr), `backup-db.sh` (3 Uhr). Müssen auf dem neuen Server neu angelegt werden (`crontab -e`, Skripte aus `docker/` mitnehmen). - [ ] **Akuter Fund, unabhängig vom Umzug:** `run-cron.sh` ruft aktuell `http://localhost:4563` — diesen Host-Port gibt es seit der 3-Replica-Umstellung (#26, 2026-08-16) nicht mehr (kein Port-Mapping mehr im compose-File). Cron-Log zeigt seit 2026-08-18 durchgehend `HTTP 000` — **`trim-feeds` und `anniversaries` laufen seit 4 Tagen ins Leere.** Sollte so oder so (auch ohne Umzug) auf `https://pawfeed.org/api/cron/...` umgestellt werden. Beim Server-Umzug gleich mit erledigen. - [ ] `backup-db.sh` (täglicher `pg_dump` der Supabase-DB, 14 Tage Retention) läuft unabhängig vom App-Server — kann 1:1 auf den neuen Server umziehen, sobald das `DIRECT_URL`-Secret dort liegt. **Hardware-Dimensionierung** - [ ] Aktueller Engpass war reine CPU-Kontention mit ~45 fremden Containern auf 4 Kernen (siehe #26-Lasttest: harte Decke bei ~60–65 Req/Sek). Ein eigenständiger Server sollte **mindestens 4, besser 6–8 Kerne exklusiv für PawFeed** bereitstellen (Puffer für eine 4. oder 5. Replica, wenn's weiter wächst). - [ ] RAM: 3 Node-Prozesse + Redis + Docker-Build-Overhead — mit 8 GB dediziert komfortabel, 16 GB falls später mehr Replicas dazukommen. - [ ] Disk: reichlich Marge einplanen (alter NAS steht bei 86 % voll — das war nie PawFeed selbst, PawFeed-Verzeichnis ist nur ~1,5 GB, aber es zeigt, wie eng geteilte Hardware werden kann). - [ ] Docker ≥ 29.x / Compose ≥ v5.x auf dem neuen Server installieren (aktuelle Versionen auf dem NAS, zum Vergleich). **Deploy-Workflow** - [ ] `docker/rolling-deploy.sh`, `start.sh`, `docker-compose.yml` unverändert vom Repo übernehmen (ggf. `ports:`-Mapping ergänzen, siehe Option A oben). - [ ] SSH-Key-Zugriff auf Gitea (`ssh://git@192.168.1.222:2222/admin/petfeed.git`) für den neuen Server einrichten, falls Deploys künftig per `git pull` statt `scp` laufen sollen.
Author
Owner

Migrationsplan (Phasen)

Voraussetzung für alle Phasen: neuer Server hat Linux, Docker, NGINX/Docker Compose installiert (wie in der Frage genannt) und ist im selben LAN wie der alte NAS erreichbar.

Phase 0 — Vorbereitung (kein Risiko für Prod)

  • SSH-Zugriff auf neuen Server einrichten (Key-Auth, analog zu daniel@192.168.1.222)
  • Docker + Compose Version prüfen (Ziel: ≥ Docker 29.x / Compose v5.x, wie auf dem NAS)
  • Gitea-SSH-Key des neuen Servers zu admin/petfeed hinzufügen (falls künftig git pull statt scp-Deploy)
  • Repo klonen: git clone ssh://git@192.168.1.222:2222/admin/petfeed.git
  • Entscheidung treffen: Option A (NPM bleibt zentral, siehe Anleitung) vs. Option B (eigenes NGINX). Empfehlung: A zuerst.
  • docker-compose.yml für den neuen Server anpassen: ports:-Mapping für pawfeed-1/2/3 ergänzen (z. B. 3001:3000, 3002:3000, 3003:3000), damit NPM sie per LAN-IP erreichen kann.

Phase 1 — Paralleler Testbetrieb (kein Cutover, kein Risiko)

  • docker/.env sicher (scp, nicht Git) auf den neuen Server kopieren, chmod 600
  • redis_data-Volume vom alten NAS auf den neuen Server klonen (siehe Anleitung — Redis kurz stoppen für konsistenten Export)
  • ./start.sh auf dem neuen Server ausführen — baut Image, startet alle 3 Replicas + Redis
  • Smoke-Test ohne Domain/DNS-Änderung: direkt gegen http://<neue-server-ip>:3001/api/health prüfen, danach mit curl -H "Host: pawfeed.org" http://<neue-server-ip>:3001/ oder lokalem /etc/hosts-Eintrag auf einem Testrechner gegen die echte App testen
  • Prüfen: Login (Clerk), Feed laden, Post erstellen, Video-Upload (Mux), Admin-Panel (/p/[ADMIN_SECRET]/) — alles gegen die gleiche Supabase-Prod-DB wie der alte NAS, also mit Vorsicht testen (siehe Risiken unten)
  • GlitchTip-Events prüfen: kommen Fehler vom neuen Server testweise in glitchtip.pawfeed.org an?

Phase 2 — Reverse-Proxy-Umstellung vorbereiten

Bei Option A:

  • /data/nginx/custom/http_top.conf auf dem NPM-Container-Volume (altes NAS) anpassen: Upstream-Block von Docker-DNS-Namen (pawfeed-1:3000) auf LAN-IP:Port des neuen Servers (192.168.1.XXX:3001 usw.) umstellen
  • NPM-Config testen/reloaden (nginx -t im Container, dann reload), noch nicht den alten Stack stoppen — beide Stacks können parallel erreichbar bleiben, solange der Upstream-Block nicht live geschaltet ist

Bei Option B:

  • NGINX/Traefik/Caddy auf dem neuen Server aufsetzen, Let's-Encrypt-Zertifikat für pawfeed.org per DNS-01 oder HTTP-01-Challenge ausstellen (HTTP-01 erst nach dem eigentlichen Cutover möglich, da Port 80 vom Router noch auf den alten NAS zeigt — ggf. DNS-01 über Cloudflare-API nutzen, um das Henne-Ei-Problem zu vermeiden)

Phase 3 — Cutover (kurzes Wartungsfenster)

  • Wartungsmodus aktivieren (maintenance.ts / Redis-Flag MAINTENANCE_KEY), falls verfügbar, um Schreibkonflikte während des Fensters zu vermeiden
  • Alten Stack auf dem NAS stoppen: docker compose stop für pawfeed-1/2/3 (Redis dort ebenfalls stoppen, falls Option A mit geklontem Redis-Volume — sonst laufen zwei Redis-Instanzen mit unterschiedlichem Zustand gegen dieselbe DB)
  • Bei Option A: NPM-Upstream-Änderung aus Phase 2 live schalten (Reload)
  • Bei Option B: Router-Portforwarding 80/443 auf neue Server-IP umstellen
  • Cron-Jobs auf dem neuen Server einrichten (crontab -e): trim-feeds, anniversaries, backup-db.sh — dabei gleich den kaputten localhost:4563-Verweis in run-cron.sh auf https://pawfeed.org/api/cron/... korrigieren
  • Alte Cron-Einträge auf dem NAS deaktivieren/entfernen (Doppelausführung vermeiden)
  • Verifikation: curl https://pawfeed.org/api/health, Login-Flow, Post erstellen, Mux-Webhook (/api/webhooks/mux), Stripe-Webhook (/api/webhooks/stripe) — Stripe/Mux/Clerk-Dashboards zeigen weiterhin dieselbe Domain, sollten ohne Neukonfiguration funktionieren
  • Wartungsmodus deaktivieren

Phase 4 — Nachbereitung

  • Alten PawFeed-Stack auf dem NAS vollständig abbauen (Container, Volumes, /Dockers/PawFeed-Verzeichnis archivieren statt sofort löschen — für Rollback-Fenster von ein paar Tagen)
  • CLAUDE.md (Abschnitt „Deployment") und Memory (project_deployment.md) aktualisieren: neue Server-IP, ggf. neuer Deploy-Workflow
  • Monitoring/Uptime-Kuma (läuft ebenfalls auf dem alten NAS) um einen Check für den neuen Server ergänzen
  • Rollback-Plan dokumentieren: solange der alte Stack archiviert ist, reicht ein Zurück-Umstellen des NPM-Upstreams (Option A) bzw. der Portweiterleitung (Option B), um binnen Minuten zurückzuwechseln

Risiken / offene Punkte

  • Doppelbetrieb gegen dieselbe Supabase-Prod-DB: Phase 1 testet den neuen Server gegen echte Prod-Daten (LIVE-Stripe!). Schreibende Aktionen (Testposts, Testzahlungen) während des Parallelbetriebs vermeiden oder klar als Test kennzeichnen — es gibt keine separate Staging-DB.
  • Redis-Split-Brain: Falls beide Redis-Instanzen (alt + geklont) gleichzeitig aktiv sind, driften Feed-Caches auseinander. Alten Redis-Container vor dem Cutover zuverlässig stoppen.
  • DNS-Cutover ist eigentlich kein DNS-Cutover: Da pawfeed.org über Cloudflare auf eine DDNS-Adresse zeigt, die auf den Router (nicht auf einen bestimmten internen Server) zeigt, ändert sich bei Option A keine öffentliche Adresse — das reduziert Cutover-Risiko erheblich gegenüber einem klassischen Server-Umzug mit DNS-Propagation.
  • run-cron.sh-Bug ist schon jetzt aktiv (nicht erst nach dem Umzug) — sollte unabhängig vom Zeitplan dieses Issues zeitnah gefixt werden, damit trim-feeds (Redis-Feed-Trimming, 90-Tage-Fenster) nicht unbegrenzt weiter ausbleibt.
## Migrationsplan (Phasen) Voraussetzung für alle Phasen: neuer Server hat Linux, Docker, NGINX/Docker Compose installiert (wie in der Frage genannt) und ist im selben LAN wie der alte NAS erreichbar. ### Phase 0 — Vorbereitung (kein Risiko für Prod) - [ ] SSH-Zugriff auf neuen Server einrichten (Key-Auth, analog zu `daniel@192.168.1.222`) - [ ] Docker + Compose Version prüfen (Ziel: ≥ Docker 29.x / Compose v5.x, wie auf dem NAS) - [ ] Gitea-SSH-Key des neuen Servers zu `admin/petfeed` hinzufügen (falls künftig `git pull` statt `scp`-Deploy) - [ ] Repo klonen: `git clone ssh://git@192.168.1.222:2222/admin/petfeed.git` - [ ] Entscheidung treffen: **Option A** (NPM bleibt zentral, siehe Anleitung) vs. **Option B** (eigenes NGINX). Empfehlung: A zuerst. - [ ] `docker-compose.yml` für den neuen Server anpassen: `ports:`-Mapping für `pawfeed-1/2/3` ergänzen (z. B. `3001:3000`, `3002:3000`, `3003:3000`), damit NPM sie per LAN-IP erreichen kann. ### Phase 1 — Paralleler Testbetrieb (kein Cutover, kein Risiko) - [ ] `docker/.env` sicher (scp, nicht Git) auf den neuen Server kopieren, `chmod 600` - [ ] `redis_data`-Volume vom alten NAS auf den neuen Server klonen (siehe Anleitung — Redis kurz stoppen für konsistenten Export) - [ ] `./start.sh` auf dem neuen Server ausführen — baut Image, startet alle 3 Replicas + Redis - [ ] Smoke-Test **ohne** Domain/DNS-Änderung: direkt gegen `http://<neue-server-ip>:3001/api/health` prüfen, danach mit `curl -H "Host: pawfeed.org" http://<neue-server-ip>:3001/` oder lokalem `/etc/hosts`-Eintrag auf einem Testrechner gegen die echte App testen - [ ] Prüfen: Login (Clerk), Feed laden, Post erstellen, Video-Upload (Mux), Admin-Panel (`/p/[ADMIN_SECRET]/`) — alles gegen die **gleiche** Supabase-Prod-DB wie der alte NAS, also mit Vorsicht testen (siehe Risiken unten) - [ ] GlitchTip-Events prüfen: kommen Fehler vom neuen Server testweise in `glitchtip.pawfeed.org` an? ### Phase 2 — Reverse-Proxy-Umstellung vorbereiten **Bei Option A:** - [ ] `/data/nginx/custom/http_top.conf` auf dem NPM-Container-Volume (altes NAS) anpassen: Upstream-Block von Docker-DNS-Namen (`pawfeed-1:3000`) auf LAN-IP:Port des neuen Servers (`192.168.1.XXX:3001` usw.) umstellen - [ ] NPM-Config testen/reloaden (`nginx -t` im Container, dann reload), **noch nicht** den alten Stack stoppen — beide Stacks können parallel erreichbar bleiben, solange der Upstream-Block nicht live geschaltet ist **Bei Option B:** - [ ] NGINX/Traefik/Caddy auf dem neuen Server aufsetzen, Let's-Encrypt-Zertifikat für `pawfeed.org` per DNS-01 oder HTTP-01-Challenge ausstellen (HTTP-01 erst nach dem eigentlichen Cutover möglich, da Port 80 vom Router noch auf den alten NAS zeigt — ggf. DNS-01 über Cloudflare-API nutzen, um das Henne-Ei-Problem zu vermeiden) ### Phase 3 — Cutover (kurzes Wartungsfenster) - [ ] Wartungsmodus aktivieren (`maintenance.ts` / Redis-Flag `MAINTENANCE_KEY`), falls verfügbar, um Schreibkonflikte während des Fensters zu vermeiden - [ ] Alten Stack auf dem NAS stoppen: `docker compose stop` für `pawfeed-1/2/3` (Redis dort ebenfalls stoppen, falls Option A mit geklontem Redis-Volume — sonst laufen zwei Redis-Instanzen mit unterschiedlichem Zustand gegen dieselbe DB) - [ ] Bei Option A: NPM-Upstream-Änderung aus Phase 2 live schalten (Reload) - [ ] Bei Option B: Router-Portforwarding 80/443 auf neue Server-IP umstellen - [ ] Cron-Jobs auf dem neuen Server einrichten (`crontab -e`): `trim-feeds`, `anniversaries`, `backup-db.sh` — dabei gleich den kaputten `localhost:4563`-Verweis in `run-cron.sh` auf `https://pawfeed.org/api/cron/...` korrigieren - [ ] Alte Cron-Einträge auf dem NAS deaktivieren/entfernen (Doppelausführung vermeiden) - [ ] Verifikation: `curl https://pawfeed.org/api/health`, Login-Flow, Post erstellen, Mux-Webhook (`/api/webhooks/mux`), Stripe-Webhook (`/api/webhooks/stripe`) — Stripe/Mux/Clerk-Dashboards zeigen weiterhin dieselbe Domain, sollten ohne Neukonfiguration funktionieren - [ ] Wartungsmodus deaktivieren ### Phase 4 — Nachbereitung - [ ] Alten PawFeed-Stack auf dem NAS vollständig abbauen (Container, Volumes, `/Dockers/PawFeed`-Verzeichnis archivieren statt sofort löschen — für Rollback-Fenster von ein paar Tagen) - [ ] `CLAUDE.md` (Abschnitt „Deployment") und Memory (`project_deployment.md`) aktualisieren: neue Server-IP, ggf. neuer Deploy-Workflow - [ ] Monitoring/Uptime-Kuma (läuft ebenfalls auf dem alten NAS) um einen Check für den neuen Server ergänzen - [ ] Rollback-Plan dokumentieren: solange der alte Stack archiviert ist, reicht ein Zurück-Umstellen des NPM-Upstreams (Option A) bzw. der Portweiterleitung (Option B), um binnen Minuten zurückzuwechseln ### Risiken / offene Punkte - **Doppelbetrieb gegen dieselbe Supabase-Prod-DB:** Phase 1 testet den neuen Server gegen echte Prod-Daten (LIVE-Stripe!). Schreibende Aktionen (Testposts, Testzahlungen) während des Parallelbetriebs vermeiden oder klar als Test kennzeichnen — es gibt keine separate Staging-DB. - **Redis-Split-Brain:** Falls beide Redis-Instanzen (alt + geklont) gleichzeitig aktiv sind, driften Feed-Caches auseinander. Alten Redis-Container vor dem Cutover zuverlässig stoppen. - **DNS-Cutover ist eigentlich kein DNS-Cutover:** Da `pawfeed.org` über Cloudflare auf eine DDNS-Adresse zeigt, die auf den Router (nicht auf einen bestimmten internen Server) zeigt, ändert sich bei Option A **keine** öffentliche Adresse — das reduziert Cutover-Risiko erheblich gegenüber einem klassischen Server-Umzug mit DNS-Propagation. - **`run-cron.sh`-Bug ist schon jetzt aktiv** (nicht erst nach dem Umzug) — sollte unabhängig vom Zeitplan dieses Issues zeitnah gefixt werden, damit `trim-feeds` (Redis-Feed-Trimming, 90-Tage-Fenster) nicht unbegrenzt weiter ausbleibt.
Author
Owner

Cron-Fix erledigt (unabhängig vom Umzugsplan oben, aber hier dokumentiert): run-cron.sh zeigte auf localhost:4563, das seit der 3-Replica-Umstellung (#26) nicht mehr existiert — HTTP 000 seit 2026-08-18.

Behoben mit curl --resolve ... https://pawfeed.neodk.ipv64.de$1 statt localhost:4563. Dabei aufgefallen: das NPM-Origin-Zertifikat ist nur für pawfeed.neodk.ipv64.de gültig, nicht für pawfeed.org selbst (subjectAltName-Mismatch) — funktioniert live nur, weil Cloudflare TLS für pawfeed.org an der Edge terminiert und das Origin-Zertifikat nicht strikt prüft. Das Skript zielt deshalb bewusst auf den DDNS-Hostnamen, nicht auf pawfeed.org.

Beide Endpunkte manuell verifiziert: trim-feeds und anniversaries liefern jetzt HTTP 200. Backup der alten Version liegt als run-cron.sh.bak-20260822 auf dem NAS.

Cron-Fix erledigt (unabhängig vom Umzugsplan oben, aber hier dokumentiert): `run-cron.sh` zeigte auf `localhost:4563`, das seit der 3-Replica-Umstellung (#26) nicht mehr existiert — HTTP 000 seit 2026-08-18. Behoben mit `curl --resolve ... https://pawfeed.neodk.ipv64.de$1` statt `localhost:4563`. Dabei aufgefallen: das NPM-Origin-Zertifikat ist nur für `pawfeed.neodk.ipv64.de` gültig, nicht für `pawfeed.org` selbst (subjectAltName-Mismatch) — funktioniert live nur, weil Cloudflare TLS für `pawfeed.org` an der Edge terminiert und das Origin-Zertifikat nicht strikt prüft. Das Skript zielt deshalb bewusst auf den DDNS-Hostnamen, nicht auf `pawfeed.org`. Beide Endpunkte manuell verifiziert: `trim-feeds` und `anniversaries` liefern jetzt `HTTP 200`. Backup der alten Version liegt als `run-cron.sh.bak-20260822` auf dem NAS.
admin added the question label 2026-08-22 13:33:48 +02:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: admin/petfeed#32