# Self-hosting Docker Compose — starts PostgreSQL, backend, and frontend. # # Usage: # cp .env.example .env # # Edit .env — change JWT_SECRET at minimum # docker compose -f docker-compose.selfhost.yml up -d # # Frontend: https://$TASK_DOMAIN (via Caddy reverse proxy) # Backend: internal on backend:8080 (health exposed at /health through Caddy) 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: - "${POSTGRES_PORT:-5432}:5432" volumes: - pgdata:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-multica} -d ${POSTGRES_DB:-multica}"] interval: 5s timeout: 5s retries: 5 backend: build: context: . dockerfile: Dockerfile depends_on: postgres: condition: service_healthy ports: - "127.0.0.1:${PORT:-8080}:8080" environment: DATABASE_URL: postgres://${POSTGRES_USER:-multica}:${POSTGRES_PASSWORD:-multica}@postgres:5432/${POSTGRES_DB:-multica}?sslmode=disable APP_ENV: ${APP_ENV:-} PORT: "8080" JWT_SECRET: ${JWT_SECRET:-change-me-in-production} FRONTEND_ORIGIN: ${FRONTEND_ORIGIN:-https://${TASK_DOMAIN:-localhost}} CORS_ALLOWED_ORIGINS: ${CORS_ALLOWED_ORIGINS:-} RESEND_API_KEY: ${RESEND_API_KEY:-} RESEND_FROM_EMAIL: ${RESEND_FROM_EMAIL:-noreply@multica.ai} 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:-} MULTICA_APP_URL: ${MULTICA_APP_URL:-http://localhost:3000} frontend: build: context: . dockerfile: Dockerfile.web args: REMOTE_API_URL: ${REMOTE_API_URL:-http://backend:8080} NEXT_PUBLIC_GOOGLE_CLIENT_ID: ${NEXT_PUBLIC_GOOGLE_CLIENT_ID:-} NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL:-} NEXT_PUBLIC_WS_URL: ${NEXT_PUBLIC_WS_URL:-} depends_on: - backend ports: - "127.0.0.1:${FRONTEND_PORT:-3000}:3000" environment: HOSTNAME: "0.0.0.0" NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL:-} NEXT_PUBLIC_WS_URL: ${NEXT_PUBLIC_WS_URL:-} caddy: image: caddy:2-alpine depends_on: - frontend - backend ports: - "80:80" - "443:443" environment: TASK_DOMAIN: ${TASK_DOMAIN:-localhost} volumes: - ./Caddyfile:/etc/caddy/Caddyfile:ro - caddy_data:/data - caddy_config:/config volumes: pgdata: caddy_data: caddy_config: