managarten/docs/PLAN_OPTION_C.md
Till JS b1b9bbc269
Some checks are pending
CD Mac Mini / Detect Changes (push) Waiting to run
CD Mac Mini / Deploy (push) Blocked by required conditions
CI / Detect Changes (push) Waiting to run
CI / Validate (push) Waiting to run
CI / Build mana-search (push) Blocked by required conditions
CI / Build mana-sync (push) Blocked by required conditions
CI / Build mana-api-gateway (push) Blocked by required conditions
CI / Build mana-crawler (push) Blocked by required conditions
Docker Validate / Validate Dockerfiles (push) Waiting to run
Docker Validate / Build calendar-web (push) Blocked by required conditions
Docker Validate / Build quotes-web (push) Blocked by required conditions
Docker Validate / Build todo-backend (push) Blocked by required conditions
Docker Validate / Build todo-web (push) Blocked by required conditions
Docker Validate / Build mana-auth (push) Blocked by required conditions
Docker Validate / Build mana-sync (push) Blocked by required conditions
Docker Validate / Build mana-media (push) Blocked by required conditions
Mirror to Forgejo / Push to Forgejo (push) Waiting to run
chore: rename repo mana-monorepo → managarten
Phase-3-Rename des ehemaligen Multi-App-Monorepos zum eigenständigen
Produkt-Repo. Verein heißt mana e.V., Plattform-Domain bleibt mana.how,
apps/mana/ bleibt unverändert — nur der Repo-Container kriegt den
neuen Namen "managarten" (Garten der mana-Apps).

