6.2 KiB
Docker Guide
Run Kandev in a Docker container. For Kubernetes deployment, see k8s.md.
Quick Start
docker run -p 8080:8080 -p 3000:3000 -v kandev-data:/data ghcr.io/kdlbs/kandev:latest
Open http://localhost:3000 in your browser.
Using the Pre-built Image
Kandev publishes images to GitHub Container Registry for linux/amd64 and linux/arm64:
# Latest release
docker pull ghcr.io/kdlbs/kandev:latest
# Specific version
docker pull ghcr.io/kdlbs/kandev:0.9.0
Building from Source
From the repository root:
# Build for your current architecture
docker build -t kandev:latest .
# Build for a specific architecture
docker build --platform linux/amd64 -t kandev:latest .
Data Persistence
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 directory
docker run -v /path/on/host:/data ghcr.io/kdlbs/kandev:latest
Without a volume, data is lost when the container is removed.
Configuration
Configuration is done via KANDEV_-prefixed environment variables:
docker run -p 8080:8080 -p 3000:3000 \
-v kandev-data:/data \
-e KANDEV_LOG_LEVEL=debug \
ghcr.io/kdlbs/kandev:latest
Environment Variables
| Variable | Default | Description |
|---|---|---|
KANDEV_PUBLIC_URL |
(unset) | External URL when behind a reverse proxy (e.g., https://kandev.example.com) |
KANDEV_DATA_DIR |
/data |
Base directory for all data (DB, worktrees, sessions, etc.) |
KANDEV_DATABASE_DRIVER |
sqlite |
Database driver (sqlite or postgres) |
KANDEV_DATABASE_PATH |
$KANDEV_DATA_DIR/kandev.db |
SQLite database file path (override) |
KANDEV_LOG_LEVEL |
info |
Log level: debug, info, warn, error |
KANDEV_LOGGING_FORMAT |
text |
Log format: text or json |
KANDEV_DOCKER_ENABLED |
false |
Enable Docker runtime for agents (see below) |
PostgreSQL
To use PostgreSQL instead of SQLite:
docker run -p 8080:8080 -p 3000:3000 \
-e KANDEV_DATABASE_DRIVER=postgres \
-e KANDEV_DATABASE_HOST=host.docker.internal \
-e KANDEV_DATABASE_PORT=5432 \
-e KANDEV_DATABASE_USER=kandev \
-e KANDEV_DATABASE_PASSWORD=secret \
-e KANDEV_DATABASE_DBNAME=kandev \
ghcr.io/kdlbs/kandev:latest
Ports
| Port | Service |
|---|---|
8080 |
Backend API + WebSocket |
3000 |
Web UI |
Override ports by changing the published ports and passing CLI flags:
docker run -p 9080:9080 -p 4000:4000 \
-v kandev-data:/data \
ghcr.io/kdlbs/kandev:latest \
kandev start --backend-port 9080 --web-port 4000
Docker Compose
Create a docker-compose.yml:
services:
kandev:
image: ghcr.io/kdlbs/kandev:latest
ports:
- "8080:8080"
- "3000:3000"
volumes:
- kandev-data:/data
restart: unless-stopped
volumes:
kandev-data:
docker compose up -d
With PostgreSQL
services:
kandev:
image: ghcr.io/kdlbs/kandev:latest
ports:
- "8080:8080"
- "3000:3000"
volumes:
- kandev-data:/data
environment:
KANDEV_DATABASE_DRIVER: postgres
KANDEV_DATABASE_HOST: postgres
KANDEV_DATABASE_PORT: "5432"
KANDEV_DATABASE_USER: kandev
KANDEV_DATABASE_PASSWORD: secret
KANDEV_DATABASE_DBNAME: kandev
depends_on:
postgres:
condition: service_healthy
restart: unless-stopped
postgres:
image: postgres:17
environment:
POSTGRES_USER: kandev
POSTGRES_PASSWORD: secret
POSTGRES_DB: kandev
volumes:
- postgres-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U kandev"]
interval: 5s
timeout: 3s
retries: 5
restart: unless-stopped
volumes:
kandev-data:
postgres-data:
Reverse Proxy
When running Kandev behind a reverse proxy (nginx, Traefik, Caddy) that serves both the API and web UI on the same domain, set KANDEV_PUBLIC_URL so the frontend routes API calls through the proxy instead of trying to reach port 8080 directly:
docker run -p 8080:8080 -p 3000:3000 \
-v kandev-data:/data \
-e KANDEV_PUBLIC_URL=https://kandev.example.com \
ghcr.io/kdlbs/kandev:latest
Your reverse proxy should route:
/api/*and/ws→http://kandev:8080(backend)/*→http://kandev:3000(web UI)
With WebSocket support enabled (for /ws).
Docker Compose with Caddy
services:
kandev:
image: ghcr.io/kdlbs/kandev:latest
volumes:
- kandev-data:/data
environment:
KANDEV_PUBLIC_URL: "https://kandev.example.com"
restart: unless-stopped
caddy:
image: caddy:2
ports:
- "80:80"
- "443:443"
volumes:
- caddy-data:/data
- ./Caddyfile:/etc/caddy/Caddyfile
restart: unless-stopped
volumes:
kandev-data:
caddy-data:
Example Caddyfile:
kandev.example.com {
handle /api/* {
reverse_proxy kandev:8080
}
handle /ws {
reverse_proxy kandev:8080
}
handle /health {
reverse_proxy kandev:8080
}
handle {
reverse_proxy kandev:3000
}
}
Docker-in-Docker (Agent Containers)
By default, KANDEV_DOCKER_ENABLED=false inside the container. To enable Docker-based agent execution, mount the Docker socket:
docker run -p 8080:8080 -p 3000:3000 \
-v kandev-data:/data \
-v /var/run/docker.sock:/var/run/docker.sock \
-e KANDEV_DOCKER_ENABLED=true \
ghcr.io/kdlbs/kandev:latest
Note: Mounting the Docker socket gives the container full access to the host's Docker daemon. Only do this in trusted environments.
Health Check
The backend exposes a /health endpoint:
curl http://localhost:8080/health
For Docker health checks in compose:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 5s
retries: 3
start_period: 15s
Troubleshooting
# View logs
docker logs kandev
# Follow logs
docker logs -f kandev
# Shell into the container
docker exec -it kandev /bin/bash
# Check data volume
docker volume inspect kandev-data