mirror of
https://github.com/multica-ai/multica.git
synced 2026-06-17 03:38:32 +02:00
* docs(self-host): explain loopback-only bindings + reverse proxy guidance (MUL-2360) Follow-up to #2759, which bound all docker-compose published ports to 127.0.0.1. The self-host quickstart still told cross-machine users to point their CLI at `http://<server-ip>:8080`, which no longer works (and shouldn't — the default JWT_SECRET/Postgres creds must not be reachable from the open internet). - Add a Callout to step 1 explaining the loopback-only bindings and linking to the new reverse-proxy step. - Split step 5 into 5a (same machine, defaults) and 5b (cross-machine), with a minimal Caddyfile that fronts both frontend and backend on a single hostname (including the `/ws` route with `flush_interval -1`). Switch the cross-machine `--server-url` example to `https://<domain>`. - Mirror the changes in the Chinese quickstart. - Add a header comment block to docker-compose.selfhost.yml so anyone reading the file directly understands why services don't show up on `0.0.0.0` and what to do about it. Co-authored-by: multica-agent <github@multica.ai> * docs(self-host): use nginx highlighter for Caddyfile snippet Shiki's default bundle does not include `caddy` / `caddyfile`, so Vercel's `pnpm build` failed with: ShikiError: Language `caddy` is not included in this bundle. Switch the code fence to `nginx`, which is in the default bundle and gives near-identical visual highlighting for this snippet. No content changes — the Caddyfile inside the block is untouched. Co-authored-by: multica-agent <github@multica.ai> --------- Co-authored-by: multica-agent <github@multica.ai>
107 lines
4.3 KiB
YAML
107 lines
4.3 KiB
YAML
# Self-hosting Docker Compose — starts PostgreSQL, backend, and frontend.
|
|
#
|
|
# Services bind to 127.0.0.1 only. For cross-machine or public access, front
|
|
# them with a reverse proxy (Caddy / nginx / Cloudflare Tunnel) that terminates
|
|
# TLS and forwards to 127.0.0.1:8080 (backend) and 127.0.0.1:3000 (frontend).
|
|
# Do NOT change these bindings to 0.0.0.0 — Docker bypasses host firewalls
|
|
# (UFW/iptables) by default, so the raw ports would be exposed to the internet
|
|
# with the default JWT_SECRET and Postgres credentials. See:
|
|
# apps/docs/content/docs/self-host-quickstart.mdx
|
|
#
|
|
# Usage:
|
|
# cp .env.example .env
|
|
# # Edit .env — change JWT_SECRET at minimum
|
|
# docker compose -f docker-compose.selfhost.yml up -d
|
|
#
|
|
# Frontend: http://localhost:3000
|
|
# Backend: http://localhost:8080 (also used by CLI/daemon)
|
|
|
|
name: multica
|
|
|
|
services:
|
|
postgres:
|
|
image: pgvector/pgvector:pg17
|
|
environment:
|
|
POSTGRES_DB: ${POSTGRES_DB:-multica}
|
|
POSTGRES_USER: ${POSTGRES_USER:-multica}
|
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-multica}
|
|
ports:
|
|
- "127.0.0.1:${POSTGRES_PORT:-5432}:5432"
|
|
volumes:
|
|
- pgdata:/var/lib/postgresql/data
|
|
restart: unless-stopped
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-multica} -d ${POSTGRES_DB:-multica}"]
|
|
interval: 5s
|
|
timeout: 5s
|
|
retries: 5
|
|
|
|
backend:
|
|
image: ${MULTICA_BACKEND_IMAGE:-ghcr.io/multica-ai/multica-backend}:${MULTICA_IMAGE_TAG:-latest}
|
|
depends_on:
|
|
postgres:
|
|
condition: service_healthy
|
|
ports:
|
|
- "127.0.0.1:${PORT:-8080}:8080"
|
|
volumes:
|
|
- backend_uploads:/app/data/uploads
|
|
environment:
|
|
DATABASE_URL: postgres://${POSTGRES_USER:-multica}:${POSTGRES_PASSWORD:-multica}@postgres:5432/${POSTGRES_DB:-multica}?sslmode=disable
|
|
PORT: "8080"
|
|
METRICS_ADDR: ${METRICS_ADDR:-}
|
|
JWT_SECRET: ${JWT_SECRET:-change-me-in-production}
|
|
FRONTEND_ORIGIN: ${FRONTEND_ORIGIN:-http://localhost:3000}
|
|
CORS_ALLOWED_ORIGINS: ${CORS_ALLOWED_ORIGINS:-}
|
|
RESEND_API_KEY: ${RESEND_API_KEY:-}
|
|
RESEND_FROM_EMAIL: ${RESEND_FROM_EMAIL:-noreply@multica.ai}
|
|
SMTP_HOST: ${SMTP_HOST:-}
|
|
SMTP_PORT: ${SMTP_PORT:-25}
|
|
SMTP_USERNAME: ${SMTP_USERNAME:-}
|
|
SMTP_PASSWORD: ${SMTP_PASSWORD:-}
|
|
SMTP_TLS_INSECURE: ${SMTP_TLS_INSECURE:-false}
|
|
GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID:-}
|
|
GOOGLE_CLIENT_SECRET: ${GOOGLE_CLIENT_SECRET:-}
|
|
GOOGLE_REDIRECT_URI: ${GOOGLE_REDIRECT_URI:-http://localhost:3000/auth/callback}
|
|
S3_BUCKET: ${S3_BUCKET:-}
|
|
S3_REGION: ${S3_REGION:-us-west-2}
|
|
CLOUDFRONT_DOMAIN: ${CLOUDFRONT_DOMAIN:-}
|
|
CLOUDFRONT_KEY_PAIR_ID: ${CLOUDFRONT_KEY_PAIR_ID:-}
|
|
CLOUDFRONT_PRIVATE_KEY: ${CLOUDFRONT_PRIVATE_KEY:-}
|
|
COOKIE_DOMAIN: ${COOKIE_DOMAIN:-}
|
|
APP_ENV: ${APP_ENV:-production}
|
|
MULTICA_DEV_VERIFICATION_CODE: ${MULTICA_DEV_VERIFICATION_CODE:-}
|
|
MULTICA_APP_URL: ${MULTICA_APP_URL:-http://localhost:3000}
|
|
ALLOW_SIGNUP: ${ALLOW_SIGNUP:-true}
|
|
ALLOWED_EMAILS: ${ALLOWED_EMAILS:-}
|
|
ALLOWED_EMAIL_DOMAINS: ${ALLOWED_EMAIL_DOMAINS:-}
|
|
GITHUB_APP_SLUG: ${GITHUB_APP_SLUG:-}
|
|
GITHUB_WEBHOOK_SECRET: ${GITHUB_WEBHOOK_SECRET:-}
|
|
# Public URL the API is reachable at from the open internet, no
|
|
# trailing slash. Used to mint absolute webhook URLs for autopilot
|
|
# webhook triggers. Leave unset behind a same-origin reverse proxy
|
|
# (e.g. plain localhost dev); the frontend will compose the URL
|
|
# from window.origin + webhook_path in that case. Headers are
|
|
# intentionally NOT used to derive this value, to avoid Host /
|
|
# X-Forwarded-Host spoofing on misconfigured proxies.
|
|
MULTICA_PUBLIC_URL: ${MULTICA_PUBLIC_URL:-}
|
|
# Comma-separated CIDRs whose source IP is allowed to set
|
|
# X-Forwarded-For / X-Real-IP for the webhook per-IP rate limiter.
|
|
# Empty default = headers ignored, RemoteAddr used. Set e.g.
|
|
# "127.0.0.1/32" when running behind a same-host reverse proxy.
|
|
MULTICA_TRUSTED_PROXIES: ${MULTICA_TRUSTED_PROXIES:-}
|
|
restart: unless-stopped
|
|
|
|
frontend:
|
|
image: ${MULTICA_WEB_IMAGE:-ghcr.io/multica-ai/multica-web}:${MULTICA_IMAGE_TAG:-latest}
|
|
depends_on:
|
|
- backend
|
|
ports:
|
|
- "127.0.0.1:${FRONTEND_PORT:-3000}:3000"
|
|
environment:
|
|
HOSTNAME: "0.0.0.0"
|
|
restart: unless-stopped
|
|
|
|
volumes:
|
|
pgdata:
|
|
backend_uploads:
|