Skip to Content
Kai

Docker Compose

The services, the images, and running Kai against your own Postgres, Redis and S3.

infra/docker-compose.yml runs everything Kai needs. Start it with:

docker compose -f infra/docker-compose.yml up -d --build

The Quickstart walks through .env.

Services

ServiceImage
kaibuilt from infra/Dockerfile (target kai)The server: the API and the web app on one port (KAI_PORT, default 3000).
migratethe same kai imageRuns node server.mjs migrate once before kai starts: creates the database roles, applies migrations, exits.
librarybuilt from infra/Dockerfile (target library)The MCP catalog, its icons and model data. Published on LIBRARY_PUBLIC_PORT (default 4100), because browsers load the icons from it.
postgrespostgres:17-alpineData. Not published.
redisredis:7-alpineRun event streams, rate limits, short-lived caches. Not published.
s3, s3-initchrislusf/seaweedfsObject storage for attachments and large tool outputs, and a one-shot job that creates the bucket. Published on S3_PUBLIC_PORT (default 9010), because browsers upload and download directly.

Compose points Kai at the other services by their service names (POSTGRES_HOST=postgres, REDIS_URL=redis://redis:6379, S3_ENDPOINT=http://s3:8333, KAI_LIBRARY_URL=http://library:4100), overriding .env.

The images

ImagePulled (compressed)On disk
kaiabout 53 MBabout 134 MB
libraryabout 31 MBabout 76 MB

Both run on Alpine with its own Node 22 build. The kai image is one bundled server file, the built web app, the SQL migrations and sharp (for resizing uploaded images). It has full ICU data so dates and numbers format correctly in every locale. Neither image runs as root.

Build them on their own with:

docker build -f infra/Dockerfile --target kai -t kai .
docker build -f infra/Dockerfile --target library -t kai-library .

Using your own Postgres, Redis and object storage

Kai only needs to reach them. Drop the postgres, redis and s3 services from the Compose file (or run the kai image any other way) and set:

  • Postgres (Kai is developed and tested on 17): POSTGRES_HOST, POSTGRES_PORT, POSTGRES_DATABASE, and the superuser (POSTGRES_USER, POSTGRES_PASSWORD) that migrate uses to create Kai's three roles and their passwords (POSTGRES_APP_*, POSTGRES_AUTH_*, POSTGRES_SERVICE_*). The server itself connects only as those roles.
  • Redis (developed on 7; Kai uses streams): REDIS_URL.
  • S3-compatible storage (AWS S3, Cloudflare R2, MinIO, SeaweedFS…): S3_ENDPOINT, S3_REGION, S3_BUCKET, S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY, S3_FORCE_PATH_STYLE, and S3_PUBLIC_ENDPOINT when browsers reach it at a different address. Browsers upload with presigned URLs, so the bucket's CORS rules must allow PUT and GET from KAI_APP_URL.

Run the migrations before starting a new version:

docker run --rm --env-file .env kai node server.mjs migrate

The library service

The library (apps/library) serves:

  • the MCP catalog that Browse library shows, and its icons;
  • model capabilities (context window, output limit, reasoning efforts, web and tool search support), built from public model datasheets.

It fetches the datasheets at start and every LIBRARY_MODELS_REFRESH_HOURS (default 6), keeps the last good copy if a refresh fails, and works offline from the copy in its image. It holds no secrets and needs no database.

VariableDefault
PORT4100
LIBRARY_PUBLIC_URLhttp://localhost:<PORT>The address browsers use, for icon URLs. Compose sets it from KAI_LIBRARY_PUBLIC_URL.
LIBRARY_MODELS_URLBifrost's model datasheetWhere model data comes from.
LIBRARY_MODEL_PARAMS_URLBifrost's model parameters sheet
LIBRARY_MODELS_REFRESH_HOURS6

Without a library (KAI_LIBRARY_URL unset), the MCP library is hidden and models get per-provider defaults.

Upgrading

git pull
docker compose -f infra/docker-compose.yml up -d --build

migrate runs again before the new kai starts. Runs in progress on the old container get 20 seconds to finish; anything still running is marked interrupted, and people can regenerate.

On this page