managarten/docs/MAC_MINI_SERVER.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

832 lines
31 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Mac Mini Server Setup
Dokumentation des Mac Mini als Self-Hosted Server für Mana Apps.
## Übersicht
Der Mac Mini dient als Self-Hosted Server fuer alle Mana-Anwendungen. Er ist ueber Cloudflare Tunnel oeffentlich erreichbar und fuehrt automatische Health Checks mit Benachrichtigungen durch.
### Container Runtime: Colima (MIT-Lizenz)
Statt Docker Desktop nutzen wir **Colima** als Container-Runtime. Colima ist Open Source (MIT), Docker-CLI-kompatibel und verbraucht ~10 GB weniger RAM als Docker Desktop.
| | Docker Desktop (vorher) | Colima (jetzt) |
|--|------------------------|----------------|
| VM-Overhead | ~12.5 GB | ~0.3-0.5 GB |
| Lizenz | Proprietaer | MIT (Open Source) |
| docker-compose | Identisch | Identisch |
**Konfiguration:** 8 CPUs, 12 GB RAM, 200 GB Disk, Apple VZ, VirtioFS
**LaunchAgent:** `~/Library/LaunchAgents/com.mana.colima.plist`
**Migration:** `./scripts/mac-mini/migrate-to-colima.sh`
**Rollback:** `./scripts/mac-mini/migrate-to-colima.sh --rollback`
### Architektur
```
Internet
Cloudflare Tunnel (cloudflared)
┌─────────────────────────────────────────────────────────────┐
│ Mac Mini M4 (mana-server) │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ PostgreSQL │ │ Redis │ │
│ │ (Docker) │ │ (Docker) │ │
│ └─────────────────┘ └─────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Docker Container (~61 Services) │ │
│ │ ├── mana-auth (Port 3001) │ │
│ │ ├── dashboard-web (Port 5173) │ │
│ │ ├── chat-web (Port 3000) │ │
│ │ ├── todo-web (Port 5188) │ │
│ │ ├── calendar-web (Port 5186) │ │
│ │ ├── clock-web (Port 5187) │ │
│ │ ├── mana-sync (Go) (Port 3050) │ │
│ │ ├── mana-llm (Port 3020) │ │
│ │ └── ... (19 web apps, core services, monitoring) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ │ LAN (192.168.178.11) │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ GPU Server (Windows, RTX 3090, 24 GB VRAM) │ │
│ │ ├── Ollama (Port 11434) - gemma3:12b │ │
│ │ ├── STT (Whisper) (Port 3020) │ │
│ │ ├── TTS (Port 3022) │ │
│ │ ├── Image Gen (FLUX) (Port 3023) │ │
│ │ └── cloudflared (Windows Service, Tunnel │ │
│ │ mana-gpu-server) │ │
│ │ → gpu-*.mana.how │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ LaunchAgents (Autostart, Mac Mini) │ │
│ │ ├── cloudflared (Tunnel mana-server, │ │
│ │ │ → all .mana.how except │ │
│ │ │ gpu-*) │ │
│ │ ├── docker-startup (Container beim Boot) │ │
│ │ └── health-check (alle 5 Minuten) │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
```
> **Two-tunnel setup**: There are two Cloudflare tunnels in this stack.
> The **mana-server** tunnel runs on the Mac Mini (LaunchAgent, locally-managed
> via `~/.cloudflared/config.yml`) and handles every `*.mana.how` route except
> the GPU ones. The **mana-gpu-server** tunnel runs on the Windows GPU box as
> a Windows Service (token-managed via the Cloudflare dashboard, NOT a local
> config.yml) and handles `gpu-stt`, `gpu-llm`, `gpu-tts`, `gpu-img`,
> `gpu-ollama`. See the "GPU Tunnel" section below for adding new hostnames.
### Öffentliche URLs
| Service | URL |
|---------|-----|
| Dashboard | https://mana.how |
| Auth API | https://auth.mana.how |
| Chat | https://chat.mana.how |
| Todo | https://todo.mana.how |
| Calendar | https://calendar.mana.how |
| Clock | https://clock.mana.how |
## SSH-Zugang
### Verbindung
```bash
ssh mana-server
```
SSH-Config (`~/.ssh/config`):
```
# Lokales Netzwerk (direkt)
Host mana-server
HostName 192.168.178.131
User mana
# Über Cloudflare Tunnel (von extern)
Host mana-server-remote
HostName mac-mini.mana.how
User mana
ProxyCommand /opt/homebrew/bin/cloudflared access ssh --hostname %h
```
### Projekt-Verzeichnis
```bash
cd ~/projects/managarten
```
## CI/CD
Ein GitHub Actions Self-Hosted Runner läuft nativ auf dem Mac Mini und deployt automatisch bei Push auf `main`.
- **CD Workflow:** `.github/workflows/cd-macmini.yml`
- **Mirror Workflow:** `.github/workflows/mirror-to-forgejo.yml` (GitHub → Forgejo Sync)
- **Runner:** `mac-mini` (self-hosted, macOS, ARM64, LaunchAgent)
- **Manuelles Deployment:** https://github.com/Memo-2023/managarten/actions/workflows/cd-macmini.yml
### Forgejo (Mirror-Only)
Forgejo v11 läuft als Push-Mirror von GitHub — kein CI/CD, nur Backup und Sichtbarkeit.
- **URL:** https://git.mana.how (Port 3041)
- **SSH:** Port 2222
- **Sync:** Automatisch bei jedem Push auf `main` via GitHub Actions
- **Kein Runner:** Forgejo Runner hat kein macOS-Binary, Docker-Runner kann nicht auf Host zugreifen
```
lokal → git push → GitHub → CD (nativer Runner) → Docker deploy
→ Mirror → Forgejo (Backup)
```
## Wichtige Befehle
### Status & Monitoring
```bash
# HTTP-Erreichbarkeit aller mana.how Domains prüfen (liest aus cloudflared-config.yml)
pnpm check:status
# oder direkt:
bash scripts/check-status.sh
# Übersicht aller Services
./scripts/mac-mini/status.sh
# Health Check manuell ausführen
./scripts/mac-mini/health-check.sh
# Docker Container Status
docker ps
# Logs eines Containers
docker logs mana-chat-backend
docker logs -f mana-chat-backend # Live-Logs
```
**Grafana Uptime-Dashboard:** `grafana.mana.how` → Ordner "Mana" → **"Mana Uptime"**
Zeigt probe_success und probe_duration_seconds aller Dienste via Blackbox Exporter (Port 9115).
Alerts: WebAppDown (2 min), APIDown (1 min), InfraToolDown (3 min), GPUServiceDown (5 min), SlowHTTPResponse (5 min > 5s).
### Public Status Page (status.mana.how)
> **Stand 2026-05-07** — Status-Page-Generator + dedizierter Nginx wurden auf die GPU-Box umgezogen (Phase 2e, siehe [`PLAN_OPTION_C.md`](./PLAN_OPTION_C.md)). Die Container `mana-mon-status-gen` + `mana-mon-status-nginx` leben jetzt in [`infrastructure/docker-compose.gpu-box.yml`](../infrastructure/docker-compose.gpu-box.yml), DNS routet `status.mana.how` auf den `mana-gpu-server`-Tunnel.
| Komponente | Pfad |
|---|---|
| Generator-Script | `scripts/generate-status-page.sh` (vom GPU-Box-Sparse-Repo `/srv/mana/source/` bind-gemountet) |
| Docker-Services | `status-page-gen` + `status-nginx` in `infrastructure/docker-compose.gpu-box.yml` |
| Output | docker-volume `status-output` (geteilt zwischen den beiden Containern) |
| Public-Tunnel | mana-gpu-server, Port `:8090` |
**Datenquellen:**
- **Service-Uptime:** VictoriaMetrics via Blackbox Exporter (`probe_success`, `probe_duration_seconds`) — VM läuft jetzt im selben docker-network auf der GPU-Box, kein Cloudflare-Round-Trip mehr.
- **App Release Tiers:** Automatisch aus `packages/shared-branding/src/mana-apps.ts` geparst.
**Automatische Aktualisierung:** Der GPU-Box-systemd-timer `mana-source-pull.timer` macht stündlich `git pull` auf `/srv/mana/source/`. Änderungen an `requiredTier` in `mana-apps.ts` schlagen nach maximal 1 h Pull-Latenz + 60 s Status-Tick auf der Status-Seite durch.
**`status.json`** wird parallel generiert und enthält Service-Status + Tier-Daten als JSON.
### Service Management
```bash
# Alle Container neustarten
./scripts/mac-mini/restart.sh
# Alle Container stoppen
./scripts/mac-mini/stop.sh
# Einzelnen Container neustarten
docker restart mana-chat-backend
# Neueste Images pullen und Container aktualisieren
./scripts/mac-mini/deploy.sh
```
### Autostart Management
```bash
# LaunchAgents Status prüfen
launchctl list | grep -E "(cloudflare|mana)"
# Health Check manuell triggern
launchctl start com.mana.health-check
# Service neuladen
launchctl unload ~/Library/LaunchAgents/com.mana.docker-startup.plist
launchctl load ~/Library/LaunchAgents/com.mana.docker-startup.plist
```
## GPU Tunnel (mana-gpu-server)
The Windows GPU box is **not** reached via the Mac Mini's tunnel. It runs its
own Cloudflare tunnel as a Windows Service, exposing five public hostnames:
| Hostname | Local target on Windows | Service |
|---|---|---|
| `gpu-stt.mana.how` | `localhost:3020` | mana-stt (Whisper) |
| `gpu-llm.mana.how` | `localhost:3025` | mana-llm |
| `gpu-tts.mana.how` | `localhost:3022` | mana-tts |
| `gpu-img.mana.how` | `localhost:3023` | mana-image-gen (FLUX) |
| `gpu-ollama.mana.how` | `localhost:11434` | Ollama |
The connector itself runs as the Windows Service `Cloudflared`, installed via
`cloudflared.exe service install <TOKEN>`. **Token-managed** means the routing
config (which hostname → which local port) lives in the Cloudflare Zero Trust
dashboard, **not** in any local config file. Editing
`~/.cloudflared/config.yml` on the Windows box has no effect on this tunnel.
### Adding a new GPU hostname
1. **Cloudflare dashboard**: Zero Trust → Networks → Tunnels → `mana-gpu-server`
→ Public Hostname → "Add a public hostname" with `Service Type: HTTP` and
`URL: localhost:<PORT>`. The dashboard creates both the DNS CNAME and the
ingress rule in one step.
2. **Verify**: `curl https://<new-host>.mana.how/health` should return 200 within
a few seconds (no need to restart the connector — it picks up dashboard
changes automatically).
### If a `gpu-*` hostname returns 502
Most likely the DNS CNAME points at a different tunnel (e.g. `mana-server`
instead of `mana-gpu-server`). To force-repoint from the Mac Mini:
```bash
ssh mana-server "/opt/homebrew/bin/cloudflared tunnel route dns \
--overwrite-dns 83454e8e-d7f5-4954-b2cb-0307c2dba7a6 <hostname>"
```
(`83454e8e-…` is the `mana-gpu-server` tunnel UUID. Use the UUID, not the
name — the CLI resolves the name against the wrong tunnel context otherwise.)
Other 502 root causes to check, in order of likelihood:
1. **Cloudflared service stopped on Windows**: `ssh mana-gpu "Get-Service Cloudflared"` → must show `Running`. Restart with `Restart-Service Cloudflared`.
2. **Origin service down**: `ssh mana-gpu "Get-ScheduledTask -TaskName ManaSTT"` → must show `Running`. The Python service runs as a Scheduled Task with `RestartCount=5, RestartInterval=PT1M`, so a crash should self-heal within ~1 min.
3. **Public Hostname missing in dashboard**: tunnel has zero ingress rules for the requested hostname.
### API key for STT proxy
The unified mana-web container's `/api/v1/voice/transcribe` proxy needs
`MANA_STT_API_KEY` to authenticate against `gpu-stt.mana.how`. The key:
- Lives in **Mac Mini `~/projects/managarten/.env`** (gitignored)
- Is referenced from `docker-compose.macmini.yml` as `${MANA_STT_API_KEY:-}`
- The source-of-truth is `services/mana-stt/.env` on the Windows GPU box (`API_KEYS=<key>:<name>`)
To rotate: append a new entry on the Windows side, restart the `ManaSTT`
scheduled task, update both `.env` files (Mac Mini + any local dev), restart
`mana-app-web`. Use a separate key per consumer (`mana-web`, future apps) so
they can be revoked individually.
## Autostart-Konfiguration
Drei LaunchAgents sorgen fuer automatischen Betrieb:
### 1. Cloudflare Tunnel
**Datei:** `~/Library/LaunchAgents/com.cloudflare.cloudflared.plist`
- Startet beim Login
- Haelt den Tunnel zu Cloudflare offen (mana-server tunnel only — the
GPU tunnel runs on the Windows box as a Windows Service, not here)
- Automatischer Neustart bei Absturz
### 2. Docker Container Startup
**Datei:** `~/Library/LaunchAgents/com.mana.docker-startup.plist`
- Startet beim Login
- Wartet auf Docker Desktop
- Fuehrt `docker compose up -d` aus
- Erstellt fehlende Datenbanken automatisch
### 3. Health Check
**Datei:** `~/Library/LaunchAgents/com.mana.health-check.plist`
- Laeuft alle 5 Minuten
- Prueft alle Services (HTTP + Docker)
- Sendet Benachrichtigungen bei Fehlern
### Deaktivierte / entfernte LaunchAgents
Seit der GPU-Server-Migration laufen keine AI-Services mehr auf dem Mac
Mini. Die zugehörigen LaunchAgents sind deaktiviert und ihre Repo-Vorlagen
wurden entfernt:
- `homebrew.mxcl.ollama.plist` — LLM läuft auf GPU-Server (`gpu-llm.mana.how`)
- `com.mana.image-gen.plist` — entfernt; image-gen läuft als
Scheduled Task `ManaImageGen` auf GPU-Server (`gpu-img.mana.how`)
- `com.mana.mana-stt.plist` — entfernt; STT als Task `ManaSTT`
- `com.mana.mana-tts.plist` — entfernt; TTS als Task `ManaTTS`
- `com.mana.vllm-voxtral.plist` — entfernt; vLLM-Voxtral nicht mehr verwendet
- `com.mana.telegram-ollama-bot.plist` — Bot deaktiviert
Falls auf einem Mac Mini noch alte plists installiert sind:
```bash
launchctl unload ~/Library/LaunchAgents/com.mana.image-gen.plist 2>/dev/null
launchctl unload ~/Library/LaunchAgents/com.mana.mana-stt.plist 2>/dev/null
launchctl unload ~/Library/LaunchAgents/com.mana.mana-tts.plist 2>/dev/null
launchctl unload ~/Library/LaunchAgents/com.mana.vllm-voxtral.plist 2>/dev/null
rm -f ~/Library/LaunchAgents/com.mana.{image-gen,mana-stt,mana-tts,vllm-voxtral}.plist
```
### Setup neu ausführen
Falls die LaunchAgents neu eingerichtet werden müssen:
```bash
./scripts/mac-mini/setup-autostart.sh
```
## Benachrichtigungen
### Konfiguration
**Datei:** `.env.notifications`
```bash
# Telegram
TELEGRAM_BOT_TOKEN=xxx
TELEGRAM_CHAT_ID=xxx
# Email
EMAIL_TO=your@email.com
EMAIL_FROM=mana@mana.how
# ntfy.sh (optional)
NTFY_TOPIC=your-topic
```
### Telegram Bot
- **Bot:** @alterts_mana_bot
- **Chat ID:** 7117174865
- Sendet Alerts mit:
- Fehlgeschlagene Services
- Hostname und Zeitstempel
- Anleitung zur Fehlersuche
### Email
- Verwendet `msmtp` als SMTP-Client
- Konfiguration in `~/.msmtprc`
- Gmail mit App-Password
### Benachrichtigung testen
```bash
# Test-Nachricht senden
curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
-d "chat_id=${TELEGRAM_CHAT_ID}" \
-d "text=Test notification"
```
## Docker Compose
**Datei:** `docker-compose.macmini.yml`
### Container-Namen
| Container | Service |
|-----------|---------|
| mana-postgres | PostgreSQL Datenbank |
| mana-redis | Redis Cache |
| mana-auth | Auth Service |
| mana-dashboard-web | Dashboard |
| mana-chat-backend | Chat API |
| mana-chat-web | Chat Frontend |
| mana-todo-backend | Todo API |
| mana-todo-web | Todo Frontend |
| mana-calendar-backend | Calendar API |
| mana-calendar-web | Calendar Frontend |
| mana-clock-backend | Clock API |
| mana-clock-web | Clock Frontend |
### Nützliche Docker-Befehle
```bash
# Alle Container starten
docker compose -f docker-compose.macmini.yml up -d
# Alle Container stoppen
docker compose -f docker-compose.macmini.yml down
# Container neustarten
docker compose -f docker-compose.macmini.yml restart
# Neueste Images pullen
docker compose -f docker-compose.macmini.yml pull
# Logs aller Container
docker compose -f docker-compose.macmini.yml logs -f
# Einzelnen Service neustarten
docker compose -f docker-compose.macmini.yml restart chat-backend
```
## Cloudflare Tunnel
### Konfiguration
**Datei:** `~/.cloudflared/config.yml`
> ⚠️ **Wichtig:** Dies ist eine separate Datei vom Repo-File `cloudflared-config.yml`.
> Neue Hostnames müssen in **beiden** Dateien eingetragen werden:
> 1. `cloudflared-config.yml` im Repo (für Dokumentation und Git-History)
> 2. `~/.cloudflared/config.yml` auf dem Server (was cloudflared tatsächlich liest)
> 3. DNS-Eintrag anlegen: `cloudflared tunnel route dns <tunnel-id> <hostname>`
> 4. Cloudflared neu starten: `launchctl stop com.cloudflare.cloudflared && launchctl start com.cloudflare.cloudflared`
```yaml
tunnel: mana-tunnel
credentials-file: ~/.cloudflared/credentials.json
ingress:
- hostname: mana.how
service: http://localhost:5173
- hostname: auth.mana.how
service: http://localhost:3001
- hostname: chat.mana.how
service: http://localhost:3000
# ... weitere Services
- service: http_status:404
```
### Tunnel Status
```bash
# Prüfen ob cloudflared läuft
pgrep -x cloudflared
# Tunnel-Logs
tail -f ~/.cloudflared/cloudflared.log
```
## Troubleshooting
### Container startet nicht
```bash
# Logs prüfen
docker logs mana-<service-name>
# Container manuell starten
docker start mana-<service-name>
# Bei Problemen: Container neu erstellen
docker compose -f docker-compose.macmini.yml up -d --force-recreate <service-name>
```
### Tunnel nicht erreichbar
```bash
# cloudflared Status
pgrep -x cloudflared
# cloudflared neustarten
launchctl stop com.cloudflare.cloudflared
launchctl start com.cloudflare.cloudflared
# Logs prüfen
tail -100 ~/.cloudflared/cloudflared.log
```
### Datenbank-Probleme
```bash
# PostgreSQL Status
docker exec mana-postgres pg_isready -U postgres
# Datenbanken auflisten
docker exec mana-postgres psql -U postgres -c "\l"
# Datenbank manuell erstellen
docker exec mana-postgres psql -U postgres -c "CREATE DATABASE chat_db;"
```
### Health Check Fehler
```bash
# Health Check manuell ausführen
./scripts/mac-mini/health-check.sh
# Einzelnen Service testen
curl -s http://localhost:3002/api/v1/health
curl -s http://localhost:3000/
```
The health check monitors:
- All backend APIs and web frontends
- Infrastructure (PostgreSQL, Redis)
- Monitoring stack (Grafana, Umami, GlitchTip, VictoriaMetrics)
- Alerting stack (vmalert, Alertmanager, Alert Notifier)
- Disk space for `/` and `/Volumes/ManaData` (warning at 80%, critical at 90%)
- Cloudflare Tunnel (cloudflared process)
### Docker PATH auf dem Server
Bei SSH-Zugriff ist Docker nicht im Standard-PATH. Für Remote-Befehle:
```bash
# Docker liegt unter Docker Desktop
PATH=/Applications/Docker.app/Contents/Resources/bin:$PATH
# Beispiel: Remote docker compose
ssh mana-server "PATH=/Applications/Docker.app/Contents/Resources/bin:\$PATH && docker compose -f ~/projects/managarten/docker-compose.macmini.yml restart grafana"
```
### Container existiert nicht (wurde nie erstellt)
Wenn ein Service im Health-Check als `HTTP 000` erscheint und `docker ps -a` den Container nicht zeigt, wurde er vermutlich beim letzten Deploy übersprungen:
```bash
# Container erstellen und starten
docker compose -f docker-compose.macmini.yml up -d <service-name>
# Nach Restart prüfen
docker ps --filter name=mana-<service> --format '{{.Names}} {{.Status}}'
```
## Wartung
### Updates einspielen
```bash
# Neuesten Code holen
git pull
# Neue Images pullen und deployen
./scripts/mac-mini/deploy.sh
# Einzelne App bauen und deployen (empfohlen)
./scripts/mac-mini/build-app.sh todo-web
./scripts/mac-mini/build-app.sh todo-web todo-backend
# Base Images neu bauen (nach Änderungen an shared packages)
./scripts/mac-mini/build-app.sh --base
```
### Docker Base Images
Alle Web-Apps werden auf einem vorgebauten Base Image aufgebaut, um Build-Zeit und Memory-Verbrauch zu reduzieren. Backend-Server verwenden `docker/Dockerfile.hono-server` (Hono + Bun) direkt.
| Base Image | Dockerfile | Verwendet von |
|------------|-----------|---------------|
| `sveltekit-base:local` | `docker/Dockerfile.sveltekit-base` | Alle SvelteKit Web-Apps |
Das Base Image enthaelt alle Shared Packages (`packages/`) vorinstalliert und vorgebaut. App-Dockerfiles muessen nur noch ihren app-spezifischen Code kopieren.
**Base Image neu bauen** wenn sich Shared Packages aendern:
```bash
./scripts/mac-mini/build-app.sh --base
```
### Build-Script (`build-app.sh`)
Das Script prüft vor dem Build den verfügbaren RAM und stoppt Monitoring-Container **nur wenn nötig** (< 3 GB frei). Alle Container haben explizite `mem_limit` Obergrenzen in der `docker-compose.macmini.yml`, sodass der tatsächliche Verbrauch typischerweise 50-70% der Limits beträgt und genug Headroom für Builds bleibt.
**Was es tut:**
1. Prüft verfügbaren RAM in der Colima VM
2. Stoppt 13 Monitoring-Container nur wenn < 3 GB frei (vorher: immer)
3. Baut die angegebenen Services
4. Startet Monitoring bei Exit automatisch wieder (auch bei Fehler/Ctrl+C via `trap`)
```bash
# Einzelne App
./scripts/mac-mini/build-app.sh todo-web
# Mehrere Apps
./scripts/mac-mini/build-app.sh todo-web todo-backend
# Alle Web-Apps
./scripts/mac-mini/build-app.sh --all-web
# Monitoring immer stoppen (altes Verhalten)
./scripts/mac-mini/build-app.sh --force-free todo-web
```
### Memory Baseline
Misst den tatsächlichen RAM-Verbrauch aller Container, sortiert nach Kategorie:
```bash
# Einmalige Messung mit Zusammenfassung
./scripts/mac-mini/memory-baseline.sh
# Live-Monitoring (docker stats)
./scripts/mac-mini/memory-baseline.sh --watch
```
### Memory-Limits
> **Stand 2026-05-07** — nach Phase 2c+2d+2e+2f+2g Cleanup (siehe [`PLAN_OPTION_C.md`](./PLAN_OPTION_C.md)) sind ~17 Service-Blöcke (Grafana, VictoriaMetrics, Loki, Tempo, Promtail, Pushgateway, Blackbox-Exporter, Vmalert, Alertmanager, Alert-Notifier, Umami, Glitchtip+Worker, Forgejo, Status-Page-Gen, news-ingester, mana-ai, mana-research) auf die GPU-Box gewandert und aus der Mini-Compose entfernt. Phase 2f-1 (verdaccio) wurde am 2026-05-07 zurückgerollt — verdaccio bleibt Standalone-Compose unter `~/projects/verdaccio/` auf dem Mini.
Container in `docker-compose.macmini.yml` haben explizite `mem_limit`:
| Kategorie | Container (~) | Budget |
|-----------|-----------|--------|
| Infrastructure (postgres, redis, minio, …) | 6 | ~1.712 MB |
| Core (Hono/Bun + Go) | ~17 | ~2.500 MB |
| Web Apps + Standalone | ~6 | ~700 MB |
| Memoro Stack | 3 | ~900 MB |
| Helper-Exporter (cadvisor, node-exporter, postgres-exporter, redis-exporter) | 4 | ~270 MB |
| Andere (admin, mail, searxng) | 3 | ~770 MB |
| Standalone-Compose (`~/projects/verdaccio/`) | 1 | ~120 MB |
| **Total (~42 running)** | **~42** | **~6 GiB nominal** |
Colima VM: 12 GiB Headroom: ~6 GiB (Limits) / ~10 GiB (real, da reale Last typisch 23 GiB liegt). Build-Skript `build-app.sh` muss seit dem Cleanup das Monitoring-Stop-Trigger nicht mehr feuern (war zuvor bei < 3 GiB free aktiv).
**Was auf der GPU-Box läuft**, sieh in [`PLAN_OPTION_C.md`](./PLAN_OPTION_C.md) §"Was läuft heute auf der GPU-Box". Hostnames `grafana.mana.how`, `git.mana.how`, `stats.mana.how`, `glitchtip.mana.how`, `status.mana.how`, `mana-ai.mana.how`, `research.mana.how`, `photon.mana.how` werden vom **`mana-gpu-server`-Tunnel** (UUID `83454e8e-…`) bedient, nicht vom Mini.
### Backup
Die PostgreSQL-Datenbank sollte regelmäßig gesichert werden:
```bash
# Backup erstellen
docker exec mana-postgres pg_dumpall -U postgres > backup_$(date +%Y%m%d).sql
# Backup wiederherstellen
cat backup_20260123.sql | docker exec -i mana-postgres psql -U postgres
```
### Logs aufräumen
```bash
# Docker Logs beschränken (bereits in compose konfiguriert)
# max-size: 10m, max-file: 3
# Alte Docker Images entfernen
docker image prune -a
```
## Skript-Übersicht
| Skript | Beschreibung |
|--------|--------------|
| `setup-autostart.sh` | Richtet LaunchAgents ein (einmalig) |
| `setup-notifications.sh` | Interaktives Notification-Setup |
| `startup.sh` | Wird von launchd beim Boot aufgerufen |
| `health-check.sh` | Prüft Services, sendet Alerts |
| `status.sh` | Zeigt Übersicht aller Services |
| `restart.sh` | Startet alle Container neu |
| `stop.sh` | Stoppt alle Container |
| `deploy.sh` | Pullt neue Images und startet neu |
| `build-app.sh` | Baut einzelne Apps (smart memory check, stoppt Monitoring nur wenn nötig) |
| `memory-baseline.sh` | Misst RAM-Verbrauch aller Container nach Kategorie |
## Hardware
- **Chip:** Apple M4 (10 Cores)
- **RAM:** 16 GB Unified Memory
- **Interne SSD:** 228 GB
- **Externe SSD:** 4 TB (ManaData)
## AI-Workloads (GPU-Server)
Alle AI-Services (LLM, Bildgenerierung, STT, TTS) laufen auf dem Windows GPU-Server (RTX 3090, 24 GB VRAM) unter `192.168.178.11`. Der Mac Mini ist reiner Hosting-Server fuer Web, API, DB und Sync.
| Service | GPU-Server Port | Zugriff aus Docker | Public URL |
|---------|----------------|-------------------|------------|
| mana-llm | 3025 | `http://192.168.178.11:3025` | `gpu-llm.mana.how` |
| mana-stt (Whisper) | 3020 | `http://192.168.178.11:3020` | `gpu-stt.mana.how` |
| mana-tts | 3022 | `http://192.168.178.11:3022` | `gpu-tts.mana.how` |
| mana-image-gen | 3023 | `http://192.168.178.11:3023` | `gpu-img.mana.how` |
| mana-video-gen | 3026 | `http://192.168.178.11:3026` | `gpu-video.mana.how` |
| Ollama | 11434 | `http://192.168.178.11:11434` | `gpu-ollama.mana.how` |
Repo-Pendants: `services/mana-{llm,stt,tts,image-gen,video-gen}/` die `service.pyw` Runner werden direkt auf der Windows-Box als Scheduled Tasks ausgeführt.
Alle Werte sind per Env-Var ueberschreibbar (`OLLAMA_URL`, `STT_SERVICE_URL`, `TTS_SERVICE_URL`, `IMAGE_GEN_SERVICE_URL`).
Cloud-Fallback bei GPU-Server-Ausfall: `mana-llm` hat `AUTO_FALLBACK_ENABLED=true` (OpenRouter, Groq, Google).
### Ollama/FLUX.2 Mac-Mini-Reste (deaktiviert)
Ollama und das alte Mac-Mini FLUX.2 (`flux2.c` MPS) waren früher lokal installiert, sind seit 2026-03-28 deaktiviert. Die zugehörigen Repo-Setup-Skripte (`scripts/mac-mini/setup-image-gen.sh`, launchd plists) wurden 2026-04-08 entfernt; die Modelle liegen ggf. noch auf der SSD als Backup:
- `/Volumes/ManaData/ollama/` (~58 GB)
- `/Volumes/ManaData/flux2/` (~15 GB)
Falls du sie auf einem alten Mac Mini noch findest, einfach löschen sie laufen nicht mehr und werden nirgendwo gebraucht.
## Externe 4TB SSD
Die externe SSD wird für persistente Daten verwendet - sowohl für große Dateien (AI-Modelle) als auch für kritische Datenbanken (PostgreSQL, MinIO).
### Mount-Punkt
- **Volume:** `/Volumes/ManaData`
- **Geschwindigkeit:** ~1 GB/s (USB-C/Thunderbolt)
### Verzeichnisstruktur
```
/Volumes/ManaData/
├── Docker/ # Docker Desktop Daten (~228 GB) ⭐ Kritisch
│ └── com.docker.docker/ # Symlink von ~/Library/Containers/
├── postgres/ # PostgreSQL Datenbank (~200 MB) ⭐ Kritisch
├── minio/ # MinIO Object Storage (Storage App)
├── backups/ # PostgreSQL Backups (täglich 3:00)
├── ollama/ # LLM Modelle (~58 GB)
├── flux2/ # FLUX.2 Bildgenerierung (~15 GB)
└── stt-models/ # Speech-to-Text Modelle (~19 GB)
```
### Docker auf externer SSD
Docker Desktop läuft komplett von der externen SSD um die interne SSD zu entlasten:
**Symlink:**
```
~/Library/Containers/com.docker.docker -> /Volumes/ManaData/Docker/com.docker.docker
```
**Vorteile:**
- Interne SSD hat ~80GB mehr freien Speicher
- Docker kann unbegrenzt wachsen (3.5TB verfügbar)
- Keine Speicherprobleme beim Pullen großer Images
**Wichtig:** Die externe SSD muss IMMER angeschlossen sein, wenn Docker läuft!
### Vorteile der SSD-Speicherung
| Aspekt | Docker VM | Externe SSD |
|--------|-----------|-------------|
| **Bei Docker-Reset** | Daten weg | Daten bleiben |
| **Bei macOS-Neuinstall** | Daten weg | Daten bleiben |
| **Performance** | Langsamer | ~20-30% schneller |
| **Backup** | Schwieriger | Einfacher |
### Docker-Integration
Die folgenden Services nutzen direkte SSD-Mounts (kein Docker Volume):
| Service | SSD-Pfad | docker-compose.macmini.yml |
|---------|----------|---------------------------|
| PostgreSQL | `/Volumes/ManaData/postgres` | `volumes: - /Volumes/ManaData/postgres:/var/lib/postgresql/data` |
| MinIO | `/Volumes/ManaData/minio` | `volumes: - /Volumes/ManaData/minio:/data` |
### Symlinks (archiviert, fuer Backup-Modelle)
| Original | Symlink | Status |
|----------|---------|--------|
| `~/.ollama` | `/Volumes/ManaData/ollama` | Deaktiviert (GPU-Server) |
| `~/stt-models` | `/Volumes/ManaData/stt-models` | Deaktiviert (GPU-Server) |
| `~/flux2` | `/Volumes/ManaData/flux2` | Deaktiviert (GPU-Server) |
### SSD prüfen
```bash
# Mount-Status
df -h /Volumes/ManaData
# Nutzung
du -sh /Volumes/ManaData/*/
# Speed-Test
dd if=/dev/zero of=/Volumes/ManaData/test bs=1m count=1024 && rm /Volumes/ManaData/test
```
### Automatische Backups
PostgreSQL-Backups laufen täglich um 3:00 Uhr:
```bash
# Backup-Skript
/Users/mana/backup-postgres.sh
# Backup-Verzeichnis
/Volumes/ManaData/backups/postgres/
# Retention: 30 Tage
```
### Docker Desktop Voraussetzung
Docker Desktop benötigt "Full Disk Access" für SSD-Mounts:
```
Systemeinstellungen → Datenschutz & Sicherheit → Voller Festplattenzugriff → Docker.app ✅
```
## Chronologie der Einrichtung
1. **Docker Setup** - PostgreSQL, Redis, App-Container
2. **Cloudflare Tunnel** - Oeffentliche Erreichbarkeit
3. **SSH via Cloudflare Access** - Sicherer Remote-Zugang
4. **LaunchAgents** - Autostart bei Boot
5. **Health Checks** - Automatische Ueberwachung
6. **Telegram Notifications** - Alerts bei Fehlern
7. **Email Notifications** - Redundante Benachrichtigung
8. ~~**Ollama** - Lokale LLM-Inferenz~~ Migriert auf GPU-Server (2026-03-28)
9. ~~**Telegram Ollama Bot**~~ Deaktiviert (2026-03-28)
10. **GPU-Server Offload** - Alle AI-Workloads auf RTX 3090 (2026-03-28)