Two compose files, one Helm chart.
Local dev runs on Docker Compose, split into a worker stack (Postgres, Redis, API, MCP) and a Langfuse stack (web, ClickHouse, MinIO, its own Postgres + Redis). Production is a single Helm chart with ~30 templates deployed to AKS.
Two compose files — why
The Langfuse stack is heavy. Splitting it out has two reasons:
- Optional in dev. Skip Langfuse to save ~6 GB of RAM and ~2 minutes of cold start. The API uses
NoopTracewhen the keys are absent. - Shared by parallel workers. A single Langfuse instance serves every worker stack on the host (see §08.2).
The worker stack
backend/docker-compose.yml — services on the worker network:
- agenticit-postgres
- Postgres 16-alpine. Host port
${POSTGRES_HOST_PORT:-5433}:5432. Volumepgdata. Healthcheck viapg_isready. - agenticit-redis
- Redis 7-alpine. Internal only — no host port.
- agenticit-api
- Built from
backend/Dockerfile. Host port${API_HOST_PORT:-8000}:8000. ~200 env vars (LLM, OAuth, MCP entries, Langfuse, feature flags). Volumeappdata:/data. Joined todefault+langfuse-net. - mcp-service
- Built from
../mcp-service/backend/Dockerfile.mcp. Host port${MCP_HOST_PORT:-8001}:8001. Health endpoint/healthwith 90 s startup grace. Reads same Postgres for token store.
The Langfuse stack
backend/docker-compose.langfuse.yml
langfuse-postgres— separate Postgres for Langfuse data only.langfuse-redis— separate Redis (cache / queue), unrelated to the API's Redis.langfuse-clickhouse— OLAP store for spans + observations.langfuse-minio+langfuse-minio-create-bucket— S3-compatible blob store for traces, media uploads.langfuse-web— Langfuse 3 (Next.js). Host port3010 → 3000. UI plus the OpenTelemetry receiver at/api/public/otel.langfuse-worker— async event processor.
Network: langfuse-net. The worker stack joins it as external: true; setup-worker.sh creates the network if it doesn't exist so workers don't fail when Langfuse isn't running.
Override files
Local secrets live in backend/docker-compose.override.yml — gitignored. Provides the required env vars for Postgres credentials, JWT signing, OpenRouter, OAuth, token encryption, Tavily, and optional Langfuse keys. Full reference: §10·Env vars →.
If backend/docker-compose.override.yml contains Langfuse service entries (langfuse-web, langfuse-clickhouse, …) it is in the old single-file format and must be split. See docs/guides/docker-compose-migration.md.
Configuration sources & precedence
- OS env vars — highest priority. Read after
AddEnvironmentVariables(). - .env file — loaded by
EnvironmentVariableMapper.LoadDotEnvFile()only if not already in OS env (no overwrite). - Python-style flat env vars —
EnvironmentVariableMapper.AddPythonEnvVars()maps flatOPENROUTER_API_KEYto hierarchicalLlm:AuthTokenvia a static map. - appsettings.json — fallback defaults loaded by ASP.NET.
The flat-env-var convention means Docker / Kubernetes / shell exports all use the same keys. The hierarchical mapping happens once, in code, in one place.