Submatter/docker/docker-compose.prod.yml
Randall Stillwell 9bde5960bf
Some checks failed
CI / Lint, type-check, test (push) Has been cancelled
Deploy homelab (CT 116) / Deploy to CT 116 (push) Has been cancelled
fix(deploy): collab URL, CT 116 prod compose, Forgejo migration
- Read NEXT_PUBLIC_COLLAB_SERVER_URL in yjs provider; bake NEXT_PUBLIC_* at Docker build
- Add prod compose, deploy scripts, CT 116 proxmox scaffolding
- Forgejo Actions CI/deploy; migrate-origin and post-migration scripts
2026-09-08 22:33:59 -05:00

244 lines
7.7 KiB
YAML

# Echodo production stack — shared CT 102 Postgres/Redis, multi-service build.
#
# Phase A (default): web, collab, mcp, orchestrator, worker
# Phase B (optional): OpenHands agent-server — enable with profile `agent-server`
#
# Deploy targets:
# - Dokploy (CT 112): compose file path `docker/docker-compose.prod.yml`, project dir = repo root
# - Standalone LXC: `docker compose -f docker/docker-compose.prod.yml up -d`
#
# Domains (Dokploy UI or CT 100 Traefik):
# web → echodo.stillwell.cloud (port 3000)
# collab → collab.echodo.stillwell.cloud (port 1234)
#
# Build (required on ≤8 GB hosts — parallel export OOMs with exit 255):
# bash scripts/build-prod.sh
# Dokploy → Advanced → Custom Build Command: bash scripts/build-prod.sh
#
# Phase B:
# docker compose -f docker/docker-compose.prod.yml --profile agent-server up -d
# Set WORKER_SESSION_TYPE=openhands and matching SESSION_API_KEY / LOCAL_BACKEND_API_KEY.
services:
web:
build:
context: .
dockerfile: docker/Dockerfile
target: web
args:
NEXT_PUBLIC_COLLAB_SERVER_URL: ${NEXT_PUBLIC_COLLAB_SERVER_URL:-ws://localhost:1234}
NEXT_PUBLIC_UMAMI_SCRIPT: ${NEXT_PUBLIC_UMAMI_SCRIPT:-}
NEXT_PUBLIC_UMAMI_WEBSITE_ID: ${NEXT_PUBLIC_UMAMI_WEBSITE_ID:-}
NEXT_PUBLIC_LIBREDESK_URL: ${NEXT_PUBLIC_LIBREDESK_URL:-}
NEXT_PUBLIC_LIBREDESK_WIDGET_ID: ${NEXT_PUBLIC_LIBREDESK_WIDGET_ID:-}
NEXT_PUBLIC_DIRECTUS_URL: ${NEXT_PUBLIC_DIRECTUS_URL:-}
depends_on:
collab:
condition: service_started
mcp:
condition: service_started
restart: unless-stopped
environment:
- DATABASE_URL=${DATABASE_URL}
- REDIS_URL=${REDIS_URL}
- NEXTAUTH_URL=${NEXTAUTH_URL}
- NEXTAUTH_SECRET=${NEXTAUTH_SECRET}
- AUTH_SECRET=${AUTH_SECRET}
- AUTH_DEV_PASSWORD=${AUTH_DEV_PASSWORD}
- AUTH_GITHUB_ID=${AUTH_GITHUB_ID}
- AUTH_GITHUB_SECRET=${AUTH_GITHUB_SECRET}
- AUTH_GOOGLE_ID=${AUTH_GOOGLE_ID}
- AUTH_GOOGLE_SECRET=${AUTH_GOOGLE_SECRET}
- AUTH_AUTHENTIK_ID=${AUTH_AUTHENTIK_ID}
- AUTH_AUTHENTIK_SECRET=${AUTH_AUTHENTIK_SECRET}
- AUTH_AUTHENTIK_ISSUER=${AUTH_AUTHENTIK_ISSUER}
- COLLAB_SERVER_URL=${COLLAB_SERVER_URL}
- NEXT_PUBLIC_COLLAB_SERVER_URL=${NEXT_PUBLIC_COLLAB_SERVER_URL}
- NEXT_PUBLIC_UMAMI_SCRIPT=${NEXT_PUBLIC_UMAMI_SCRIPT}
- NEXT_PUBLIC_UMAMI_WEBSITE_ID=${NEXT_PUBLIC_UMAMI_WEBSITE_ID}
- NEXT_PUBLIC_LIBREDESK_URL=${NEXT_PUBLIC_LIBREDESK_URL}
- NEXT_PUBLIC_LIBREDESK_WIDGET_ID=${NEXT_PUBLIC_LIBREDESK_WIDGET_ID}
- NEXT_PUBLIC_DIRECTUS_URL=${NEXT_PUBLIC_DIRECTUS_URL}
- DIRECTUS_FEEDBACK_TOKEN=${DIRECTUS_FEEDBACK_TOKEN}
- OPENAI_API_KEY=${OPENAI_API_KEY}
- OPENAI_BASE_URL=${OPENAI_BASE_URL}
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
- OMNIROUTE_BASE_URL=${OMNIROUTE_BASE_URL:-}
- OMNIROUTE_API_KEY=${OMNIROUTE_API_KEY:-}
- GITHUB_TOKEN=${GITHUB_TOKEN:-}
- FORGEJO_TOKEN=${FORGEJO_TOKEN:-}
- FORGE_WEBHOOK_SECRET=${FORGE_WEBHOOK_SECRET:-}
- NODE_ENV=production
- NEXT_TELEMETRY_DISABLED=1
expose:
- "3000"
healthcheck:
test:
- CMD-SHELL
- "wget -qO- --timeout=3 http://127.0.0.1:3000/ >/dev/null || exit 1"
interval: 30s
timeout: 5s
retries: 3
start_period: 90s
networks:
- echodo
collab:
build:
context: .
dockerfile: docker/Dockerfile
target: collab
restart: unless-stopped
environment:
- DATABASE_URL=${DATABASE_URL}
- REDIS_URL=${REDIS_URL}
- PORT=1234
- NODE_ENV=production
expose:
- "1234"
healthcheck:
test:
- CMD-SHELL
- 'node -e "const p=+(process.env.PORT||1234);require(''net'').createConnection(p,''127.0.0.1'').on(''connect'',()=>process.exit(0)).on(''error'',()=>process.exit(1))"'
interval: 30s
timeout: 5s
retries: 3
start_period: 25s
networks:
- echodo
mcp:
build:
context: .
dockerfile: docker/Dockerfile
target: mcp
args:
MCP_SERVER_PORT: ${MCP_SERVER_PORT:-3001}
restart: unless-stopped
environment:
- DATABASE_URL=${DATABASE_URL}
- MCP_SERVER_PORT=${MCP_SERVER_PORT:-3001}
- NODE_ENV=production
stdin_open: true
expose:
- "${MCP_SERVER_PORT:-3001}"
healthcheck:
test:
- CMD-SHELL
- "pgrep -f 'node.*dist/index\\.js' > /dev/null || exit 1"
interval: 30s
timeout: 5s
retries: 3
start_period: 25s
networks:
- echodo
orchestrator:
build:
context: .
dockerfile: docker/Dockerfile
target: orchestrator
restart: unless-stopped
environment:
- DATABASE_URL=${DATABASE_URL}
- REDIS_URL=${REDIS_URL}
- ORCHESTRATOR_DRY_RUN=${ORCHESTRATOR_DRY_RUN:-0}
- ORCHESTRATOR_POLL_MS=${ORCHESTRATOR_POLL_MS:-5000}
- ORCHESTRATOR_STALL_MINUTES=${ORCHESTRATOR_STALL_MINUTES:-120}
- ORCHESTRATOR_MAX_IMPLEMENTERS=${ORCHESTRATOR_MAX_IMPLEMENTERS:-2}
- NODE_ENV=production
healthcheck:
test:
- CMD-SHELL
- "pgrep -f 'node.*dist/index\\.js' > /dev/null || exit 1"
interval: 30s
timeout: 5s
retries: 3
start_period: 25s
networks:
- echodo
worker:
build:
context: .
dockerfile: docker/Dockerfile
target: worker
depends_on:
orchestrator:
condition: service_started
restart: unless-stopped
environment:
- DATABASE_URL=${DATABASE_URL}
- REDIS_URL=${REDIS_URL}
- WORKER_DRY_RUN=${WORKER_DRY_RUN:-0}
- WORKER_BLPOP_TIMEOUT_S=${WORKER_BLPOP_TIMEOUT_S:-5}
- WORKER_MAX_TOOL_STEPS=${WORKER_MAX_TOOL_STEPS:-12}
- WORKER_REPO_ROOT=/app/repo
- WORKER_WORKTREE_BASE=/tmp/echodo-worker-worktrees
- WORKER_SESSION_TYPE=${WORKER_SESSION_TYPE:-minimal}
- WORKER_OPENHANDS_WORKSPACE=${WORKER_OPENHANDS_WORKSPACE:-/workspace/repo}
- AGENT_SERVER_URL=${AGENT_SERVER_URL:-http://agent-server:8001}
- SESSION_API_KEY=${SESSION_API_KEY:-}
- OPENAI_API_KEY=${OPENAI_API_KEY}
- OPENAI_BASE_URL=${OPENAI_BASE_URL}
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
- OMNIROUTE_BASE_URL=${OMNIROUTE_BASE_URL:-}
- OMNIROUTE_API_KEY=${OMNIROUTE_API_KEY:-}
- GITHUB_TOKEN=${GITHUB_TOKEN:-}
- FORGEJO_TOKEN=${FORGEJO_TOKEN:-}
- NODE_ENV=production
volumes:
- echodo-workspace:/workspace
healthcheck:
test:
- CMD-SHELL
- "pgrep -f 'node.*dist/index\\.js' > /dev/null || exit 1"
interval: 30s
timeout: 5s
retries: 3
start_period: 25s
networks:
- echodo
agent-server:
profiles: ["agent-server"]
image: ghcr.io/open-hands/agent-server:0.19.0
restart: unless-stopped
environment:
- AGENT_SERVER_HOST=0.0.0.0
- AGENT_SERVER_PORT=8001
- LOCAL_BACKEND_API_KEY=${LOCAL_BACKEND_API_KEY}
- LLM_MODEL=${OMNIROUTE_MODEL:-auto/coding}
- LLM_API_KEY=${OMNIROUTE_API_KEY}
- LLM_BASE_URL=${OMNIROUTE_BASE_URL}
- SANDBOX_TYPE=docker
- SANDBOX_DOCKER_IMAGE=ghcr.io/open-hands/sandbox:latest
- SANDBOX_TIMEOUT=300
- WORKSPACE_BASE=/workspace
volumes:
- echodo-workspace:/workspace
- /var/run/docker.sock:/var/run/docker.sock
expose:
- "8001"
healthcheck:
test:
- CMD-SHELL
- "wget -qO- --timeout=3 http://127.0.0.1:8001/server_info >/dev/null || exit 1"
interval: 30s
timeout: 10s
retries: 3
start_period: 15s
deploy:
resources:
limits:
memory: 2G
cpus: "2"
networks:
- echodo
volumes:
echodo-workspace:
networks:
echodo:
driver: bridge