Docker
Deploy the Kitaru server using Docker or Docker Compose
The Kitaru server container image is available at
zenmldocker/kitaru and works
with Docker, Docker Compose, or any container orchestration platform.
Quick start
Start a server with sensible defaults:
docker run -d --platform linux/amd64 --name kitaru-server -p 8080:8080 zenmldocker/kitaru:latestApple Silicon (M-series) Macs: The published image is currently amd64-only.
Keep --platform linux/amd64 on every docker run, docker pull, or Docker
Compose service that uses zenmldocker/kitaru:*. Without it, Docker will fail
with no matching manifest for linux/arm64/v8.
On Intel/AMD hosts, you can omit the flag if you prefer.
Use a version-pinned tag (e.g. zenmldocker/kitaru:0.2.0) that matches your
client SDK version to avoid API incompatibilities.
The server initializes an internal SQLite database on first startup. Wait for the health endpoint before connecting:
until curl -fsS http://localhost:8080/health >/dev/null; do sleep 2; doneThen connect your local CLI:
kitaru login http://localhost:8080This opens browser-based device authorization. After completing the flow, verify with:
kitaru statusHeadless activation
The first time the server starts, you need to activate it by visiting the
dashboard at http://localhost:8080 and creating an initial admin user account.
To skip this manual onboarding step (useful for automated or headless deployments), pass these environment variables:
docker run -d --platform linux/amd64 --name kitaru-server -p 8080:8080 \
-e ZENML_SERVER_AUTO_ACTIVATE=1 \
-e ZENML_DEFAULT_USER_NAME=admin \
-e ZENML_DEFAULT_USER_PASSWORD=password \
zenmldocker/kitaru:latestIf ZENML_DEFAULT_USER_PASSWORD is omitted, the admin account is created
with an empty password — only appropriate for local development.
The server activation variables use ZENML_* names because the Kitaru server
uses ZenML's server runtime internally. These are server-side configuration
knobs, not user-facing SDK settings.
Building from source
If you are testing changes from a local checkout:
just DOCKER_REPO=kitaru-local DOCKER_TAG=dev server-imagedocker build -f docker/Dockerfile --target server -t kitaru-local:dev .Then run it:
docker run -d --name kitaru-server -p 8080:8080 kitaru-local:devContainer management
docker logs kitaru-server # View server logs
docker logs kitaru-server -f # Follow logs
docker stop kitaru-server # Stop
docker start kitaru-server # Restart
docker rm kitaru-server # Remove
docker rm -f kitaru-server # Force remove (stop + remove)Persist your data
Default (ephemeral)
Without any volume mounts, the server stores everything inside the container:
- Metadata: SQLite database
- Artifacts: Local filesystem
This data is lost when the container is removed (docker rm). It survives
docker stop / docker start.
Persisting the SQLite database
Mount a host directory or Docker volume at the default data path:
docker run -d --platform linux/amd64 --name kitaru-server -p 8080:8080 \
-v kitaru-data:/zenml/.zenconfig/local_stores/default_zen_store \
zenmldocker/kitaru:latestmkdir kitaru-data
docker run -d --platform linux/amd64 --name kitaru-server -p 8080:8080 \
--mount type=bind,source=$PWD/kitaru-data,target=/zenml/.zenconfig/local_stores/default_zen_store \
zenmldocker/kitaru:latestThe path /zenml/.zenconfig/local_stores/default_zen_store is an internal
default inherited from the base image. It may change in a future release. For
production deployments, use an external MySQL database
instead of relying on this path.
The container runs as UID 1000 (zenml). If using a bind mount, ensure the
host directory is writable by UID 1000: chown -R 1000:1000 kitaru-data
Using MySQL (recommended for production)
SQLite works well for development and single-user setups, but for production you should use MySQL:
- Supports concurrent access from multiple server replicas
- Better performance under load
- Standard backup and HA tooling
Start a MySQL container:
docker run -d --name kitaru-mysql \
-p 3306:3306 \
-e MYSQL_ROOT_PASSWORD=password \
-e MYSQL_DATABASE=kitaru \
-v kitaru-mysql:/var/lib/mysql \
mysql:8.0Then start the Kitaru server pointing at it:
docker run -d --platform linux/amd64 --name kitaru-server -p 8080:8080 \
--add-host host.docker.internal:host-gateway \
--env ZENML_STORE_URL=mysql://root:password@host.docker.internal:3306/kitaru \
zenmldocker/kitaru:latestThe server automatically runs database migrations on first startup.
The database URL uses ZENML_STORE_URL because the Kitaru server uses
ZenML's server runtime internally. Future versions may provide a
KITARU_DATABASE_URL equivalent.
Linux users: The --add-host host.docker.internal:host-gateway flag is
required on Linux to make host.docker.internal resolve inside the
container. On macOS and Windows, Docker provides this automatically.
Docker Compose (server + MySQL)
A docker-compose.yml for a production-like setup:
services:
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: password
MYSQL_DATABASE: kitaru
volumes:
- kitaru-mysql:/var/lib/mysql
ports:
- "3306:3306"
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
kitaru:
image: zenmldocker/kitaru:latest
platform: linux/amd64
ports:
- "8080:8080"
environment:
ZENML_STORE_URL: mysql://root:password@mysql:3306/kitaru
ZENML_SERVER_AUTO_ACTIVATE: "1"
ZENML_DEFAULT_USER_NAME: admin
ZENML_DEFAULT_USER_PASSWORD: "${KITARU_ADMIN_PASSWORD:-}"
depends_on:
mysql:
condition: service_healthy
restart: on-failure
volumes:
kitaru-mysql:Create a .env file alongside the compose file:
KITARU_ADMIN_PASSWORD=your-secure-passwordThe KITARU_ADMIN_PASSWORD environment variable is optional here for
convenience. If not set, the admin account is created with an empty
password — not recommended for production.
Start:
docker compose up -dWait for health and connect:
until curl -fsS http://localhost:8080/health >/dev/null; do sleep 2; done
kitaru login http://localhost:8080Tear down:
docker compose down # Stop containers (keep data)
docker compose down -v # Stop and delete volumesConnect to the server
Interactive login (browser-based)
kitaru login http://localhost:8080The CLI opens a browser for device authorization. If the browser does not open automatically, copy/paste the printed URL.
API key login (headless / CI)
kitaru login https://kitaru.example.com --api-key kat_abc123...Environment variable bootstrap (Docker / CI)
For containers or CI jobs that need to talk to the server without running
kitaru login:
export KITARU_SERVER_URL=https://kitaru.example.com
export KITARU_AUTH_TOKEN=kat_abc123...
export KITARU_PROJECT=my-projectSee Configuration for the full env-var reference and precedence rules.
Verify connection
kitaru status # Compact view
kitaru info # Detailed view (shows server version, database type, deployment type)Disconnect
kitaru logoutTroubleshooting
Server won't start
Check container logs:
docker logs kitaru-server -fCommon causes:
- Database connection refused (wrong host/port/credentials in
ZENML_STORE_URL) - Port 8080 already in use (
docker: bind: address already in use) - Insufficient permissions on mounted volumes (UID 1000 cannot write)
Login stalls or shows errors
- Ensure the
/healthendpoint returns 200 before attempting login. The server takes ~20 seconds on first startup to initialize the database. - If the browser shows
{"detail":"An unexpected error occurred."}, the dashboard assets may be missing from the image. Rebuild from source or use a newer image tag. - If the CLI keeps printing
authorization_pending, the server may not be fully initialized. Wait and retry. - Port 8383 belongs to bare
kitaru loginlocal-server mode. To connect to the Docker container, runkitaru login http://localhost:8080explicitly. If you already used the URL form and still see login failures, treat that as a separate problem: confirm/healthreturns 200 and inspectdocker logs kitaru-serverfor the server-side error. - Check
docker logs kitaru-serverfor error details.
no matching manifest for linux/arm64/v8 (Apple Silicon)
If you see this error when pulling or running the image on an M-series Mac:
no matching manifest for linux/arm64/v8 in the manifest list entriesAdd --platform linux/amd64 to your docker run (or docker pull) command.
Docker Desktop will run the image under Rosetta emulation. See the
Quick start callout for the full command.
host.docker.internal not resolving (Linux)
Add --add-host host.docker.internal:host-gateway to your docker run
command, or use extra_hosts in Docker Compose.