Docker environment for Kilo CLI - enabling portable, zero-install execution on remote hosts.
- Non-root user - Runs as
kilouser with dynamicPUID/PGIDmapping to match host user - Persistent database - SQLite database and auth state survive container restarts via named volume
- Token persistence - MCP server tokens are prompted once and saved in encrypted volume storage
- Custom environment variables - Store your own API keys and config values in encrypted storage, available in all sessions
- One-time sessions -
--onceflag for ephemeral runs without persistence - Browser automation -
--playwrightflag starts a Playwright MCP sidecar for screenshots, navigation, and web interaction - Built-in services - Extensible service system with
--docker,--go,--nvm, and more (see Services)
Download the host binary from GitHub Releases:
# Linux amd64
curl -fsSL -o ~/.local/bin/kilo-docker https://github.com/mbabic84/kilo-docker/releases/latest/download/kilo-docker-linux-amd64
chmod +x ~/.local/bin/kilo-docker
# macOS arm64
curl -fsSL -o ~/.local/bin/kilo-docker https://github.com/mbabic84/kilo-docker/releases/latest/download/kilo-docker-darwin-arm64
chmod +x ~/.local/bin/kilo-dockerOr use the bootstrap installer:
curl -fsSL https://raw.githubusercontent.com/mbabic84/kilo-docker/main/scripts/install.sh | shThen run from any directory:
kilo-docker| Command | Description |
|---|---|
sessions [name|index] |
List sessions or attach to one by name or index |
sessions stop <name|index> |
Stop a running session, freeing its ports |
sessions stop --legacy |
Stop all sessions using legacy volumes |
sessions stop --needs-update |
Stop all sessions needing an image update |
sessions cleanup [-y] [name|index] |
Remove a session (interactive if no name given) |
sessions cleanup -y -a |
Remove all exited sessions |
sessions cleanup --legacy [-y] |
Remove all sessions using legacy volumes |
sessions cleanup --needs-update [-y] |
Remove all sessions needing an image update |
sessions recreate <name|index> [flags] |
Recreate a session, optionally overriding its configuration (preserves volume) |
networks |
List available Docker networks |
playwright |
Recreate the Playwright MCP sidecar container |
profile save <name> <flags> |
Save current flags as a profile (flags passed after name) |
profile list |
List all saved profiles (default marked with *) |
profile show <name> |
Display full profile JSON |
profile edit <name> |
Open profile in $EDITOR |
profile delete <name> |
Remove a profile |
profile import <file> |
Load a profile from a JSON file |
profile export <name> |
Print profile JSON to stdout |
profile set-default <name> |
Set a profile as the default |
profile unset-default |
Remove the default profile |
profile show-default |
Print the current default profile name |
backup [-f] [--legacy-volume] |
Create backup of volume to tar.gz |
restore <file> [-f] [-v|--volume <name>] [--legacy-volume] |
Restore volume from backup |
init |
Reset configuration (remove volume, re-enter tokens) |
cleanup |
Remove volume, containers, image, and installed binary |
update [config] |
Pull latest Docker image and update binary, or merge config template |
install-dev |
Install the currently running development binary to ~/.local/bin/kilo-docker |
version |
Show kilo-docker and kilo versions |
help |
Show help message |
| Option | Description |
|---|---|
--help, -h |
Show help message |
--once |
Run a one-time session without persistence (no volume) |
--volume, -v |
Mount a volume (host_path:container_path), repeatable |
--workspace, -w |
Set custom workspace path (default: current directory) |
--port, -p |
Map port (host_port:container_port), repeatable |
--playwright |
Start a Playwright MCP sidecar container for browser automation |
--ssh |
Enable SSH agent forwarding into the container |
--network <name> |
Connect to a Docker network (repeatable, kilo-shared included by default). Special modes: host, none, container:<name> — these are mutually exclusive with all other networks |
--profile <name> |
Load a named flag profile from ~/.config/kilo-docker/profiles/ |
--yes, -y |
Auto-confirm all prompts (useful for piped/non-interactive installs) |
Use --volume (or -v) to mount additional host directories or files into the container:
# Mount a single directory
kilo-docker --volume /host/data:/container/data
# Mount multiple volumes
kilo-docker -v /host/cache:/cache -v ~/.config:/home/kd-xxx/.config
# Mount with read-only access
kilo-docker --volume /host/readonly:/container/readonly:roVolume mounts follow the same format as Docker's -v flag:
host_path:container_path— Standard mounthost_path:container_path:ro— Read-only mount- Named volumes:
volume_name:container_path
The current working directory is always mounted at the same path automatically.
| Service | Description |
|---|---|
--docker |
Mount Docker socket for container management from within Kilo |
--go |
Install Go (latest stable) for development |
--build |
Install build essentials (gcc, g++, make) for compiling native extensions |
--gh |
Install GitHub CLI and GitHub MCP server (via gh-mcp extension) for GitHub interactions from within Kilo |
--uv |
Install uv for fast Python package management |
--nvm |
Install NVM (Node Version Manager) for managing Node.js versions |
--rclone |
Install rclone, a universal CLI for S3 and 40+ cloud storage backends |
--gitnexus |
Install GitNexus for codebase knowledge graph indexing and MCP-based code intelligence |
--diagnostics |
Install diagnostic tools (ps, top, ss, lsof, netstat, nc, ping, pstree) |
Named profiles let you save reusable flag combinations and apply them with a single --profile flag. Profiles are stored as JSON files under ~/.config/kilo-docker/profiles/.
# Save current flags as a profile
kilo-docker --go --ssh --docker --workspace /path profile save fullstack
# List all profiles (default marked with *)
kilo-docker profile list
# Show full profile JSON
kilo-docker profile show fullstack
# Edit a profile in your editor
kilo-docker profile edit fullstack
# Use a profile
kilo-docker --profile fullstackSet a default profile and it auto-loads whenever you run kilo-docker with no flags:
kilo-docker profile set-default fullstack
kilo-docker # auto-loads --go --ssh --docker
kilo-docker --profile other # explicit profile overrides default
kilo-docker --ssh # CLI flags override profile flags
kilo-docker profile unset-default # stop auto-loadingMerge precedence: CLI flags always win. Services from the profile are additive — a service already enabled by CLI won't be disabled. SSH only enables from the profile if not already set on the CLI. Ports, volumes, and networks are always additive. Special network modes (host, none, container:<name>) are mutually exclusive with other networks — when one is used, all others are discarded (Docker restriction).
Share profiles between machines:
kilo-docker profile export fullstack > fullstack.json
scp fullstack.json remote-host:
ssh remote-host kilo-docker profile import fullstack.jsonUse --once to run without creating or mounting a named volume. No data persists after the container exits:
kilo-docker --onceThis is useful for CI pipelines, ephemeral environments, or when you don't want to leave any state on the host.
The --playwright flag starts a Playwright MCP sidecar container alongside Kilo, enabling browser automation (screenshots, navigation, form filling, etc.):
kilo-docker --playwrightThe sidecar runs headless Chromium in HTTP mode on port 8931 inside the shared Docker network (kilo-shared). The Playwright container is shared across sessions — if already running, it's reused rather than recreated.
Screenshots and output files are saved to a shared volume (kilo-playwright-output) mounted at /mnt/playwright-output inside the Kilo container.
Use the --ssh flag to enable SSH agent forwarding. The host binary detects whether an SSH agent is running on the host:
- Agent running — Uses the existing agent via
$SSH_AUTH_SOCK - No agent — Starts one automatically, loads all private keys from
~/.ssh/, and cleans up on exit
The container mounts the host's SSH agent socket, allowing git, ssh, and scp to use your host SSH keys without copying private keys into the container.
kilo-docker --sshSecurity: Private keys never enter the container. The container communicates with the host's SSH agent via a Unix socket.
All Kilo containers are automatically attached to a shared Docker network (kilo-shared), enabling container-to-container communication by name across sessions:
# Kilo containers can resolve each other by container name
kilo-docker
# Inside container: ping <other-container-name>You can attach to additional networks using the --network flag (repeatable):
kilo-docker --network my-network
kilo-docker --network net1 --network net2The kilo-shared network is always included implicitly — it doesn't need to be specified and won't trigger flag mismatch detection when comparing stored vs current args.
Use --network host to share the host's network stack. The container's localhost becomes the host's localhost, giving direct access to any services or other containers running on the host:
kilo-docker --network hostWhen host is specified, all other networks (including kilo-shared) are ignored, and a warning is printed if additional --network flags were passed. This matches Docker's restriction that host networking cannot be combined with other networks.
Host networking also bypasses port mapping — all host ports are directly accessible inside the container without needing --port / -p.
Use --network none to run the container with no network access:
kilo-docker --network noneThis is useful for isolated execution environments where network access is not required or not permitted.
Use --network container:<name> to share another container's network namespace. The container will have the same IP address and network interfaces as the referenced container:
kilo-docker --network container:my-appBoth none and container:<name> are mutually exclusive with other networks, just like host.
Kilo Docker uses a data-driven service architecture. Services are defined as structured data, making it easy to add new capabilities without modifying core logic.
Each service can specify:
- CLI flag — enabling the service
- Installation commands — shell commands run inside the container at startup
- Environment variables — passed to the container
- Host environment variables — values sourced from the host
- Volumes — filesystem paths mounted from the host
- Required socket — optional host socket path for validation
- Config files — optional files copied from container to user home
When you pass a service flag, the host binary validates required host resources, collects socket GIDs, builds the KD_SERVICES env var, and mounts required volumes.
Inside the container, the entrypoint reads KD_SERVICES, runs installation commands, copies config files, and sets environment variables.
The host binary uses a per-user named Docker volume mounted at /home. Each user gets their own volume (named by SHA-256 hash of the username, e.g. kilo-a1b2c3d4e5f6-data), providing data isolation between users. All sessions for the same user share this volume. Inside the container, the user home directory is dynamically generated as /home/kd-<hash>. This stores:
- SQLite database, auth state, logs
- Configuration (
kilo.jsonc— model selection, provider connections, MCP settings) - Custom commands (
.config/kilo/commands/*.md) and agents (.config/kilo/agents/*.md) - Plugins (
.config/kilo/plugins/*.{js,ts}), skills (.config/kilo/skills/*/SKILL.md), tools (.config/kilo/tools/*.{js,ts}) - Instruction files (
.config/kilo/rules/*.md) - Session state and snapshots
- Cache
The volume persists across container restarts. Use kilo-docker init to reset tokens, or kilo-docker cleanup to remove all state (volume, containers, image, and installed binary).
If you previously used a shared kilo-docker-data volume, kilo-docker will automatically copy your data to a new per-user volume on first run. The legacy volume is left intact so you can verify the migration succeeded. Remove it manually once confirmed:
docker volume rm kilo-docker-dataWhen a new Kilo Docker image adds MCP servers or config changes, run:
kilo-docker update configThis downloads the latest kilo.jsonc template from the repository and merges it with your existing config. New servers are added, existing customizations are preserved.
Create a backup of your volume to transfer data between hosts or protect against data loss:
# Create backup with auto-generated filename
kilo-docker backup
# Create backup with custom filename
kilo-docker backup ~/my-kilo-backup.tar.gz
# Restore from backup
kilo-docker restore ~/my-kilo-backup.tar.gzDuring the transition from the shared volume, use --legacy-volume to back up or restore the old shared volume:
# Backup the legacy shared volume
kilo-docker backup --legacy-volume
# Restore to the legacy shared volume
kilo-docker restore ~/my-kilo-backup.tar.gz --legacy-volumeBackups are portable tar.gz archives containing all volume data. The restore command validates the archive and preserves file ownership.
Kilo-docker tracks sessions by directory. Each working directory gets its own container (named by SHA-256 hash of the path).
# List all sessions
kilo-docker sessions
# Attach to a session by name or index
kilo-docker sessions <name-or-index>
# Remove a session (interactive selection)
kilo-docker sessions cleanup
# Remove a specific session
kilo-docker sessions cleanup <name-or-index>
# Remove all exited sessions
kilo-docker sessions cleanup -a
# Remove all sessions using legacy volumes
kilo-docker sessions cleanup --legacy
# Remove all sessions needing an image update
kilo-docker sessions cleanup --needs-update
# Stop a running session (frees ports, preserves container)
kilo-docker sessions stop <name-or-index>
# Stop all sessions using legacy volumes
kilo-docker sessions stop --legacy
# Stop all sessions needing an image update
kilo-docker sessions stop --needs-update
# Stop all running sessions
kilo-docker sessions stop --all
# Recreate a session with the same flags (preserves volume)
kilo-docker sessions recreate <name-or-index>
# Recreate a session with flag overrides
kilo-docker sessions recreate <name-or-index> --ssh
kilo-docker sessions recreate <name-or-index> --port 9090:80 --profile dev
# Show recreate help
kilo-docker sessions recreate -hWhen attaching to a session, kilo-docker detects the container state: if running it attaches directly, if stopped it starts the container then attaches.
sessions recreate removes the old container but keeps the volume, then starts a fresh container with the merged configuration. If no flags are given, the session uses its original flags. Any flags passed after the session name override the stored values.
| Server | Description | Auth |
|---|---|---|
context7 |
Library documentation lookup | API key header |
ainstruct |
Document storage and semantic search | Bearer token (auto-created) |
playwright |
Browser automation (screenshots, navigation) | None (local sidecar) |
gitnexus |
Codebase knowledge graph indexing and MCP-based code intelligence | None (local) |
github |
GitHub repository, issue, and PR management | None (uses gh auth) |
context7 requires an API key passed via the CONTEXT7_API_KEY header. Use kilo-entrypoint mcp-tokens to manage MCP tokens interactively. Use kilo-entrypoint custom-envs to manage your own environment variables (see Custom Environment Variables).
playwright is only available when using the --playwright flag. It runs as a separate container on a shared Docker network with no authentication required.
github is only available when using the --gh flag. It runs as a local MCP server inside the Kilo container, reusing your existing gh auth session — no separate PAT or token needed.
Store your own API keys, config values, or any environment variables in AES-256 encrypted storage. Values are loaded automatically into every Kilo session alongside the built-in MCP tokens.
# List all custom envs (values are masked)
kilo-entrypoint custom-envs list
# Add a new variable
kilo-entrypoint custom-envs add MY_API_KEY "sk-abc123"
# Edit an existing variable
kilo-entrypoint custom-envs edit MY_API_KEY "sk-new-value"
# Get raw value (for scripting)
kilo-entrypoint custom-envs get MY_API_KEY
# Remove a variable
kilo-entrypoint custom-envs remove MY_API_KEYCustom envs use the same encryption as MCP tokens (AES-256-CBC, keyed by your user ID) and are stored at .local/share/kilo/.custom-envs.env.enc.
To use custom envs from a subshell or script inside a Kilo session:
eval $(kilo-entrypoint print-env 2>/dev/null)
echo $MY_API_KEY# Run directly on a remote host (binary download)
ssh remote-host 'curl -fsSL -o ~/.local/bin/kilo-docker https://github.com/mbabic84/kilo-docker/releases/latest/download/kilo-docker-linux-amd64 && chmod +x ~/.local/bin/kilo-docker && ~/.local/bin/kilo-docker'Use
kilo-entrypoint mcp-tokensto manage MCP server tokens andkilo-entrypoint custom-envsfor custom environment variables.
Add to your ~/.ssh/config:
Host remote
HostName remote.example.com
User username
RequestTTY yes
RemoteCommand ~/.local/bin/kilo-docker
| Tag | Description |
|---|---|
latest |
Most recent stable release (base) |
v{version} |
Exact semantic version (e.g., v1.2.3) |
v{major}.{minor} |
Minor track (e.g., v1.2) |
v{major} |
Major track (e.g., v1) |
# Build Go binaries and Docker image
scripts/build.sh all
# Build Docker image only
scripts/build.sh docker
# Test
docker run --rm kilo-docker version
# Interactive
docker run -it --rm -v $(pwd):/workspace -e PUID=$(id -u) -e PGID=$(id -g) kilo-dockerAfter building the host binary, install it for local development with:
./bin/kilo-docker install-devThis installs the currently running ./bin/kilo-docker binary to ~/.local/bin/kilo-docker, matching the bootstrap installer target.
The build uses a multi-stage Dockerfile: a golang:1.26-bookworm builder compiles the kilo-entrypoint binary as a static binary, then the runtime stage copies it into the final Debian Bookworm image. No Go toolchain is needed on the host.
See LICENSE for details.