name: agentforge services: postgres: image: postgres:16-alpine restart: unless-stopped environment: POSTGRES_DB: ${POSTGRES_DB:-agentforge} POSTGRES_USER: ${POSTGRES_USER:-agentforge} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-agentforge-local} ports: - "127.0.0.1:${POSTGRES_PORT:-5432}:5432" volumes: - postgres_data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"] interval: 5s timeout: 5s retries: 10 start_period: 5s networks: - data redis: image: redis:7.4-alpine restart: unless-stopped command: ["redis-server", "--appendonly", "yes", "--save", "60", "1"] ports: - "127.0.0.1:${REDIS_PORT:-6379}:6379" volumes: - redis_data:/data healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 5s timeout: 3s retries: 10 networks: - data backend: build: context: ./backend restart: unless-stopped init: true environment: APP_ENV: ${APP_ENV:-development} LOG_LEVEL: ${LOG_LEVEL:-INFO} DATABASE_URL: ${DATABASE_URL:-postgresql+asyncpg://agentforge:agentforge-local@postgres:5432/agentforge} REDIS_URL: ${REDIS_URL:-redis://redis:6379/0} SECRET_KEY: ${SECRET_KEY:-local-development-secret-key-change-before-production} JWT_SECRET: ${JWT_SECRET:-local-development-jwt-secret-change-before-production} JWT_ALGORITHM: ${JWT_ALGORITHM:-HS256} ACCESS_TOKEN_EXPIRE_MINUTES: ${ACCESS_TOKEN_EXPIRE_MINUTES:-15} REFRESH_TOKEN_EXPIRE_MINUTES: ${REFRESH_TOKEN_EXPIRE_MINUTES:-10080} AUTH_MODE: ${AUTH_MODE:-local} KEYCLOAK_URL: ${KEYCLOAK_URL:-http://localhost:8080} KEYCLOAK_REALM: ${KEYCLOAK_REALM:-agentforge} KEYCLOAK_CLIENT_ID: ${KEYCLOAK_CLIENT_ID:-agentforge-web} KEYCLOAK_CLIENT_SECRET: ${KEYCLOAK_CLIENT_SECRET:-} KEYCLOAK_REDIRECT_URI: ${KEYCLOAK_REDIRECT_URI:-http://localhost:3000/auth/callback} KEYCLOAK_VALIDATE_TOKENS: ${KEYCLOAK_VALIDATE_TOKENS:-true} MASTER_ENCRYPTION_KEY: ${MASTER_ENCRYPTION_KEY:-MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY=} CORS_ORIGINS: '${CORS_ORIGINS:-["http://localhost","http://localhost:3000"]}' DEPLOYMENT_IMAGE_TAG: ${DEPLOYMENT_IMAGE_TAG:-agentforge-agent:latest} DEPLOYMENT_DOCKER_NETWORK: ${DEPLOYMENT_DOCKER_NETWORK:-agentforge_app} LIVEKIT_URL: ${LIVEKIT_URL:-ws://livekit:7880} LIVEKIT_API_KEY: ${LIVEKIT_API_KEY:-} LIVEKIT_API_SECRET: ${LIVEKIT_API_SECRET:-} LIVEKIT_AGENT_NAME: ${LIVEKIT_AGENT_NAME:-agentforge-voice} LIVEKIT_TOKEN_TTL_MINUTES: ${LIVEKIT_TOKEN_TTL_MINUTES:-15} LIVEKIT_ROOM_EMPTY_TIMEOUT: ${LIVEKIT_ROOM_EMPTY_TIMEOUT:-300} LIVEKIT_AUTO_DISPATCH: ${LIVEKIT_AUTO_DISPATCH:-true} DEEPGRAM_API_KEY: ${DEEPGRAM_API_KEY:-} ASSEMBLYAI_API_KEY: ${ASSEMBLYAI_API_KEY:-} ELEVENLABS_API_KEY: ${ELEVENLABS_API_KEY:-} SPEECHIFY_API_KEY: ${SPEECHIFY_API_KEY:-} LANGSMITH_TRACING: ${LANGSMITH_TRACING:-false} LANGSMITH_API_KEY: ${LANGSMITH_API_KEY:-} LANGSMITH_PROJECT: ${LANGSMITH_PROJECT:-agentforge} LANGSMITH_ENDPOINT: ${LANGSMITH_ENDPOINT:-https://api.smith.langchain.com} LANGTRACE_TRACING: ${LANGTRACE_TRACING:-false} LANGTRACE_API_HOST: ${LANGTRACE_API_HOST:-http://localhost:3000} LANGTRACE_API_KEY: ${LANGTRACE_API_KEY:-} LANGTRACE_PROJECT: ${LANGTRACE_PROJECT:-agentforge} LANGTRACE_SERVICE_NAME: ${LANGTRACE_SERVICE_NAME:-agentforge} group_add: - "${DOCKER_GID:-998}" volumes: - /var/run/docker.sock:/var/run/docker.sock expose: - "8000" depends_on: postgres: condition: service_healthy redis: condition: service_healthy healthcheck: test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3)"] interval: 10s timeout: 5s retries: 5 start_period: 20s networks: - app - data frontend: build: context: ./frontend args: NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL:-/api/v1} restart: unless-stopped init: true environment: NODE_ENV: production NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL:-/api/v1} expose: - "3000" depends_on: backend: condition: service_healthy healthcheck: test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:3000').then(r=>{if(!r.ok)process.exit(1)}).catch(()=>process.exit(1))"] interval: 10s timeout: 5s retries: 5 start_period: 20s networks: - app caddy: image: caddy:2.9-alpine restart: unless-stopped ports: - "${APP_PORT:-80}:80" volumes: - ./infra/Caddyfile:/etc/caddy/Caddyfile:ro - caddy_data:/data - caddy_config:/config depends_on: backend: condition: service_healthy frontend: condition: service_healthy healthcheck: test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://127.0.0.1/health"] interval: 10s timeout: 5s retries: 5 networks: - app volumes: postgres_data: redis_data: caddy_data: caddy_config: networks: app: driver: bridge data: driver: bridge