# OpenFactory - Docker Compose # Single-command deployment: docker compose up # # Services: # - postgres: PostgreSQL 17 with pgvector extension # - redis: Redis 7 for BullMQ jobs, Hocuspocus pub/sub, session cache # - api: Fastify API server # - web: Next.js frontend # - hocuspocus: Collaborative editing WebSocket server # - nginx: Reverse proxy with WebSocket upgrade handling # # Usage: # docker compose up # Start all services # docker compose up -d # Start detached # docker compose down # Stop all services # docker compose down -v # Stop and remove volumes (data loss!) services: # -------------------------------------------------------------------------- # PostgreSQL 17 with pgvector # -------------------------------------------------------------------------- postgres: image: pgvector/pgvector:pg17 restart: unless-stopped environment: POSTGRES_USER: ${POSTGRES_USER:-openfactory} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required} POSTGRES_DB: ${POSTGRES_DB:-openfactory} ports: - "${POSTGRES_PORT:-5432}:5432" volumes: - postgres_data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-openfactory} -d ${POSTGRES_DB:-openfactory}"] interval: 5s timeout: 5s retries: 10 start_period: 10s networks: - openfactory # -------------------------------------------------------------------------- # Redis 7 - BullMQ jobs, Hocuspocus pub/sub, session cache # -------------------------------------------------------------------------- redis: image: redis:7-alpine restart: unless-stopped command: > redis-server --requirepass ${REDIS_PASSWORD:?REDIS_PASSWORD is required} --maxmemory ${REDIS_MAXMEMORY:-256mb} --maxmemory-policy allkeys-lru --appendonly yes ports: - "${REDIS_PORT:-6379}:6379" volumes: - redis_data:/data healthcheck: test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"] interval: 5s timeout: 5s retries: 10 start_period: 5s networks: - openfactory # -------------------------------------------------------------------------- # Fastify API Server # -------------------------------------------------------------------------- api: build: context: .. dockerfile: docker/Dockerfile.api restart: unless-stopped environment: NODE_ENV: ${NODE_ENV:-production} PORT: 3001 DATABASE_URL: postgresql://${POSTGRES_USER:-openfactory}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-openfactory}?sslmode=disable REDIS_URL: redis://:${REDIS_PASSWORD}@redis:6379 BETTER_AUTH_SECRET: ${BETTER_AUTH_SECRET:?BETTER_AUTH_SECRET is required} BETTER_AUTH_URL: ${BETTER_AUTH_URL:-http://localhost:3000} # LLM provider (at least one required for AI features) OPENAI_API_KEY: ${OPENAI_API_KEY:-} ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:-} OLLAMA_BASE_URL: ${OLLAMA_BASE_URL:-} OPENROUTER_API_KEY: ${OPENROUTER_API_KEY:-} LLM_PROVIDER: ${LLM_PROVIDER:-openrouter} LLM_MODEL: ${LLM_MODEL:-anthropic/claude-sonnet-4} LLM_TEMPERATURE: ${LLM_TEMPERATURE:-0.7} LLM_MAX_TOKENS: ${LLM_MAX_TOKENS:-4096} GOOGLE_GENERATIVE_AI_API_KEY: ${GOOGLE_GENERATIVE_AI_API_KEY:-} EMBEDDING_PROVIDER: ${EMBEDDING_PROVIDER:-google} EMBEDDING_MODEL: ${EMBEDDING_MODEL:-gemini-embedding-001} # GitHub App (optional, for codebase integration) GITHUB_APP_ID: ${GITHUB_APP_ID:-} GITHUB_APP_PRIVATE_KEY: ${GITHUB_APP_PRIVATE_KEY:-} GITHUB_APP_WEBHOOK_SECRET: ${GITHUB_APP_WEBHOOK_SECRET:-} GITHUB_CLIENT_ID: ${GITHUB_CLIENT_ID:-} GITHUB_CLIENT_SECRET: ${GITHUB_CLIENT_SECRET:-} # S3-compatible object storage (for artifacts) S3_ENDPOINT: ${S3_ENDPOINT:-} S3_BUCKET: ${S3_BUCKET:-openfactory} S3_ACCESS_KEY: ${S3_ACCESS_KEY:-} S3_SECRET_KEY: ${S3_SECRET_KEY:-} S3_REGION: ${S3_REGION:-us-east-1} ports: - "${API_PORT:-3001}:3001" depends_on: postgres: condition: service_healthy redis: condition: service_healthy healthcheck: test: ["CMD-SHELL", "node -e \"fetch('http://localhost:3001/health').then(r=>{if(!r.ok)process.exit(1)}).catch(()=>process.exit(1))\""] interval: 10s timeout: 5s retries: 5 start_period: 30s networks: - openfactory # -------------------------------------------------------------------------- # Next.js Web Application # -------------------------------------------------------------------------- web: build: context: .. dockerfile: docker/Dockerfile.web restart: unless-stopped environment: NODE_ENV: ${NODE_ENV:-production} PORT: 3000 NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL:-/api} NEXT_PUBLIC_WS_URL: ${NEXT_PUBLIC_WS_URL:-} NEXT_PUBLIC_HOCUSPOCUS_URL: ${NEXT_PUBLIC_HOCUSPOCUS_URL:-} ports: - "${WEB_PORT:-3000}:3000" depends_on: api: condition: service_healthy healthcheck: test: ["CMD-SHELL", "node -e \"fetch('http://localhost:3000/').then(r=>{if(!r.ok)process.exit(1)}).catch(()=>process.exit(1))\""] interval: 10s timeout: 5s retries: 5 start_period: 30s networks: - openfactory # -------------------------------------------------------------------------- # Hocuspocus Collaboration Server # -------------------------------------------------------------------------- hocuspocus: build: context: .. dockerfile: docker/Dockerfile.api restart: unless-stopped command: ["node", "dist/hocuspocus.js"] environment: NODE_ENV: ${NODE_ENV:-production} PORT: 3002 DATABASE_URL: postgresql://${POSTGRES_USER:-openfactory}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-openfactory}?sslmode=disable REDIS_URL: redis://:${REDIS_PASSWORD}@redis:6379 ports: - "${HOCUSPOCUS_PORT:-3002}:3002" depends_on: postgres: condition: service_healthy redis: condition: service_healthy healthcheck: test: ["CMD-SHELL", "node -e \"fetch('http://localhost:3002/health').then(r=>{if(!r.ok)process.exit(1)}).catch(()=>process.exit(1))\""] interval: 10s timeout: 5s retries: 5 start_period: 15s networks: - openfactory # -------------------------------------------------------------------------- # Nginx Reverse Proxy # -------------------------------------------------------------------------- nginx: image: nginx:1-alpine restart: unless-stopped ports: - "${NGINX_PORT:-80}:80" - "${NGINX_SSL_PORT:-443}:443" volumes: - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro - nginx_certs:/etc/nginx/certs:ro depends_on: api: condition: service_healthy web: condition: service_healthy healthcheck: test: ["CMD-SHELL", "wget -qO- http://127.0.0.1/health || exit 1"] interval: 10s timeout: 5s retries: 5 start_period: 10s networks: - openfactory volumes: postgres_data: driver: local redis_data: driver: local nginx_certs: driver: local networks: openfactory: driver: bridge