Geändert:
- package.json#name + #description
- README.md (Titel + erster Absatz)
- TROUBLESHOOTING.md
- alle Mac-Mini-Skripte (Pfade ~/projects/mana-monorepo → ~/projects/managarten)
- COMPOSE_PROJECT_NAME-default in scripts/mac-mini/status.sh
- .github/workflows/cd-macmini.yml + mirror-to-forgejo.yml
- apps/docs (astro.config.mjs + content)
- .claude/settings.local.json (Bash-Permission-Pfade)
- alle docs/*.md Pfad-Referenzen
- launchd plists, .env.macmini.example, infrastructure/

Forgejo-Repo + GitHub-Repo bereits via API umbenannt. Lokales
Verzeichnis-Rename + Mac-Mini-Cutover folgen separat.
2026-05-09 01:16:02 +02:00

27 KiB
Raw Permalink Blame History

Plan — Option C: Workload-Split Mac Mini ↔ Windows-GPU-Box

Stand: 2026-05-06 Ziel: Nicht-zeitkritische Hilfsdienste (Monitoring, Forgejo, Glitchtip, Umami) auf die Windows-GPU-Box (64 GB RAM, derzeit 95 % idle System-RAM) verlagern. Single-Point-of-Failure innerhalb des Standorts entfernen, ohne Geld auszugeben. Production-Hot-Path bleibt unverändert auf dem Mini.

Status

Phase Stand Anmerkung
Phase 0 — Vor-Setup Hardware bestätigt (Ryzen 9 5950X / 64 GB / RTX 3090 / 660 GB frei C:), in WINDOWS_GPU_SERVER_SETUP.md dokumentiert
Phase 1 — WSL2 + Docker War schon eingerichtet (Ubuntu 24.04, Docker 29.4.1, systemd). .wslconfig erweitert um memory=24GB, processors=12, swap=8GB, vmIdleTimeout=-1
Phase 2a — Grafana auf GPU-Box Container mana-mon-grafana läuft auf :8000, Cross-Box-Datasources testen erfolgreich (Prometheus/VM, Loki, Business Metrics). DB-Datasources schlagen fehl wegen Pre-existing-Mis-config (DBs heißen mana_admin, nicht mana; keine glitchtip DB)
Phase 2b — Umami + Forgejo Beide healthy auf GPU-Box. Glitchtip übersprungen — Mini-Glitchtip ist bereits im Broken-State (DB glitchtip existiert nicht in Postgres, läuft nur in Degraded-Mode), Migration würde Bug nicht heilen
Phase 2c — VM + Loki + Alerts Komplett auf GPU-Box. 11 Container neu (VM, Loki, Pushgateway, Blackbox, Vmalert, Alertmanager, Alert-notifier, GPU-eigenes Node-Exporter+Cadvisor+Promtail). VM scrapt 76 Targets, 69 UP / 7 DOWN (DOWN sind alle pre-existing wrong /metrics endpoints auf Mana-Services, nicht durch Migration). Konfig-Pfade: monitoring/{prometheus,loki,blackbox,alertmanager,alert-notifier}/. Bekannte Limits siehe unten.
Phase 2d — Glitchtip mit dediziertem DB-Stack 4 Container neu (mana-mon-glitchtip + worker + dedizierte glitchtip-postgres + glitchtip-redis). Mini-Postgres scheiterte bei logs.0001_initial-Partition-Creation mit OS-level "Permission denied" (macOS-Docker-Storage-Quirk auf externer SSD). Auf der GPU-Box mit Linux-ext4 saubere 333-Tabellen-Migration. Worker enqueuet UND finished Tasks → DB-Writes funktional (vorher hingen sie ewig). Public-Hostname glitchtip.mana.how → mana-gpu-server-Tunnel (config v23).
Phase 2e — Status-Page auf GPU-Box 2 Container neu (mana-mon-status-gen + mana-mon-status-nginx). Sparse /srv/mana/source mit mana-source-pull.timer (stündlich) hostet das generate-status-page.sh und mana-apps.ts. status-gen schreibt in das Docker-Volume status-output, das status-nginx auf :8090 ausliefert. Public-Hostname status.mana.how → mana-gpu-server-Tunnel (config v25). Bonus: behebt den Inode-Stale-Bind-Mount-Bug, der auf dem Mini bei jedem CD-git checkout -f die Status-Page kaputt machte. vm.mana.how (Phase-2c-Workaround für Mini→GPU-VM-Routing) wurde wieder aus dem Tunnel entfernt — VM ist nicht mehr public.
Phase 2f — drei weitere Hilfsdienste verlagert ⚠️ teilweise zurückgerollt (1) verdaccio (npm.mana.how, was im mana-platform-Repo): Volume tar-stream + Config-bundle in managarten (infrastructure/verdaccio/config.yaml) — am 2026-05-07 zurückgerollt: das Storage-Volume kam nie auf der GPU-Box an, der dortige Container war leer. DNS+Tunnel zurück auf Mini, Mini-Standalone-Compose-Project unter ~/projects/verdaccio/ bleibt Single-Source. (2) news-ingester (Bun-Background-Tick): Cross-LAN-DB zur Mini-Postgres. Cross-arch-Limit aufgedeckt — docker save|load zwischen Mini (arm64) und GPU-Box (x86_64) wirft exec format error, daher nativer Build mit GPU-Box-eigenem Dockerfile in infrastructure/news-ingester/ der @mana/shared-rss als file:-ref vendored. (3) mana-ai (AI Mission Runner): Cross-LAN für mana-api/mana-llm/mana-research, RSA-Key-Sync (MANA_AI_PRIVATE_KEY_PEM), mana-ai.mana.how zum GPU-Tunnel (config v28). Bonus: AI Mission Runner sitzt jetzt im selben docker-network wie gpu-llm/gpu-ollama — künftige direct-LLM-Pfade ohne Cloudflare-Round-Trip. Mini Container 44 → 43 (verdaccio bleibt Mini-side).
Phase 2g — mana-research auslagern Web-Research-Orchestrator mit 16+ Search-/LLM-Providern. Nativer Build via workspace-Dockerfile (sparse-checkout services/mana-research + packages/{shared-research,shared-types,shared-hono,shared-logger}). Cross-LAN zu mana-auth/mana-credits/mana-llm/mana-search/postgres/redis (alle auf 192.168.178.131); Redis-Auth via REDIS_PASSWORD aus Mini's .env.macmini übernommen. research.mana.how zum GPU-Tunnel umgebogen via Cloudflare-API (config v29). Beide PUBLIC_MANA_RESEARCH_URL-Vars in mana-app-web auf https-URL umgestellt — gleicher Cross-LAN-Bridge-Pattern wie mana-ai (Mini-Container können 192.168.178.11 nicht direkt erreichen, daher Tunnel-Roundtrip). Mini Container 42 → 41.
Phase 3 — Daten-Migration n/a Alle migrierten Apps lesen Mini-Postgres direkt — keine separate Datenmigration
Phase 4 — Cloudflare-Cutover API-Approach via cert.pem apiToken: PUT /accounts/.../cfd_tunnel/.../configurations für GPU-Tunnel, dann cloudflared tunnel route dns --overwrite-dns. Kein Dashboard-Klick nötig. 3 Hostnames live (grafana/git/stats)
Phase 5 — Mini-Compose aufräumen 3 Blöcke in cloudflared-config.yml auskommentiert (Backup angelegt), cloudflared neu geladen, Mini-Container mana-mon-grafana + mana-mon-umami gestoppt (nicht entfernt — Rollback bleibt möglich)

Cloudflare-API-Approach (für nachvollziehbares Re-Run / weitere Cutover)

~/.cloudflared/cert.pem ist ein Argo-Tunnel-Token mit eingebettetem accountID + apiToken. Damit ist Tunnel-Ingress-Management vollständig automatisierbar — kein Dashboard-Klick, kein separater Cloudflare-API-Token nötig:

import json, urllib.request, base64, subprocess
cert = json.loads(subprocess.check_output(
    "awk '/BEGIN ARGO/{f=1;next}/END ARGO/{f=0}f' ~/.cloudflared/cert.pem | tr -d '\\n' | base64 -d", shell=True))
TOKEN, ACCOUNT_ID = cert['apiToken'], cert['accountID']
TUNNEL_ID = '83454e8e-d7f5-4954-b2cb-0307c2dba7a6'  # mana-gpu-server
# GET → modify ingress → PUT

Workflow für weitere Migrationen (Phase 2c o. ä.):

  1. GET aktuelle Ingress-Liste
  2. Lokal mergen (neue Hostnames vor dem http_status:404-Catchall)
  3. PUT zurück → erhöht version
  4. cloudflared tunnel route dns --overwrite-dns <tunnel-id> <hostname> für jeden neuen Hostname
  5. Mini-cloudflared-config.yml entsprechend bereinigen + launchctl kickstart -k gui/501/com.cloudflare.cloudflared

Was läuft heute (2026-05-06) auf der GPU-Box

WSL2 (Ubuntu 24.04, 24 GB RAM-Limit, 12 vCPU, vmIdleTimeout=-1)
└── Docker (systemd-managed, auto-start)
    ├── photon (eclipse-temurin:21-jre, port 2322) — Geocoder-Backend für Mini-mana-geocoding (Pre-existing)
    ├── Phase 2a — Grafana
    │   └── mana-mon-grafana (grafana:10.4.1, :8000)
    │       ├── volume: mana-grafana-data
    │       └── datasources jetzt LOKAL (victoriametrics:9090, loki:3100, tempo:3200)
    ├── Phase 2b — Apps
    │   ├── mana-core-forgejo (forgejo:11, :3041) — DB → Mini-Postgres
    │   └── mana-mon-umami (umami:2.18.0, :8010) — DB → Mini-Postgres
    ├── Phase 2d — Glitchtip mit eigener DB-Insel
    │   ├── mana-mon-glitchtip (glitchtip:latest, :8020) — DB → glitchtip-postgres
    │   ├── mana-mon-glitchtip-worker — Celery + Beat
    │   ├── mana-mon-glitchtip-postgres (postgres:16-alpine) — eigene DB-Instanz
    │   │   └── volume: glitchtip-pg-data (333 Tabellen, alle Migrationen sauber)
    │   └── mana-mon-glitchtip-redis (redis:7-alpine) — eigene Cache+Queue
    ├── Phase 2e — Status-Page (eigener Stack)
    │   ├── mana-mon-status-gen (alpine:3.20) — Generator-Loop, queryt VM lokal
    │   ├── mana-mon-status-nginx (nginx:alpine, :8090) — serviert HTML+JSON
    │   └── volume: status-output (geteilt zwischen den beiden)
    └── /srv/mana/source/ — Sparse managarten-clone (scripts/ + packages/shared-branding/src/)
        └── systemd-timer: mana-source-pull.timer (stündlich `git pull --ff-only`)
    └── Phase 2c — Metrics-Stack
        ├── mana-mon-victoria (VM v1.99.0, :9090) — scrapt Mini-Services via 192.168.178.131:<port>
        │   ├── extra_hosts: host.docker.internal:host-gateway
        │   └── volume: victoriametrics-data
        ├── mana-mon-loki (loki:3.0.0, :3100) — empfängt von gpu-promtail (Mini-side blockiert via LAN)
        ├── mana-mon-gpu-promtail — sammelt GPU-Box-Container-Logs
        ├── mana-mon-pushgateway (:9091)
        ├── mana-mon-blackbox (:9115)
        ├── mana-mon-vmalert (:8880)
        ├── mana-mon-alertmanager (:9093)
        ├── mana-mon-alert-notifier (:9095) — Telegram-Bot, lokal gebaut
        ├── mana-mon-gpu-node-exporter — eigenes Host-Metric-Endpoint
        └── mana-mon-gpu-cadvisor — eigenes Container-Metric-Endpoint

Cloudflare-API-Approach + Tunnel-Routes

Hostname Tunnel Origin
grafana.mana.how mana-gpu-server http://localhost:8000
git.mana.how mana-gpu-server http://localhost:3041
stats.mana.how mana-gpu-server http://localhost:8010
glitchtip.mana.how mana-gpu-server http://localhost:8020
(interne) gpu-box-eigene VM/Loki nur LAN, via Mini-Promtail blockiert

Cross-LAN Routing zwischen Mini-Container und GPU-Box

Wiederkehrendes Pattern: Mac-Mini-Docker-Container (in der Colima-Linux-VM) können die Windows-GPU-Box zwar IP-technisch erreichen, aber Docker-Bridge + Colima-NAT routen nicht zur LAN-IP 192.168.178.11. Workaround = Cloudflare-Tunnel als interne-IP-Bridge:

Was Status Workaround
vm.mana.how (VictoriaMetrics) nicht mehr nötig War kurzzeitig aktiv für Mini-side status-gen; Phase 2e zog status-gen zur GPU-Box → vm.mana.how raus
photon.mana.how (Geocoder, GPU-WSL2 :2322) aktiv (config v26) mana-geocoding's PHOTON_SELF_API_URL auf https://photon.mana.how — closed das /health/photon-self-Probe-Loch; geocoding-Provider-Tier privacy:'local' ist erstmals seit Phase 2c real funktional
gpu-stt/gpu-llm/gpu-tts/gpu-img/gpu-video/gpu-ollama aktiv (vor Phase 2c) Direktes scraping von VM aus deaktiviert; blackbox-exporter probt nur die /health-Endpoints öffentlich

Restliche Pre-Existing Issues nach Phase 2c

  1. Mini-Container-Logs werden nicht zu GPU-Loki geshipped. Mini-Promtail kann GPU-Box 192.168.178.11:3100 aus Colima-Container-Network nicht erreichen, obwohl der Mini-Host es kann. Ports 3100/9090/9091 sind via Windows-Firewall + netsh interface portproxy von der LAN-IP erreichbar. Mini-Promtail war ohnehin schon vor der Migration aus, ist also keine Regression — aktuell sind nur GPU-Box-Logs in Loki. Workaround analog zu photon: Loki-HTTP-Push via Cloudflare-Tunnel-Hostname (loki.mana.how).
  2. gpu- direct scrape jobs deaktiviert.* Aus Docker-Container in WSL2 ist host.docker.internal:port (= host-gateway 172.18.0.1) nicht in der Lage, Windows-Host-Services zu erreichen (die binden auf 127.0.0.1). Workaround: blackbox-exporter probt gpu-stt.mana.how/health etc. öffentlich → grobe Up/Down-Visibility ist erhalten, nur App-interne Metriken (Token-Counts etc.) fehlen.
  3. 7 Pre-Existing-DOWN-Targets: mana-auth, mana-credits, mana-user, mana-subscriptions, mana-analytics, memoro-server, uload-server geben non-2xx auf /metrics zurück (entweder kein Endpoint oder Auth-protected). Waren auf dem Mini schon DOWN, nicht durch Migration verursacht.
  4. 2 Scrape-Jobs übersprungen: mana-mcp und mana-crawler exposen keine Host-Ports (nur Container-internal), daher von der GPU-Box nicht erreichbar. Auskommentiert in prometheus.yml.

Mac Mini nach Phase 2c

Mini hat nun 46 laufende Container (von vorher 53). Gestoppt: mana-mon-{victoria,loki,promtail,vmalert,alertmanager,blackbox,pushgateway,alert-notifier}. Im Compose drin gelassen für Rollback.

WSL-Keepalive: Scheduled Task MANA_WSL_Keepalive (Trigger: AtLogOn + AtStartup, Restart bei Failure) hält wsl.exe -d Ubuntu-24.04 --exec /bin/sleep infinity als langlebigen Windows-Prozess offen → WSL-VM idled nicht aus, Container überleben SSH-Session-Pausen.


0. Voraussetzungen

Mac Mini SSH ssh mana-server (192.168.178.131, User mana) — OK
GPU-Box SSH ssh mana-gpu (192.168.178.11, User tills) — derzeit offline
GPU-Box muss vor Phase 1 erreichbar sein Box einschalten, Network-Profile auf "Privat" setzen (Doku §1 in WINDOWS_GPU_SERVER_SETUP.md)
Live-Tunnel-Config Mac Mini /Users/mana/projects/managarten/cloudflared-config.yml (geladen via LaunchAgent)
Mac-Mini-Tunnel-UUID 1435166a-0e3f-4222-8de6-744f32cea5c9
GPU-Box-Tunnel-UUID 83454e8e-d7f5-4954-b2cb-0307c2dba7a6 (Token-managed im Cloudflare-Dashboard)

1. Service-Inventar — was wandert, was bleibt

Wandert auf GPU-Box (WSL2/Docker)

Container Host-Port (Mini) Image Daten-Volumen Anmerkung
grafana 8000 grafana/grafana:10.4.1 mana-grafana-data (46 MB) Dashboards + Provisioning aus Repo bind-mount
victoriametrics 9090 victoriametrics:v1.99.0 mana-victoria-data (988 MB) Scrapes über LAN auf Mini-Exporter umkonfigurieren
loki 3100 grafana/loki:3.0.0 mana-loki-data (4 MB) Promtail auf Mini schickt zu 192.168.178.11:3100
pushgateway 9091 prom/pushgateway:v1.7.0
blackbox-exporter 9115 prom/blackbox:v0.25.0 Probt von außen — sogar besser, wenn nicht auf der Mini-Box
vmalert 8880 victoriametrics/vmalert:v1.99.0 Alerts-Dir aus Repo bind-mount
alertmanager 9093 prom/alertmanager:v0.27.0 mana-alertmanager-data (8 KB)
alert-notifier 9095 alert-notifier:local Lokales Image — auf GPU-Box neu bauen
umami 8010 umami:postgresql-v2.18.0 (DB only) DB umami bleibt auf Mini-Postgres, App ruft via 192.168.178.131:5432
glitchtip + worker 8020 glitchtip:latest (DB only) DB-Name TBD beim Migrieren — wahrscheinlich auch in Mini-Postgres
forgejo (re-aktivieren) 3041 codeberg.org/forgejo/forgejo:11 (frisch oder Volume-Restore) Container läuft heute nicht; DB-Tabelle vorhanden

Bleibt auf Mac Mini

Production Hot-Path: postgres, postgres-backup, redis, minio, minio-init, mana-auth, mana-api, mana-app-web, mana-sync, mana-credits, mana-user, mana-subscriptions, mana-events, mana-geocoding, mana-analytics, mana-research, mana-ai, mana-media, mana-crawler, mana-notify, mana-search, mana-landing-builder, api-gateway, mana-llm (Proxy), mana-app-manavoxel-web, mana-app-uload-server, mana-app-llm-playground, mana-admin, mana-mail, mana-status-gen, mana-infra-landings (nginx), memoro-server, memoro-audio-server, memoro-landing, chorportal-app + chorportal-prod-postgres

  • chorportal-prod-minio, mana-verdaccio (npm-Registry, Standalone-Compose unter ~/projects/verdaccio/; Phase-2f-1-Migration zur GPU am 2026-05-07 zurückgerollt — Storage-Volume kam dort nie an).

Box-lokale Helpers (laufen auf jeder Box separat):

  • node-exporter (Host-Metriken)
  • cadvisor (lokale Docker-Metriken)
  • promtail (lokaler Log-Shipper)
  • watchtower (lokaler Docker-Updater)
  • postgres-exporter (bleibt nur auf Mini, weil Postgres dort)
  • redis-exporter (bleibt nur auf Mini)

Native Prozesse Mini (kein Docker, bleiben unverändert)

who-server (PM2), who-web (LaunchAgent), cloudflared (Mini-Tunnel), colima, GitHub-Runner, actions.runner.Memo-2023-managarten.mac-mini.

2. Cloudflare-Routing — Vorher / Nachher

Hostname Vorher Nachher Tunnel
grafana.mana.how Mini :8000 GPU-Box :8000 mana-gpu-server
git.mana.how Mini :3041 (heute 502, da Forgejo aus) GPU-Box :3041 mana-gpu-server
glitchtip.mana.how Mini :8020 GPU-Box :8020 mana-gpu-server
stats.mana.how (Umami) Mini :8010 GPU-Box :8010 mana-gpu-server
status.mana.how Mini :4400 (nginx) bleibt Mini (status-page-gen schreibt in landings dir) mana-server
Alle anderen *.mana.how Mini unverändert Mini mana-server

Hinzufügen im Cloudflare-Dashboard (Zero Trust → Networks → Tunnels → mana-gpu-server → Public Hostnames):

grafana.mana.how    → http://localhost:8000
git.mana.how        → http://localhost:3041
glitchtip.mana.how  → http://localhost:8020
stats.mana.how      → http://localhost:8010

DNS-CNAMEs zeigen heute auf den Mini-Tunnel und müssen umgeschwenkt werden:

ssh mana-server '/opt/homebrew/bin/cloudflared tunnel route dns \
  --overwrite-dns 83454e8e-d7f5-4954-b2cb-0307c2dba7a6 grafana.mana.how'
# … pro Hostname

3. Phasen

Phase 0 — Vor-Setup (10 min, kein Risiko)

  1. GPU-Box einschalten, Network-Profile auf "Privat" setzen, SSH testen.
    ssh mana-gpu "Get-NetConnectionProfile; Get-Service sshd"
    
  2. CPU/RAM/Disk dokumentieren in docs/WINDOWS_GPU_SERVER_SETUP.md:
    ssh mana-gpu "Get-ComputerInfo | Select-Object CsTotalPhysicalMemory,CsNumberOfLogicalProcessors,WindowsVersion; Get-PSDrive C"
    
  3. Bestätigen, dass die AI-Scheduled-Tasks ungestört laufen:
    ssh mana-gpu "Get-ScheduledTask -TaskName Mana* | Select-Object TaskName,State"
    curl -s https://gpu-llm.mana.how/health
    

Phase 1 — WSL2 + Docker auf GPU-Box (1 h)

  1. WSL2 + Ubuntu 24.04 installieren (falls nicht vorhanden):
    wsl --install -d Ubuntu-24.04 --no-launch
    wsl --set-default-version 2
    
  2. .wslconfig in C:\Users\tills\.wslconfig erstellen — WSL2 auf 16 GB RAM begrenzen, damit AI-Tasks ihren System-RAM behalten:
    [wsl2]
    memory=16GB
    processors=8
    swap=4GB
    localhostForwarding=true
    
  3. WSL starten, User anlegen, dann:
    sudo apt update && sudo apt install -y ca-certificates curl
    curl -fsSL https://get.docker.com | sudo sh
    sudo usermod -aG docker $USER
    # systemd in WSL aktivieren:
    sudo tee /etc/wsl.conf >/dev/null <<EOF
    [boot]
    systemd=true
    EOF
    
  4. WSL neu starten (wsl --shutdown, dann erneut starten), Docker testen:
    docker run --rm hello-world
    
  5. Auto-Start: Scheduled Task anlegen, der bei Boot wsl -d Ubuntu-24.04 --exec /bin/sh -c "true" ausführt → das hält die WSL-VM warm und damit Docker-Daemon am Leben.

Smoketest: ssh mana-gpu "wsl -d Ubuntu-24.04 -- docker ps" muss leere Container-Liste zurückgeben.

Phase 2 — Monitoring-Stack auf GPU-Box hochziehen (parallel zum Mini, 2 h)

  1. Im Repo docker-compose.gpu-box.yml anlegen — initial nur die Container, die keine Daten-Migration brauchen: Grafana, VictoriaMetrics, Loki, Pushgateway, Blackbox-Exporter, Vmalert, Alertmanager, Alert-Notifier. Plus GPU-Box-lokales node-exporter, cadvisor, promtail, watchtower.
  2. Configs aus docker/{prometheus,loki,blackbox,alertmanager,grafana}/ per git clone auf die GPU-Box → bind-mounten.
  3. Datenquellen für VictoriaMetrics neu konfigurieren: Scrape-Targets müssen 192.168.178.131:9100 (node-exporter Mini), 192.168.178.131:9110 (cadvisor Mini), 192.168.178.131:9187 (postgres-exporter), etc. enthalten. Plus die GPU-Box-lokalen Exporter.
  4. Grafana-Datenquellen auf neue VM-URL (http://victoria:8428 lokal in compose) zeigen lassen.
  5. docker compose -f docker-compose.gpu-box.yml up -d auf GPU-Box.
  6. Parallel-Verifikation: GPU-Box-Grafana ist intern erreichbar (curl http://192.168.178.11:8000/api/health), aber noch nicht öffentlich. Mini-Grafana läuft weiter — kein Cutover.

Phase 3 — Daten-Migration (3 h)

Glitchtip + Umami

Beide haben keine persistenten Container-Volumes — der Zustand liegt komplett in mana-infra-postgres. Einfachster Weg: App-Container auf GPU-Box hochziehen, mit DB-Connection-String auf 192.168.178.131:5432 zeigen lassen. Daten bleiben auf Mini-Postgres, nur die App-Layer wandert.

Voraussetzung: Mini-Postgres muss von der GPU-Box (LAN) erreichbar sein. pg_hba.conf checken:

ssh mana-server 'PATH=/Applications/Docker.app/Contents/Resources/bin:$PATH; docker exec mana-infra-postgres cat /var/lib/postgresql/data/pg_hba.conf | grep -v "^#" | grep -v "^$"'

Falls nötig: Eintrag für 192.168.178.0/24 ergänzen, Postgres-Reload.

DB-Namen + Credentials aus docker-compose.macmini.yml extrahieren (Umami: umami DB, Glitchtip-DB-Name TBD beim Migrieren).

Forgejo

Container läuft heute nicht (Live-Check 2026-05-06). DB-Tabelle existiert aber. Zwei Optionen:

  • A — Frisch starten: Forgejo-Container auf GPU-Box hochziehen, DB neu initialisieren, alle Repos frisch von GitHub mirrorn (Mirror-Sync ist konfiguriert in .github/workflows/mirror-to-forgejo.yml).
  • B — DB übernehmen: Forgejo-DB aus Mini-Postgres dump'en und auf GPU-Box-internem Postgres restoren. Komplizierter, kein Mehrwert für einen Mirror-only Forgejo.

Empfehlung: Option A. Saubere Neuinstallation auf GPU-Box.

Grafana / VM / Loki / Alertmanager

  • VM-Daten (988 MB): per docker run --rm -v mana-victoria-data:/d -v $PWD:/o alpine tar czf /o/vm.tgz -C /d . auf Mini, rsync zur GPU-Box, dort in Volume reinpacken. Alternativ: einfach neu starten und 14-Tage-Retention füllt sich neu — gegen den 988 MB-Verlust spricht nichts Geschäftliches.
  • Loki-Daten (4 MB): irrelevant, Logs füllen sich neu.
  • Grafana-Daten (46 MB): Dashboards sind im Repo (provisioning), Datenquellen konfigurieren wir neu. Nur User-Settings/Stars wären verloren — verkraftbar.
  • Alertmanager-Daten (8 KB): leer.

Empfehlung: Daten-Migration weglassen. Container starten frisch, alles relevante kommt aus dem Repo (Dashboards, Alert-Rules, Configs). Ein sauberer Re-Start ist hier billiger als jeder rsync-Fehler.

Phase 4 — Cloudflare-Tunnel-Cutover (30 min)

  1. Im Cloudflare-Dashboard (Zero Trust → Tunnels → mana-gpu-server → Public Hostname → Add):
    • grafana.mana.howhttp://localhost:8000
    • git.mana.howhttp://localhost:3041
    • glitchtip.mana.howhttp://localhost:8020
    • stats.mana.howhttp://localhost:8010
  2. DNS umrouten (atomar, < 60 s Propagation in eigener Zone):
    ssh mana-server '
      for h in grafana git glitchtip stats; do
        /opt/homebrew/bin/cloudflared tunnel route dns \
          --overwrite-dns 83454e8e-d7f5-4954-b2cb-0307c2dba7a6 ${h}.mana.how
      done
    '
    
  3. Smoketest pro Hostname:
    for h in grafana git glitchtip stats; do
      curl -sI https://${h}.mana.how/ | head -2
      echo
    done
    
  4. Mini-Tunnel-Einträge entfernen (im Repo cloudflared-config.yml die vier Hostnames auskommentieren), dann launchctl kickstart -k gui/501/com.cloudflare.cloudflared.

Phase 5 — Aufräumen + Verifikation (1 h)

  1. Mini-Container stoppen + auskommentieren (nicht löschen, für schnelles Rollback) in docker-compose.macmini.yml:
    grafana, victoriametrics, loki, tempo, pushgateway, blackbox-exporter,
    vmalert, alertmanager, alert-notifier, umami, glitchtip, glitchtip-worker
    
  2. Sum-of-Limits neu berechnen (./scripts/mac-mini/memory-baseline.sh). Ziel: ≤ 6 GiB.
  3. Mini-Reboot-Test: Box neu starten, alle laufenden Container kommen wieder, pnpm check:status alle grün.
  4. Cross-Box-Health-Check: GPU-Box-Reboot → Mini-Production läuft weiter, mana.how antwortet, nur Monitoring kurz blind.
  5. Build-Test: ./scripts/mac-mini/build-app.sh todo-web ohne --force-free muss durchlaufen, ohne Monitoring zu stoppen.

4. Rollback-Plan

Bei jedem Phasen-Fehler: keine Mini-Container wurden vorher gestoppt → Rollback ist trivial.

Phase Rollback
Phase 1 (WSL2 broken) WSL2 deinstallieren, GPU-Box bleibt AI-only
Phase 2 (Monitoring auf GPU-Box läuft nicht) GPU-Box-Compose down, Mini bleibt SoT — kein Impact
Phase 3 (Glitchtip/Umami können Mini-Postgres nicht erreichen) pg_hba.conf zurück, GPU-Box-Container down, Mini-Container weiter — kein Impact
Phase 4 (Cutover) DNS-Route zurück auf Mini-Tunnel + Mini-Container up -d (waren ja nur gestoppt)
Phase 5 (Mini bricht beim Aufräumen) git checkout docker-compose.macmini.yml && docker compose up -d — Container kommen zurück

5. Risiken + Mitigation

Risiko Mitigation
WSL2-Networking nach Patch-Tuesday weg Healthcheck auf Mini probt GPU-Box-Endpoints; manuelles Eingreifen am nächsten Werktag
AI-Tasks werden ge-OOM'd .wslconfig begrenzt WSL auf 16 GB → AI-Tasks behalten 48 GB
Glitchtip/Umami können Mini-Postgres-IP nicht auflösen Vorab-Test in Phase 3: psql -h 192.168.178.131 -U postgres -p 5432 -c '\l' aus WSL2
Mini-Tunnel führt nach Cutover noch alte Routen, weil DNS-Cache TTL der Cloudflare-Records ist 60 s; dig prüfen vorm Verifizieren
Status-Page-Gen findet VM nicht mehr URL in scripts/generate-status-page.sh von localhost:9090 auf 192.168.178.11:9090 ändern

6. Was der Plan nicht leistet

  • Keine geografische Redundanz. Hausbrand / längerer Stromausfall / FRITZ!Box-Tod trifft beide Boxen weiterhin. Dafür ist Hetzner (§5 im Hauptbericht) nötig.
  • Kein Postgres-Failover. Mini-Postgres bleibt SPOF. Logical-Replication zu Hetzner-CX22 ist ein separates Projekt (Option A im Hauptbericht).
  • Kein Verdaccio-Move. npm.mana.how bleibt Mini, weil Build-Pipeline davon abhängt — eigener Migrationsschritt später.
  • Kein Stalwart/Mail-Move. Mail-DNS hängt am Mini-Tunnel-IP — gesonderter Schritt.

7. Offene Punkte (zu klären während der Migration)

  • Glitchtip-DB-Name + Credentials aus docker-compose.macmini.yml extrahieren
  • Forgejo: re-aktivieren (frisch) oder ganz streichen, falls niemand wirklich auf git.mana.how schaut (GitHub ist eh SoT)
  • docker/grafana/, docker/loki/ (im aktuellen Repo unter manacore-monorepo-Pfad gemountet — Pfad-Konsistenz prüfen)
  • CPU/RAM-Specs der GPU-Box dokumentieren in docs/WINDOWS_GPU_SERVER_SETUP.md
  • scripts/check-status.sh und scripts/generate-status-page.sh auf neue VM-URL umstellen (192.168.178.11:9090)

8. Zeit-Schätzung

Phase Dauer Risiko
Phase 0 (Pre-Setup) 10 min Niedrig
Phase 1 (WSL2/Docker) 1 h Niedrig
Phase 2 (Monitoring auf GPU-Box) 2 h Niedrig (parallel)
Phase 3 (Daten-Migration) 3 h Mittel (DB-Konnektivität)
Phase 4 (Cutover) 30 min Mittel (DNS-Propagation)
Phase 5 (Aufräumen) 1 h Niedrig
Gesamt ~7,5 h über 12 Tage realistisch verteilt