x-kajet-http-healthcheck: &kajet-http-healthcheck test: [ "CMD", "uv", "run", "--no-dev", "python", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/readyz', timeout=2).read()", ] interval: 30s timeout: 5s retries: 5 start_period: 30s services: # Single host-facing entrypoint (Caddy). Kept under the name "kajet-turbo" so # Coolify's existing domain->service binding (it keys Traefik labels by service # name) keeps routing the FQDN here after the split. The MCP/API path split # happens internally (Caddyfile), not in host config. kajet-turbo: build: context: . dockerfile: Dockerfile.caddy expose: - "8000" depends_on: kajet-api: condition: service_healthy kajet-mcp: condition: service_healthy healthcheck: test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:8000/healthz >/dev/null"] interval: 30s timeout: 5s retries: 5 start_period: 10s # Stateless REST API + SPA. Scales to any worker count. kajet-api: build: . image: kajet-turbo:local env_file: .env environment: KAJET_ROLE: api API_WORKERS: "2" volumes: - kajet_data:/data - kajet_workspaces:/workspaces healthcheck: *kajet-http-healthcheck # Stateful MCP. Single process (enforced in code) so sessions stay coherent # and ctx.sample()/elicitation keep working. Shares the DB + workspaces with # the api role; api runs migrations first. kajet-mcp: image: kajet-turbo:local depends_on: kajet-api: condition: service_healthy env_file: .env environment: KAJET_ROLE: mcp volumes: - kajet_data:/data - kajet_workspaces:/workspaces healthcheck: *kajet-http-healthcheck # Background worker: drains the SQLite job queue (auto-push, future embeddings). # No HTTP. Shares the DB (jobs/remotes) + workspaces (git push) with api/mcp; # depends on api so migrations run first. Decrypts SSH keys to /dev/shm and # writes the TOFU known_hosts under /data/ssh (self-created on first push). # Horizontally scalable (claims are atomic + same-workspace pushes serialized # at the queue level), but one replica is plenty at current volume. kajet-worker: image: kajet-turbo:local depends_on: kajet-api: condition: service_healthy env_file: .env environment: KAJET_ROLE: worker KAJET_WORKER_CONCURRENCY: "4" KAJET_WORKER_POLL_INTERVAL: "1" volumes: - kajet_data:/data - kajet_workspaces:/workspaces volumes: kajet_data: kajet_workspaces: