Docker sandbox for AI coding agents. Single binary, runs on Linux and macOS (ARM and x86).
Asylum wraps Docker to give Claude Code, Gemini CLI, and Codex a full development environment with Python, Node.js, Java, and Docker-in-Docker β while keeping your host clean. Containers are ephemeral, but caches, auth, and history persist.
curl -fsSL https://raw.githubusercontent.com/inventage-ai/asylum/main/install.sh | shOr download a binary from the releases page and put it in your PATH.
Requires: Docker installed and running.
cd your-project/
# Start Claude Code in YOLO mode (default)
asylum
# Start Gemini CLI
asylum -a gemini
# Start Codex
asylum -a codex
# Interactive shell (no agent)
asylum shellOn first run, Asylum builds a Docker image (~5 min) and seeds agent config from your host. Subsequent runs start in seconds.
asylum [flags] Start default agent
asylum [flags] -- [args] Start agent with extra args
asylum [flags] shell Interactive zsh shell
asylum [flags] shell --admin Admin shell with sudo notice
asylum [flags] run <cmd> Run command in container
asylum ssh-init Set up SSH keys for containers
asylum self-update [--dev] Update to latest version
| Flag | Description |
|---|---|
-a, --agent |
Agent to use: claude, gemini, codex (default: claude) |
-p <port> |
Forward a port (repeatable, e.g. -p 3000 -p 8080:80) |
-v <volume> |
Mount a volume (repeatable, e.g. -v ~/data:/data:ro) |
-e KEY=VALUE |
Set environment variable (repeatable, last wins) |
--java <version> |
Select Java version (17, 21, 25 pre-installed; others installed on demand). Auto-detected from .tool-versions. |
-n, --new |
Start a fresh session (skip auto-resume) |
--rebuild |
Force rebuild the Docker image |
--skip-onboarding |
Skip project onboarding tasks for this run |
--cleanup |
Remove Asylum images and cached data |
--version |
Show version |
Use -- to pass extra flags to the agent (e.g. asylum -- --verbose). Use run to execute commands in the container (e.g. asylum run python test.py).
Asylum reads config from three YAML files, merged in order:
| Priority | File | Purpose |
|---|---|---|
| 1 (lowest) | ~/.asylum/config.yaml |
Global defaults |
| 2 | .asylum |
Project config (commit this) |
| 3 (highest) | .asylum.local |
Local overrides (gitignore this) |
CLI flags override all config files.
agent: gemini
ports:
- "3000"
- "8080:80"
volumes:
- ~/shared-data:/data:ro
env:
MY_API_KEY: "abc123"
DEBUG: "true"
versions:
java: "17"
tab-title: "π€ {project}" # Terminal tab title ({project}, {agent}, {mode})
features:
session-name: true # Name new Claude sessions after project dir
allow-agent-terminal-title: true # Let the agent set the terminal tab title
onboarding:
npm: true # Auto-install Node.js deps on first container start
packages:
apt:
- libpq-dev
- redis-tools
npm:
- "@anthropic-ai/claude-mcp-server-filesystem"
pip:
- pandas
- numpy
run:
- "curl -fsSL https://deno.land/install.sh | sh"Asylum sets the terminal tab/window title before starting the container. The default is π€ projectname. Customize it with tab-title using placeholders:
| Placeholder | Value |
|---|---|
{project} |
Project directory basename |
{agent} |
Agent name (claude, gemini, codex) |
{mode} |
Mode (agent, shell, admin, run) |
By default, Claude Code is prevented from overriding this title. Set allow-agent-terminal-title: true in features to let it.
Asylum can automatically run setup tasks (like npm install) when a container is first created. Onboarding is opt-in per task:
onboarding:
npm: true # Auto-install Node.js dependencies (detects lockfiles)When enabled, asylum scans for lockfiles (package-lock.json, pnpm-lock.yaml, yarn.lock, bun.lock), shows a prompt listing what it found, and runs the appropriate install command inside the container. State is tracked β it won't re-prompt unless a lockfile changes.
Skip onboarding for a single run with --skip-onboarding, or disable it globally with features: { onboarding: false }.
Boolean flags in features control opt-in/opt-out behaviors:
| Flag | Default | Description |
|---|---|---|
session-name |
false |
Name new Claude sessions after the project directory |
allow-agent-terminal-title |
false |
Let the agent set the terminal tab title (overrides asylum's tab-title) |
shadow-node-modules |
true |
Shadow node_modules with named volumes to isolate host native binaries |
onboarding |
true |
Enable the onboarding system (individual tasks still need opt-in via onboarding map) |
- Scalars (agent, java version, tab-title): last value wins
- Lists (ports, volumes): concatenated across layers
- Maps (env, versions, features): merged per key, last value wins
- Package lists (apt, npm, pip, run): each sub-list concatenated independently
Asylum uses a two-tier image strategy:
- Base image (
asylum:latest) β full dev toolchain with all three agent CLIs, shared across projects - Project image (
asylum:proj-<hash>) β adds packages from your config, only built whenpackagesis set
Images auto-rebuild when the Dockerfile or your packages config changes (hash-based detection).
- Languages: Python 3 + uv, Node.js LTS + fnm, Java 17/21/25 + mise
- Build tools: gcc, g++, make, cmake
- Package managers: npm, yarn, pnpm, pip, uv, poetry, maven, gradle
- Dev tools: git, vim, nano, tmux, htop, ripgrep, fd, jq, yq, direnv
- Git tools: gh (GitHub CLI), glab (GitLab CLI)
- Docker: Full engine with buildx and compose (Docker-in-Docker)
- Shell: zsh with oh-my-zsh
- Agents: Claude Code, Gemini CLI, Codex
Your project is mounted at its real host path inside the container (not /workspace). This preserves absolute paths and makes git worktrees work correctly.
| What | Host Path | Scope |
|---|---|---|
| Project directory | $PWD |
Per project |
| Git config | ~/.gitconfig |
Global (read-only) |
| SSH keys | ~/.asylum/ssh/ |
Global |
| Agent config | ~/.asylum/agents/<agent>/ |
Global (per agent) |
| Package caches (npm, pip, maven, gradle) | Named Docker volumes | Per project |
| Shell history | ~/.asylum/projects/<id>/history |
Per project |
| Direnv approvals | ~/.local/share/direnv/allow |
Global (read-only) |
.env file |
$PWD/.env |
Per project (env vars only) |
On first run, agent config is seeded (one-time copy) from the host native config directory (e.g. ~/.claude β ~/.asylum/agents/claude/). After that, the asylum copy is independent β changes to the host config won't propagate.
On macOS, Node.js native binaries built on the host won't work inside the Linux container. Asylum automatically shadows node_modules directories with named Docker volumes so each platform has its own binaries. This is transparent β your source files are shared, only node_modules is isolated.
Disable with features: { shadow-node-modules: false }.
Multiple terminal sessions can share a single container. The first asylum invocation starts the container; subsequent ones exec into it. The container is automatically removed when the last session exits.
Agents auto-resume previous sessions by default. Use -n to start fresh. Session detection is per-project (keyed by absolute path).
asylum ssh-initCreates ~/.asylum/ssh/ with an Ed25519 key pair. Add the public key to GitHub/GitLab, or replace with your own keys. These are mounted into every container.
asylum --cleanupRemoves Asylum Docker images and optionally clears caches. Agent config (~/.asylum/agents/) is preserved since it contains auth tokens.
asylum self-update # Update to latest stable release
asylum self-update --dev # Update to latest dev build from main
asylum self-update --safe # Emergency update (always dev, no checks)To always track dev builds, set release-channel: dev in your config:
# ~/.asylum/config.yaml
release-channel: devgit clone https://github.com/inventage-ai/asylum.git
cd asylum
make build # Build for current platform
make build-all # Cross-compile all targets
make test # Run testsMIT