Two flavors are published. The default vanilla image is smallest and bundles npm-installable agent CLIs only. The :universal image (~1.4 GB) adds language toolchains (Go, Rust, build-essential), linters, and Playwright Chromium system libs - pick this if your agents work on Go/Rust/Python projects or drive headless browsers.
docker pull ghcr.io/kdlbs/kandev:universal
See the repository image guide for the full comparison, inclusion policy, and recipes for deriving your own image.
The root Dockerfile is a release-image Dockerfile: it copies prebuilt binaries from a bundle/ directory in the Docker build context. It does not compile the repository inside Docker. On a Linux host matching the target architecture, build the service bundle and prepare the same context layout used by the release workflow:
For a cross-architecture image, first produce or download the matching kandev-linux-x64.tar.gz or kandev-linux-arm64.tar.gz release bundle, extract its top-level kandev/ directory into ctx/bundle/, copy docker-entrypoint.sh into ctx/, and build that context with the matching --platform. Setting --platform without a matching bundle only labels an image containing the wrong native binaries.
Kandev stores its SQLite database and git worktrees in /data. Mount a volume to persist data across container restarts:
# Named volume (recommended)docker run -v kandev-data:/data ghcr.io/kdlbs/kandev:latest# Bind mount to a host directorydocker run -v /path/on/host:/data ghcr.io/kdlbs/kandev:latest
Without a volume, data is lost when the container is removed.
The image sets HOME=/data/home, so every CLI that writes its auth under $HOME lands on the volume and survives container restarts and image upgrades:
gh CLI — ~/.config/gh/hosts.yml
Claude Code — ~/.claude/.credentials.json, ~/.claude.json
Codex — ~/.codex/auth.json, ~/.codex/config.toml
Auggie — ~/.augment/session.json
GitHub Copilot — ~/.copilot/...
OpenCode, Amp — ~/.config/<tool>/...
A one-time docker exec -it kandev gh auth login (or claude login, codex login, etc.) is enough; you do not need to redo it after docker pull and recreating the container.
The GitHub PAT configured in Settings → Integrations → GitHub is stored as a secret in the database and has always persisted. The HOME=/data/home setup covers the separate gh auth login flow that the backend falls back to when no GITHUB_TOKEN secret is set.
See configuration.md for the full reference (including the YAML form and every knob the backend reads). The table below covers the env vars most often set in a Docker deployment.
Variable
Required
Default
Description
KANDEV_HOME_DIR
No
/data
Kandev home directory - contains data/ (DB), tasks/, worktrees/, repos/, sessions/, and lsp-servers/
KANDEV_DATABASE_DRIVER
No
sqlite
Database driver (sqlite or postgres)
KANDEV_DATABASE_PATH
No
$KANDEV_HOME_DIR/data/kandev.db
SQLite database file path (override)
KANDEV_LOG_LEVEL
No
info
Log level: debug, info, warn, error
KANDEV_LOGGING_FORMAT
No
environment-selected (text in ordinary Docker runs)
Explicit format: text or json. The default becomes json when KANDEV_ENV is production or prod.
KANDEV_LOGGING_OUTPUTPATH
No
stdout
Log destination: stdout, stderr, or a file path (rotated when a file)
KANDEV_LOGGING_MAXSIZEMB
No
100
Rotate the log file when it exceeds this size (MB). File output only.
KANDEV_LOGGING_MAXBACKUPS
No
5
Max rotated files to retain (0 = unlimited). File output only.
KANDEV_LOGGING_MAXAGEDAYS
No
30
Max age of rotated files in days (0 = unlimited). File output only.
KANDEV_LOGGING_COMPRESS
No
true
Gzip rotated files. File output only.
KANDEV_DOCKER_ENABLED
No
false
Enable Docker runtime for agents (see below)
File-mode note: when KANDEV_LOGGING_OUTPUTPATH is a file path, the active log file is created with mode 0600 (owner read/write only). Run any log shipper or sidecar as the same user, or use stdout/stderr and let the container runtime collect logs.
Upgrading from a pre-KANDEV_HOME_DIR image? The SQLite DB path moved from /data/kandev.db to /data/data/kandev.db. The backend auto-migrates the legacy kandev.db (plus any -wal/-shm files) on first boot — look for Migrated SQLite database from pre-KANDEV_HOME_DIR location in the logs. If you prefer to pin the old location instead, set -e KANDEV_DATABASE_PATH=/data/kandev.db. If you previously set KANDEV_DATA_DIR, replace it with KANDEV_HOME_DIR.
Since Kandev serves everything on a single port, a reverse proxy only needs to forward all traffic to port 38429. No extra environment variables are needed — the frontend automatically uses window.location.origin to reach the API.
docker pull ghcr.io/kdlbs/kandev:latestdocker compose up -d # or: docker stop kandev && docker rm kandev && docker run ...
The volume at /data carries over the database, worktrees, npm globals, and $HOME for agent CLIs, so there is no manual migration step.
Upgrading across the HOME=/data/home change: if you previously ran docker exec to gh auth login or log in to agent CLIs on a pre-HOME=/data/home image, that state lived in the ephemeral /home/kandev inside the container and is not carried over. Log in once on the new container and it will persist for all subsequent upgrades. If you want to preserve the old state, copy it onto the volume before recreating the container:
docker exec kandev sh -c 'cp -a /home/kandev/. /data/home/ 2>/dev/null || true'