6.2 KiB
Kubernetes Deployment Guide
This guide covers building the Kandev Docker image and deploying it to a Kubernetes cluster.
Prerequisites
- Docker (for building the image)
- A container registry (Docker Hub, GHCR, ECR, etc.)
- A Kubernetes cluster with
kubectlconfigured - A StorageClass that supports
ReadWriteOncePVCs (for SQLite persistence)
Building the Image
From the repository root:
# Build for your current architecture
docker build -t kandev:latest .
# Build for a specific architecture (e.g., for amd64 clusters)
docker build --platform linux/amd64 -t kandev:latest .
# Multi-arch build (requires buildx)
docker buildx build --platform linux/amd64,linux/arm64 -t kandev:latest .
Using the Pre-built Image
Kandev publishes images to GitHub Container Registry. Pull directly:
docker pull ghcr.io/kdlbs/kandev:latest
Or reference it in your K8s deployment:
image: ghcr.io/kdlbs/kandev:latest
Deploying to Kubernetes
Quick Start
# Apply all manifests
kubectl apply -f k8s/
# Check status
kubectl get pods -l app=kandev
kubectl logs -l app=kandev -f
What Gets Created
| Resource | File | Purpose |
|---|---|---|
| Deployment | deployment.yaml |
Single-replica pod running backend + web |
| Service | service.yaml |
ClusterIP exposing port 8080 |
| ConfigMap | configmap.yaml |
Non-sensitive environment configuration |
| PVC | pvc.yaml |
10Gi persistent volume for SQLite + worktrees |
| Ingress | ingress.yaml |
Example ingress with WebSocket support |
Accessing the UI
Port-forward (quickest for testing):
kubectl port-forward svc/kandev 8080:8080
# Open http://localhost:8080
Ingress: Edit k8s/ingress.yaml to set your domain, then apply. The ingress routes all traffic to the backend on port 8080 (the Go backend reverse-proxies the Next.js frontend internally).
Custom Domain / Reverse Proxy
No extra configuration is needed. The frontend automatically uses window.location.origin to reach the API, which works with any domain, reverse proxy, or ingress setup.
Configuration
Kandev reads configuration via KANDEV_-prefixed environment variables (Viper). Set these in k8s/configmap.yaml or as environment variables in the deployment.
Core Settings
| Env Var | Default | Description |
|---|---|---|
KANDEV_SERVER_PORT |
8080 |
Server port (API + WebSocket + Web UI) |
KANDEV_DATA_DIR |
/data |
Kandev home directory — contains DB, worktrees, sessions, repos, and LSP servers |
KANDEV_DATABASE_DRIVER |
sqlite |
Database driver (sqlite or postgres) |
KANDEV_DATABASE_PATH |
$KANDEV_DATA_DIR/kandev.db |
SQLite database file path (override) |
KANDEV_DOCKER_ENABLED |
false |
Enable Docker runtime for agents (requires DinD) |
KANDEV_LOG_LEVEL |
info |
Log level: debug, info, warn, error |
KANDEV_LOGGING_FORMAT |
auto | Log format: json (auto-detected in K8s) or text |
PostgreSQL Settings (when KANDEV_DATABASE_DRIVER=postgres)
| Env Var | Default | Description |
|---|---|---|
KANDEV_DATABASE_HOST |
localhost |
PostgreSQL host |
KANDEV_DATABASE_PORT |
5432 |
PostgreSQL port |
KANDEV_DATABASE_USER |
kandev |
Database user |
KANDEV_DATABASE_PASSWORD |
(empty) | Database password |
KANDEV_DATABASE_DBNAME |
kandev |
Database name |
KANDEV_DATABASE_SSLMODE |
disable |
SSL mode |
Database: SQLite vs PostgreSQL
SQLite (default)
- Zero-config, works out of the box
- Database stored at
/data/kandev.dbon the PV - Single replica only (SQLite is single-writer)
- Deployment strategy is
Recreateto prevent concurrent writes - Good for small teams / personal use
PostgreSQL (recommended for production)
- Supports multiple replicas for horizontal scaling
- Change deployment strategy to
RollingUpdate - Set via environment variables:
# In configmap.yaml or a Secret
KANDEV_DATABASE_DRIVER: postgres
KANDEV_DATABASE_HOST: postgres.default.svc.cluster.local
KANDEV_DATABASE_PORT: "5432"
KANDEV_DATABASE_USER: kandev
KANDEV_DATABASE_PASSWORD: <from-secret>
KANDEV_DATABASE_DBNAME: kandev
When using Postgres, the PVC is still needed for worktree storage but the database itself is external.
Persistent Storage
The PVC at /data stores:
- SQLite database (
/data/kandev.db,/data/kandev.db-wal,/data/kandev.db-shm) - Git worktrees (
/data/worktrees/) for workspace isolation
The PVC uses ReadWriteOnce access mode. If your cluster requires a specific StorageClass, add it to k8s/pvc.yaml:
spec:
storageClassName: your-storage-class
Health Checks
The deployment includes both probes on the /health endpoint:
- Liveness probe: Restarts the pod if the backend becomes unresponsive (30s interval, 3 failures)
- Readiness probe: Removes the pod from service during startup or issues (10s interval, 3 failures)
The CLI launcher also performs an internal health check — it waits for the backend to be healthy before starting the web server.
Scaling
Single replica (SQLite): The default configuration uses replicas: 1 with Recreate strategy. This ensures only one instance writes to SQLite at a time.
Multiple replicas (PostgreSQL): Switch to Postgres, change the deployment strategy to RollingUpdate, and increase replicas:
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
Upgrading
# Build and push new image
docker build -t your-registry.com/kandev:v1.1.0 .
docker push your-registry.com/kandev:v1.1.0
# Update deployment
kubectl set image deployment/kandev kandev=your-registry.com/kandev:v1.1.0
# Or edit the deployment directly
kubectl edit deployment kandev
SQLite migrations run automatically on startup — no manual migration step needed.
Troubleshooting
# Check pod status
kubectl get pods -l app=kandev
# View logs
kubectl logs -l app=kandev -f
# Shell into the pod (if needed)
kubectl exec -it deployment/kandev -- /bin/bash
# Check PVC status
kubectl get pvc kandev-data
# Describe pod for events
kubectl describe pod -l app=kandev