name: agentrove services: redis: image: redis:7-alpine restart: unless-stopped command: ["redis-server", "--appendonly", "yes"] volumes: - redis_data:/data healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 5s timeout: 5s retries: 5 networks: default: # Coolify renames services to redis-pr-N on preview deploys; keep the # stable "redis" DNS name so REDIS_URL and other clients still resolve. aliases: - redis sandbox-setup: image: docker:27-cli restart: "no" volumes: - /var/run/docker.sock:/var/run/docker.sock command: ["sh", "-c", "docker pull $${DOCKER_IMAGE}"] environment: DOCKER_IMAGE: ${DOCKER_IMAGE:-ghcr.io/mng-dev-ai/agentrove-sandbox:latest} api: build: context: . dockerfile: backend/Dockerfile restart: unless-stopped command: ["api"] user: root privileged: true group_add: - "0" depends_on: redis: condition: service_healthy sandbox-setup: condition: service_completed_successfully volumes: # Absolute path → host bind (default keeps production data at # /data/agentrove/storage). Non-path name → named volume (set # AGENTROVE_STORAGE_SOURCE=agentrove_storage in Coolify preview env # so each PR gets isolated SQLite/storage). - ${AGENTROVE_STORAGE_SOURCE:-/data/agentrove/storage}:/data/agentrove/storage - /var/run/docker.sock:/var/run/docker.sock environment: ENVIRONMENT: production DATABASE_URL: sqlite+aiosqlite:////data/agentrove/storage/agentrove.db REDIS_URL: redis://${SERVICE_NAME_REDIS:-redis}:6379/0 SECRET_KEY: ${SECRET_KEY:?Set SECRET_KEY to a 32+ character value before starting production} SESSION_SECRET_KEY: ${SESSION_SECRET_KEY:-} # Set in Coolify preview env so each fresh preview DB gets your superuser # (no-op once a superuser exists). ADMIN_EMAIL: ${ADMIN_EMAIL:-} ADMIN_USERNAME: ${ADMIN_USERNAME:-admin} ADMIN_PASSWORD: ${ADMIN_PASSWORD:-} REGISTRATION_OPEN: ${REGISTRATION_OPEN:-false} STORAGE_PATH: /data/agentrove/storage DOCKER_IMAGE: ${DOCKER_IMAGE:-ghcr.io/mng-dev-ai/agentrove-sandbox:latest} # Empty → entrypoint joins/creates the compose sandbox network for this stack # (required for PR previews; a fixed name collides with production). DOCKER_NETWORK: ${DOCKER_NETWORK:-} LOG_LEVEL: ${LOG_LEVEL:-INFO} PYTHONUNBUFFERED: "1" # Prefer Coolify SERVICE_URL_WEB_80 so PR previews get the right origin without # hardcoding production APP_URL into preview env. ALLOWED_ORIGINS: ${ALLOWED_ORIGINS:-${SERVICE_URL_WEB_80:-http://localhost,http://127.0.0.1}} BASE_URL: ${APP_URL:-${SERVICE_URL_WEB_80:-http://localhost}} FRONTEND_URL: ${APP_URL:-${SERVICE_URL_WEB_80:-http://localhost}} # Coolify/Traefik (and the web nginx container) sit in front of the API. # Without this, ProxyHeadersMiddleware ignores X-Forwarded-Proto and # SQLAdmin emits http:// asset URLs on an https:// page (unstyled admin). TRUSTED_PROXY_HOSTS: ${TRUSTED_PROXY_HOSTS:-*} networks: default: aliases: - api sandbox: aliases: - api healthcheck: test: ["CMD", "curl", "-fsS", "http://localhost:8080/health"] interval: 10s timeout: 5s retries: 12 start_period: 30s web: build: context: ./frontend dockerfile: Dockerfile.production restart: unless-stopped depends_on: api: condition: service_healthy environment: # Coolify renames api → api-pr-N; nginx proxies to this hostname. API_HOST: ${SERVICE_NAME_API:-api} SERVICE_URL_WEB_80: ${SERVICE_URL_WEB_80:-} expose: - "80" healthcheck: test: ["CMD", "wget", "-q", "--spider", "http://127.0.0.1/"] interval: 10s timeout: 5s retries: 12 networks: # No fixed `name:` — Coolify / compose project isolation gives each deploy # (production + every PR preview) its own sandbox network. sandbox: volumes: redis_data: # Used when AGENTROVE_STORAGE_SOURCE=agentrove_storage (preview deploys). agentrove_storage: