# Local/dev deployment: API + Postgres. For a single-node SQLite deploy, drop the # `db` service and DATABASE_URL override — the image defaults to SQLite on the # handler-data volume. services: api: image: ghcr.io/0xwheatyz/handler:latest build: . ports: - "8000:8000" environment: DATABASE_URL: postgresql+psycopg://handler:handler@db:5432/handler # Required — the API refuses to start without it. Set in .env or the shell. AUTH_TOKEN: ${AUTH_TOKEN:?set AUTH_TOKEN in .env or the environment} SHARED_CONTEXT_WRITE_TOKEN: ${SHARED_CONTEXT_WRITE_TOKEN:-} WEBHOOK_URL: ${WEBHOOK_URL:-} UI_ENABLED: ${UI_ENABLED:-true} CORS_ORIGINS: ${CORS_ORIGINS:-} volumes: - handler-data:/var/lib/handler depends_on: db: condition: service_healthy restart: unless-stopped # Control layer: the `handler` CLI running the CI poller loop. Shares the database and # the handler-data volume with the API. It waits for the API (which owns migrations), # so RUN_MIGRATIONS is off here to avoid a startup race. Run one-shot control commands # against the same image with, e.g., `docker compose run --rm control handler list`. # Live agent spawning also needs `git`/`tmux` (baked in) plus bring-your-own # `claude`/`forge` binaries — layer or mount those in. control: image: ghcr.io/0xwheatyz/handler/control:latest build: context: . dockerfile: Dockerfile.control environment: DATABASE_URL: postgresql+psycopg://handler:handler@db:5432/handler RUN_MIGRATIONS: "false" PROJECTS_ROOT: /var/lib/handler/projects # Per-project forge credentials are resolved from credential_ref pointers at spawn; # export the referenced vars here when spawning agents from this container. FORGE_VERSION: ${FORGE_VERSION:-} volumes: - handler-data:/var/lib/handler depends_on: db: condition: service_healthy api: condition: service_healthy restart: unless-stopped db: image: postgres:16-alpine environment: POSTGRES_USER: handler POSTGRES_PASSWORD: handler POSTGRES_DB: handler volumes: - postgres-data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U handler -d handler"] interval: 5s timeout: 3s retries: 10 restart: unless-stopped volumes: handler-data: postgres-data: