Skip to content

Docker Deployment

The Imbi Docker image packages all services into a single container that can run everything together or individual services for scaled-out deployments.

All-in-One Mode

By default, the container starts all services behind a Caddy reverse proxy:

docker run -p 8080:8080 \
  -e CLICKHOUSE_URL=clickhouse+http://default:password@clickhouse:8123/imbi \
  -e IGGY_URL=iggy+tcp://iggy:iggy@iggy:8090 \
  -e POSTGRES_URL=postgresql://user:pass@postgres/imbi \
  -e IMBI_AUTH_JWT_SECRET=your-secret \
  -e IMBI_AUTH_ENCRYPTION_KEY=your-key \
  ghcr.io/aweber-imbi/imbi:latest

This starts:

Port Service
8080 Caddy (public, routes to all services)
8000 imbi-api (internal)
8001 imbi-mcp (internal)
8002 imbi-assistant (internal)
8003 imbi-gateway (internal)
8005 imbi-scheduler (internal, started only when IMBI_SCHEDULER_SA_CLIENT_ID and IMBI_SCHEDULER_SA_CLIENT_SECRET are set)
8004 imbi-slackbot (internal, started only when the Slack tokens and ANTHROPIC_API_KEY are set)

Caddy serves imbi-scheduler under /scheduler, stripping that prefix before the request reaches it — so /scheduler/api/tasks hits /api/tasks on the service and /scheduler/status reaches its unprefixed health endpoint.

Individual Services

For production deployments where you want to scale services independently, set IMBI_SERVICE to one of api, assistant, gateway, mcp, scheduler, slackbot, or ui:

# Run only the API
docker run -p 8000:8000 \
  -e IMBI_SERVICE=api \
  -e CLICKHOUSE_URL=clickhouse+http://default:password@clickhouse:8123/imbi \
  -e IGGY_URL=iggy+tcp://iggy:iggy@iggy:8090 \
  -e IMBI_AUTH_JWT_SECRET=your-secret \
  -e IMBI_AUTH_ENCRYPTION_KEY=your-key \
  ghcr.io/aweber-imbi/imbi:latest

When running individual services, Caddy is not started. Either provide your own reverse proxy, or run IMBI_SERVICE=ui: the bundled Caddy alone, serving the UI on :8080 and proxying to the other containers. Each upstream defaults to the in-pod loopback, so point them at the containers by name:

docker run -p 8080:8080 \
  -e IMBI_SERVICE=ui \
  -e VITE_API_URL=http://localhost:8080/api \
  -e IMBI_API_UPSTREAM=imbi-api:8000 \
  -e IMBI_MCP_UPSTREAM=imbi-mcp:8001 \
  -e IMBI_ASSISTANT_UPSTREAM=imbi-assistant:8002 \
  -e IMBI_GATEWAY_UPSTREAM=imbi-gateway:8003 \
  -e IMBI_SCHEDULER_UPSTREAM=imbi-scheduler:8005 \
  ghcr.io/aweber-imbi/imbi:latest

Running the scheduler on its own needs the service-account credentials and both API URLs; the entrypoint refuses to start without them:

docker run -p 8005:8005 \
  -e IMBI_SERVICE=scheduler \
  -e CLICKHOUSE_URL=clickhouse+http://default:password@clickhouse:8123/imbi \
  -e IGGY_URL=iggy+tcp://iggy:iggy@iggy:8090 \
  -e POSTGRES_URL=postgresql://user:pass@postgres/imbi \
  -e IMBI_AUTH_JWT_SECRET=your-secret \
  -e IMBI_SCHEDULER_SA_CLIENT_ID=... \
  -e IMBI_SCHEDULER_SA_CLIENT_SECRET=... \
  -e IMBI_INTERNAL_API_URL=http://imbi-api:8000 \
  -e IMBI_API_URL=https://imbi.example.com/api \
  ghcr.io/aweber-imbi/imbi:latest

If the reverse proxy in front of it passes /scheduler through instead of stripping it, set IMBI_SCHEDULER_API_PREFIX=/scheduler/api. More than one replica is safe — see Scheduler configuration.

The Iggy connectors runtime

The ClickHouse write path runs through Apache Iggy, and it is the connectors runtime that drains each stream into its table (see ADR 0019, Apache Iggy message streaming). It runs from the Iggy image, not the Imbi one, and carries no sink configuration of its own: it fetches that from imbi-api once at startup and never re-reads it.

Because that response carries the ClickHouse credentials, imbi-api serves it only to a caller presenting a key from IMBI_IGGY_CONNECTORS_API_KEYS (comma-separated, so a rotation can overlap). Unset, imbi-api answers 503 and the runtime exits — there is no open default.

docker run \
  --restart on-failure \
  -e IGGY_MODE=connectors \
  -e IGGY_CONNECTORS_CONNECTORS_BASE_URL=http://imbi-api:8000/api/iggy/connectors \
  -e IGGY_CONNECTORS_API_KEY=the-same-key \
  -e IGGY_CONNECTORS_IGGY_ADDRESS=iggy:8090 \
  -e IGGY_CONNECTORS_IGGY_USERNAME=iggy \
  -e IGGY_CONNECTORS_IGGY_PASSWORD=iggy \
  ghcr.io/aweber-imbi/iggy:0.9.0-edge.6-0

The base URL carries whatever path imbi-api mounts its routes under, which is the path component of IMBI_API_URL. Point it at imbi-api's own port rather than at Caddy in all-in-one mode.

Restart the runtime after a release that adds a stream; that fetch is what picks it up. It also exits when imbi-api is not answering yet or when a stream it is configured for does not exist, so run it with --restart on-failure: Imbi provisions every stream as it starts, and coming back is the recovery for both.

Running Setup

The setup command initializes the authentication system:

docker run -it \
  -e CLICKHOUSE_URL=clickhouse+http://default:password@clickhouse:8123/imbi \
  -e IMBI_AUTH_JWT_SECRET=your-secret \
  -e IMBI_AUTH_ENCRYPTION_KEY=your-key \
  ghcr.io/aweber-imbi/imbi:latest setup

Custom Caddyfile

To customize the reverse proxy configuration, mount your own Caddyfile:

docker run -p 8080:8080 \
  -v /path/to/Caddyfile:/etc/caddy/Caddyfile:ro \
  ...
  ghcr.io/aweber-imbi/imbi:latest

UI Static Files

The UI static files are served by Caddy from /srv/ui. To use a custom build of the UI, mount it as a volume:

docker run -p 8080:8080 \
  -v /path/to/ui/dist:/srv/ui:ro \
  ...
  ghcr.io/aweber-imbi/imbi:latest