# 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 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: ```python 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 ` 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 mana-monorepo-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: │ ├── 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 | ### Bekannte Limits / 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 (Colima-NAT-Routing-Quirk). 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. Als Workaround später möglich: Loki-HTTP-Push via Cloudflare-Tunnel. 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/mana-monorepo/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`, `news-ingester`, `mana-verdaccio` (npm-Registry, Migration auf GPU-Box ist späterer Schritt). **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-mana-monorepo.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: ```sh 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. ```sh ssh mana-gpu "Get-NetConnectionProfile; Get-Service sshd" ``` 2. CPU/RAM/Disk dokumentieren in `docs/WINDOWS_GPU_SERVER_SETUP.md`: ```sh ssh mana-gpu "Get-ComputerInfo | Select-Object CsTotalPhysicalMemory,CsNumberOfLogicalProcessors,WindowsVersion; Get-PSDrive C" ``` 3. Bestätigen, dass die AI-Scheduled-Tasks ungestört laufen: ```sh 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): ```powershell 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: ```ini [wsl2] memory=16GB processors=8 swap=4GB localhostForwarding=true ``` 3. WSL starten, User anlegen, dann: ```sh 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 <