managarten/docker-compose.macmini.yml
Till JS 832adfe37b
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
Mirror to Forgejo / Push to Forgejo (push) Waiting to run
infra: Kern-Services aus Compose entfernen (leben jetzt in mana-core)
docker-compose.macmini.yml von 37 auf 6 reale App-Services reduziert
(landings, searxng, mana-web, manavoxel-web, mana-llm, mana-api).
postgres/redis/minio/auth/credits/share/mcp/notify/sync/... + Dubletten
raus; depends_on auf Kern-Services entfernt (Kern via Netz erreichbar).
mana-api + mana-web credits-URL auf kanonisch 3061.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 02:39:48 +02:00

323 lines
14 KiB
YAML
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.

# Mana Mac Mini Configuration
# Domain: mana.how (via Cloudflare Tunnel)
#
# Port Schema:
# 3000-3099: Core Services & Backends
# 5000-5099: Web Frontends
# 5100-5199: Games
# 8000-8099: Monitoring Dashboards
# 9000-9199: Infrastructure & Exporters
#
# Naming Convention: mana-{category}-{service}
# Categories: infra, core, app, mon, auto
#
# Memory Limits:
# All containers have explicit mem_limit to prevent unbounded growth.
# Total budget: ~9.8 GiB (fits in 12 GiB Colima VM with ~2 GiB for builds)
# Run ./scripts/mac-mini/memory-baseline.sh to verify actual usage.
# Limits are ceilings — actual usage is typically 50-70% of limits.
services:
# ── Verbleibende managarten-Apps (Kern lebt in mana-core, 2026-05-25) ──
landings:
image: nginx:alpine
container_name: mana-infra-landings
restart: always
mem_limit: 48m
volumes:
- ./docker/nginx:/etc/nginx/host-config:ro
- /Volumes/ManaData/landings:/srv/landings:ro
command: >
sh -c "mkdir -p /etc/nginx/snippets &&
cp /etc/nginx/host-config/landings.conf /etc/nginx/conf.d/default.conf &&
cp /etc/nginx/host-config/snippets/* /etc/nginx/snippets/ &&
nginx -g 'daemon off;'"
ports:
- "4400:80"
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://127.0.0.1/health"]
interval: 30s
timeout: 5s
retries: 3
searxng:
image: searxng/searxng:latest
container_name: mana-core-searxng
restart: always
mem_limit: 256m
volumes:
- ./services/mana-search/searxng:/mnt/searxng-config:ro
entrypoint: ["sh", "-c", "cp /mnt/searxng-config/settings.yml /etc/searxng/settings.yml && cp /mnt/searxng-config/limiter.toml /etc/searxng/limiter.toml 2>/dev/null; exec /usr/local/searxng/entrypoint.sh"]
environment:
SEARXNG_BASE_URL: http://searxng:8080
SEARXNG_SECRET: ${SEARXNG_SECRET:-change-me-searxng-secret}
# Internal only - no external port mapping
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost:8080/healthz"]
interval: 120s
timeout: 10s
retries: 3
start_period: 15s
mana-web:
build:
context: .
dockerfile: apps/mana/apps/web/Dockerfile
args:
PUBLIC_SYNC_SERVER_URL: wss://sync.mana.how
image: mana-web:local
container_name: mana-app-web
restart: always
mem_limit: 256m
# Kern (mana-auth) lebt in Projekt mana-core, erreichbar übers Netz.
depends_on:
mana-api:
condition: service_healthy
environment:
NODE_ENV: production
PORT: 5000
PUBLIC_MANA_AUTH_URL: http://mana-auth:3001
PUBLIC_MANA_AUTH_URL_CLIENT: https://auth.mana.how
# Auth-Portal-UI (mana-auth-web). In Prod gleiche Origin wie
# mana-auth API — nginx splittet `/api/*` (mana-auth) von den
# UI-Routen `/login`, `/register`, `/auth/callback` (mana-auth-web).
# Client-Code liest die `_CLIENT`-Variante via hooks.server.ts
# injection (`window.__PUBLIC_AUTH_WEB_URL__`).
PUBLIC_AUTH_WEB_URL: https://auth.mana.how
PUBLIC_AUTH_WEB_URL_CLIENT: https://auth.mana.how
PUBLIC_SYNC_SERVER_URL: http://mana-sync:3010
PUBLIC_SYNC_SERVER_URL_CLIENT: https://sync.mana.how
# Unified Hono/Bun API server (apps/api) — hosts all 16 product
# compute modules (calendar, todo, picture, …) on port 3060.
# Browser calls go through https://mana-api.mana.how (cloudflared
# tunnel route to mana-api:3060). SSR calls inside the docker
# network use the internal hostname.
#
# NOTE: api.mana.how is already in use for the Go mana-api-gateway
# on port 3016 (different service, predates apps/api). The
# mana-api.* subdomain is the unambiguous new home.
PUBLIC_MANA_API_URL: http://mana-api:3060
PUBLIC_MANA_API_URL_CLIENT: https://mana-api.mana.how
# Server-side same-origin proxy upstream for routes/api/v1/who/[...path].
# Inside docker the SvelteKit handler reaches mana-api over the internal
# network; the dev fallback in code is localhost:3060, so this env var
# is what makes prod hit the right hostname.
MANA_API_INTERNAL_URL: http://mana-api:3060
PUBLIC_MANA_CREDITS_URL: http://mana-credits:3061
PUBLIC_MANA_CREDITS_URL_CLIENT: https://credits.mana.how
# Per-app HTTP backend URLs (todo-api, calendar-api, contacts-api,
# chat-api, storage-api, cards-api, music-api,
# picture-api, presi-api, quotes-api, clock-api, context-api) and
# standalone server URLs were removed in the pre-launch
# ghost-API cleanup — every product module talks to mana-sync
# directly via mana-sync.
# See docs/PRE_LAUNCH_CLEANUP.md for the full rationale.
PUBLIC_MANA_MEDIA_URL: http://mana-media:3011
PUBLIC_MANA_MEDIA_URL_CLIENT: https://media.mana.how
PUBLIC_MANA_LLM_URL: http://mana-llm:3025
PUBLIC_MANA_LLM_URL_CLIENT: https://llm.mana.how
PUBLIC_MANA_EVENTS_URL: http://mana-events:3065
PUBLIC_MANA_EVENTS_URL_CLIENT: https://events.mana.how
# mana-research — async web-research provider orchestration.
# Browser hits /research/* endpoints directly; SSR uses the
# internal docker-network URL. Without this pair, the SSR-
# injected window.__PUBLIC_MANA_RESEARCH_URL__ is empty string
# and research fetches fall back to the current origin (404).
PUBLIC_MANA_RESEARCH_URL: https://research.mana.how
PUBLIC_MANA_RESEARCH_URL_CLIENT: https://research.mana.how
# mana-analytics — public-feedback hub. Browser hits the
# /api/v1/(public/)feedback/* endpoints directly; SSR uses the
# internal docker-network URL.
PUBLIC_MANA_ANALYTICS_URL: http://mana-analytics:3064
PUBLIC_MANA_ANALYTICS_URL_CLIENT: https://feedback.mana.how
# mana-ai background Mission Runner. Browser calls the audit
# endpoint (/api/v1/me/ai-audit) to render the Workbench
# "Datenzugriff" tab. SSR doesn't hit this service directly.
PUBLIC_MANA_AI_URL: http://mana-ai:3067
PUBLIC_MANA_AI_URL_CLIENT: https://mana-ai.mana.how
# Feature flag for the Mission Key-Grant consent UI. false → the
# dialog + Workbench audit tab stay hidden even on missions with
# encrypted inputs. Flip to "true" per deployment once the keypair
# is provisioned (see docs/plans/ai-mission-key-grant.md).
PUBLIC_AI_MISSION_GRANTS: ${PUBLIC_AI_MISSION_GRANTS:-false}
# Analytics & Error Tracking
PUBLIC_UMAMI_WEBSITE_ID: 32777167-e026-4618-933a-3429120b479b
PUBLIC_GLITCHTIP_DSN: ${GLITCHTIP_DSN_MANA_WEB:-}
# Speech-to-Text proxy: SvelteKit /api/v1/voice/transcribe forwards
# to mana-stt via Cloudflare Tunnel. The browser never sees the API
# key — it stays server-side.
MANA_STT_URL: https://gpu-stt.mana.how
MANA_STT_API_KEY: ${MANA_STT_API_KEY:-}
# LLM proxy: /api/v1/voice/parse-task and /api/v1/voice/parse-habit
# call mana-llm for structured extraction. Set WITHOUT the PUBLIC_
# prefix because $env/dynamic/private explicitly excludes vars
# that start with the public prefix — so the parse endpoints
# would never see PUBLIC_MANA_LLM_URL even though it's right
# there in the compose env. Both vars exist; the public one
# is read by the browser-side playground and status page.
MANA_LLM_URL: http://mana-llm:3025
MANA_LLM_API_KEY: ${MANA_LLM_API_KEY:-}
ports:
- "5000:5000"
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:5000/health"]
interval: 180s
timeout: 10s
retries: 3
start_period: 20s
# REMOVED standalone web containers — now served by unified mana-web container (mana.how):
# chat-web, todo-web, quotes-web, calendar-web, clock-web, contacts-web,
# storage-web, presi-web, cards-web, skilltree-web, photos-web,
# music-web, picture-web, inventory-web, calc-web, times-web,
# uload-web
# picture-backend: REMOVED — replaced by Hono server (apps/picture/apps/server)
# arcade-web: REMOVED — extracted to standalone repo at ~/Documents/Code/arcade
manavoxel-web:
build:
context: .
dockerfile: apps/manavoxel/apps/web/Dockerfile
image: manavoxel-web:local
container_name: mana-app-manavoxel-web
restart: always
mem_limit: 128m
# Kern (mana-auth) lebt in Projekt mana-core, erreichbar übers Netz.
environment:
NODE_ENV: production
PORT: 5028
PUBLIC_MANA_AUTH_URL: http://mana-auth:3001
PUBLIC_MANA_AUTH_URL_CLIENT: https://auth.mana.how
PUBLIC_SYNC_SERVER_URL: http://mana-sync:3010
PUBLIC_SYNC_SERVER_URL_CLIENT: https://sync.mana.how
ports:
- "5028:5028"
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:5028/health"]
interval: 180s
timeout: 10s
retries: 3
start_period: 45s
mana-llm:
build:
context: ../mana/services/mana-llm
dockerfile: Dockerfile
container_name: mana-service-llm
restart: unless-stopped
# Tier-3 right-size 2026-04-28: live RSS ~46 MiB (18%). The service
# is a thin OpenAI-compatible router around the GPU-box Ollama —
# all heavy LLM work happens upstream, this container just proxies.
# 128m is 2.5× headroom for streaming response buffers.
mem_limit: 128m
# Kern (redis) lebt in Projekt mana-core, erreichbar übers Netz.
# Ollama lives on the Windows GPU box at 192.168.178.11:11434, but
# Colima containers can't reach the LAN range — the entire
# 192.168.178.0/24 subnet gets synthesized RST from inside any
# container, even though the macOS host routes there fine. The
# gpu-proxy LaunchAgent on the Mac Mini host (com.mana.gpu-proxy,
# see /Users/mana/gpu-proxy.py) bridges 127.0.0.1:13434 → GPU
# box's 11434, so we go through host.docker.internal:13434 to
# reach Ollama. Without this hop the local mana-llm starts
# cleanly but reports an empty model list and every chat
# completion fails with "All connection attempts failed", which
# cascades into voice quick-add silently degrading to its no-LLM
# fallback for everyone hitting the local stack.
extra_hosts:
- "host.docker.internal:host-gateway"
environment:
PORT: 3025
LOG_LEVEL: info
OLLAMA_URL: ${OLLAMA_URL:-http://host.docker.internal:13434}
OLLAMA_DEFAULT_MODEL: ${OLLAMA_MODEL:-gemma3:12b}
OLLAMA_TIMEOUT: 120
REDIS_URL: redis://redis:6379
OPENROUTER_API_KEY: ${OPENROUTER_API_KEY:-}
GROQ_API_KEY: ${GROQ_API_KEY:-}
TOGETHER_API_KEY: ${TOGETHER_API_KEY:-}
GOOGLE_API_KEY: ${GOOGLE_API_KEY:-}
GOOGLE_DEFAULT_MODEL: gemini-2.0-flash
# Direct providers (added 2026-05-14, option D — sowohl direkt als auch
# OpenRouter). Leer-Werte werden vom Router still übersprungen.
OPENAI_API_KEY: ${OPENAI_API_KEY:-}
OPENAI_DEFAULT_MODEL: ${OPENAI_DEFAULT_MODEL:-gpt-4o-mini}
ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:-}
ANTHROPIC_DEFAULT_MODEL: ${ANTHROPIC_DEFAULT_MODEL:-claude-sonnet-4-5}
AUTO_FALLBACK_ENABLED: "true"
OLLAMA_MAX_CONCURRENT: 5
CORS_ORIGINS: https://playground.mana.how,https://mana.how,https://chat.mana.how,https://manawald.mana.how,http://localhost:3090
ports:
- "3025:3025"
healthcheck:
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:3025/health')"]
interval: 120s
timeout: 10s
retries: 3
start_period: 30s
mana-api:
build:
context: .
dockerfile: apps/api/Dockerfile
args:
# Verdaccio-Token zum Pullen von @mana/media-client (und künftiger
# Plattform-Pakete). Auf dem Mac-Mini kommt aus `.env.macmini`,
# in CI aus `secrets.NPM_AUTH_TOKEN`. Setzt die `.npmrc`-
# Variable `${NPM_TOKEN}` zur Build-Zeit.
NPM_TOKEN: ${NPM_AUTH_TOKEN:-${NPM_TOKEN:-}}
image: mana-api:local
container_name: mana-api
restart: always
mem_limit: 384m
# Kern (postgres, mana-auth) lebt in Projekt mana-core, erreichbar übers Netz.
environment:
TZ: Europe/Berlin
NODE_ENV: production
PORT: 3060
# Auth (JWT validation via JWKS)
MANA_AUTH_URL: http://mana-auth:3001
JWT_PUBLIC_KEY: ${JWT_PUBLIC_KEY:-}
# Compute services apps/api orchestrates
MANA_LLM_URL: http://mana-llm:3025
MANA_SEARCH_URL: http://mana-search:3012
MANA_CREDITS_URL: http://mana-credits:3061
MANA_MEDIA_URL: http://mana-media:3011
MANA_CRAWLER_URL: http://mana-crawler:3014
# mana-news-pool — Plattform-Service (Lift-B 2026-05-16). Wird
# vom news-research-Modul + von der Pageta-Standalone konsumiert.
MANA_NEWS_POOL_URL: http://mana-news-pool:3079
MANA_LLM_DEFAULT_MODEL: ${MANA_LLM_DEFAULT_MODEL:-gemma3:4b}
MANA_SERVICE_KEY: ${MANA_SERVICE_KEY}
# OpenAI — picture module gpt-image-2 path. Optional: without it,
# /api/v1/picture/generate falls through to Replicate/local Flux.
OPENAI_API_KEY: ${OPENAI_API_KEY:-}
# Replicate — fallback for Flux-schnell image generation
REPLICATE_API_TOKEN: ${REPLICATE_API_TOKEN:-}
APP_ID: mana-api
# Database (used by modules that have server-side state — research,
# presi share-links, traces guides). Same Postgres + schema split
# as the rest of the platform.
DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD:-mana123}@postgres:5432/mana_platform
# CORS — only the unified mana.how origin needs access today.
# The manavoxel game frontend doesn't call apps/api.
CORS_ORIGINS: https://mana.how
# Structured-logger format
LOGGER_FORMAT: json
ports:
- "3060:3060"
healthcheck:
test: ["CMD", "bun", "-e", "fetch('http://127.0.0.1:3060/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"]
interval: 60s
timeout: 5s
retries: 3
start_period: 30s
# ============================================
# News-Pool — wurde 2026-05-17 zur Plattform geliftet.
# ============================================
# Der ehemalige services/news-ingester:3066-Container ist abgeschaltet
# zugunsten von `mana-news-pool` (Plattform-Service Port 3079,
# ~/projects/mana/services/mana-news-pool/, eigene DB `mana_news_pool`).
# mana-api/news/routes.ts proxied auf MANA_NEWS_POOL_URL.