-
-
Notifications
You must be signed in to change notification settings - Fork 0
Deployment
Maestro deploys as a single-host Docker Compose stack behind one Caddy reverse proxy. A 4 GB VM is sufficient (the reference host is an Oracle Cloud Always Free Ampere arm64 instance). Full runbook: docs/DEPLOYMENT.md.
WebSockets, tasks that run up to 1800s (vs a typical 300s serverless ceiling), and background work that continues after the HTTP response make a long-lived container the right shape. See Realtime-and-WebSockets and Agent-Orchestration.
Only Caddy publishes ports (80/443). It auto-provisions TLS and routes by path:
-
@api path /api/* /health*→backend:8000(WebSocket upgrades handled automatically; no read timeout on hijacked connections, so 30-minute task streams stay open). -
@umami path /a/script.js /a/api/send→umami:3000(only these two analytics paths are public; the dashboard is not exposed). - everything else →
frontend:3000(including/docs, which is a marketing page in prod — FastAPI Swagger is disabled whenENVIRONMENT=production).
CORS disappears entirely and NEXT_PUBLIC_* fall to empty defaults, so the image is domain-agnostic and built once.
Compose project maestro; a shared x-logging anchor caps every service at json-file 10m × 5 (≈550 MB total). Memory limits per service.
| Service | Image | Public ports | Role |
|---|---|---|---|
postgres |
postgres:16-alpine |
none | Relational DB (password required). |
mongo |
mongo:7 |
none | Logs/sessions/marketplace (root auth). |
qdrant |
qdrant/qdrant:v1.18.2 (pinned) |
none | Vectors (API key; no healthcheck — distroless). |
redis |
redis:7-alpine |
none | Rate-limit + event bus (password, allkeys-lru, no persistence). |
ollama |
ollama/ollama:latest |
none | Embeddings only by default. |
ollama-pull |
ollama/ollama:latest |
— | One-shot; pulls nomic-embed-text. |
migrate |
ghcr.io/yigtwxx/maestro-backend |
— | One-shot alembic upgrade head; backend waits on service_completed_successfully. |
backend |
ghcr.io/yigtwxx/maestro-backend:${IMAGE_TAG} |
none | FastAPI app. |
frontend |
ghcr.io/yigtwxx/maestro-frontend:${IMAGE_TAG} |
none | Next standalone server. |
umami-db-init / umami
|
postgres / umami v3.2 |
loopback 3001
|
Optional analytics (profile analytics). |
caddy |
caddy:2-alpine |
80/443 | Reverse proxy + TLS. |
Named volumes: pgdata, mongodata, qdrantdata, ollamadata, caddydata, caddyconfig.
-
backend/Dockerfile— 2-stagepython:3.11-slim-bookworm: builder installs into a venv (all deps ship manylinux wheels, no compiler), runtime copies the venv +app/+alembic/+alembic.ini, runs as non-rootappuser, healthcheck hits/health, CMDuvicorn ... --workers ${WEB_CONCURRENCY:-1}. Alembic ships in the image because migrate and the purge cron reuse it. -
frontend/Dockerfile— 2-stagenode:20-bookworm-slim(both stages bookworm-slim so the standalone native trace doesn't break on a musl/glibc mix): buildernpm ci+npm run build(output: 'standalone'), runner copies.next/standalone+.next/static, non-rootnode, CMDnode server.js. Nopublic/dir.
The hosted ollama serves embeddings only (OLLAMA_CHAT_ENABLED=false), because a chat model needs more RAM. A hosted instance cannot reach a user's local Ollama — all LLM calls are backend-side, so localhost:11434 is the server itself. To offer a free chat tier, raise the ollama memory limit above 2g, pull qwen3.5:9b, and set OLLAMA_CHAT_ENABLED=true. See Configuration.
WEB_CONCURRENCY scales Uvicorn workers. Multi-worker requires REDIS_URL — the event bus (maestro:events:{id}), control channel (maestro:ctrl:{id}), and crash reconciliation all coordinate through Redis. See Realtime-and-WebSockets.
| Workflow | Trigger | Does |
|---|---|---|
ci.yml |
push/PR to main | Backend: ruff check, ruff format --check, pytest. Frontend: lint, type-check, build. PR-only Docker build verification of both Dockerfiles. |
docker-publish.yml |
push main, tags v*
|
Builds + pushes images to GHCR. amd64 on main; amd64+arm64 on release tags (Oracle Ampere is arm64). |
deploy.yml |
tags v*, manual dispatch |
environment: production (reviewer-gated); SSHes to the host and runs compose pull && up -d at /opt/maestro. |
codeql.yml |
push/PR + weekly | SAST for Python + JS/TS. |
security.yml |
push/PR | Blocking: gitleaks, dependency-review (fail-on high). Informational: semgrep, pip-audit, npm audit, trivy-fs. |
pr-lint.yml |
PR | Enforces Conventional-Commit PR titles. |
scorecard.yml |
cron + push | OpenSSF Scorecard. |
workflow-lint.yml |
push/PR | actionlint + zizmor + merge-conflict scan. |
The migrate one-shot runs alembic upgrade head; the backend waits on service_completed_successfully, so a failed migration leaves the old backend up rather than starting a broken one.
- Install Docker (Compose v2.17+), point DNS at the host, open 80/443.
- Place three files at
/opt/maestro:docker-compose.prod.yml,Caddyfile,.env.prod(chmod 600). - Generate secrets,
docker compose -f docker-compose.prod.yml up -d. - Seed the marketplace.
- Add a cron for account purge:
0 3 * * *→python -m app.scripts.purge_deleted_accounts(see Security).
Rollback is IMAGE_TAG=x.y.z ... up -d (does not roll back the DB). Full details, Oracle-specific iptables notes, backups, and monitoring: docs/DEPLOYMENT.md.
-
CODE_EXECUTION_ENABLED=false(enabling requires the Docker socket = host takeover). - Mongo root auth +
?authSource=adminmandatory; Qdrant pinned tov1.18.2. -
/healthliveness +/health/readyreadiness (503 when degraded); backend + frontend Sentry;X-Request-ID/maestro.accessstructured logs. No Prometheus/Grafana (RAM) — uptime via external/healthchecks.
Maestro — source repository · Sustainable Use License v1.0 · This wiki documents the current code; where it differs from README.md, the wiki is authoritative.
Overview
Backend
- Backend-Reference
- API-Reference
- Database-Schema
- LLM-Providers-and-BYOK
- Security
- Billing-and-Quota
- RAG-and-Memory
- Realtime-and-WebSockets
Frontend
Operations
Project