Run Claude Code in isolated Lima VMs. Dangerously skip permissions safely.
- macOS with Apple Silicon or Intel
- Lima (
brew install lima) - Python 3
- jq (
brew install jq) - tmux (
brew install tmux)
brew install lima jq tmuxThe tools VM is a base image with dev tools and Claude Code pre-installed. Project VMs are cloned from it, so they start fast.
./rebuild-tools-vmThis creates a VM named tools-vm from tools-vm.yaml. It takes a while on first run (downloads Ubuntu, installs toolchains). You might need to tweak the resources usage of the tools VM for your hardware.
Add to your ~/.bashrc or ~/.zshrc:
export CLAUDE_SANDBOX_BASE_VM=tools-vmThis tells claude-sandbox to clone project VMs from the tools VM.
Hook events from Claude Code (e.g. "waiting for input") are forwarded from the VM to the host and displayed as macOS desktop notifications with sound.
The sandbox's Claude settings dir (~/.claude-sandbox/.claude) is mounted as ~/.claude inside the VM. Configure hooks there:
mkdir -p ~/.claude-sandbox/.claude/hooks
cp hook.sh ~/.claude-sandbox/.claude/hooks/hook.shCreate ~/.claude-sandbox/.claude/settings.json:
{
"hooks": {
"Notification": [
{ "matcher": "*", "hooks": [{ "type": "command", "command": "~/.claude/hooks/hook.sh" }] }
],
"Stop": [
{ "matcher": "*", "hooks": [{ "type": "command", "command": "~/.claude/hooks/hook.sh" }] }
]
}
}Note: paths use ~/.claude/ (not ~/.claude-sandbox/.claude/) because that's the mount point inside the VM.
./claude-sandbox -d ~/projects/myappclaude-sandbox # Start Claude in the current directory
claude-sandbox -d ~/projects/myapp # Start in a specific project
claude-sandbox shell # Open a shell in the VM
claude-sandbox -- --resume # Pass arguments to Claude CLI
claude-sandbox stop # Stop the current project's VM
claude-sandbox stop-all # Stop all VMs and hooks server
claude-sandbox delete # Delete the current project's VM
claude-sandbox prune # Delete all project VMs
claude-sandbox prune <prefix> # Delete VMs starting with prefix
claude-sandbox restart-hooks # Restart the hooks server
claude-sandbox status # Show all VMs and hooks server status- Tools VM: A base Lima VM with dev tools and Claude Code pre-installed (tools-vm.yaml)
- Project VMs: Cloned from the tools VM per project directory, with the project mounted read-write
- Claude Execution: Runs Claude Code inside the VM with
--permission-mode bypassPermissions - Hooks: A host-side server (claude-hooks-server) receives events via a unix socket forwarded by Lima and sends desktop notifications
Claude settings are stored in ~/.claude-sandbox/.claude/ on the host, mounted into each VM as ~/.claude. This persists settings across VM recreations and shares them between VMs. You could set CLAUDE_SANDBOX_SETTINGS=~/.claude to use your host settings, but this gives the AI a path to escape the VM.
| Command | Description |
|---|---|
| (none) | Start Claude in the VM |
shell |
Open a shell in the VM |
stop |
Stop the project's VM |
stop-all |
Stop all sandbox VMs |
restart-hooks |
Restart the hooks server |
status |
Show all sandbox VMs status |
delete |
Delete the project's VM. Including Claude sessions |
prune [PREFIX] |
Delete all project VMs (optionally matching PREFIX) |
| Option | Description |
|---|---|
-d, --dir DIR |
Project directory (default: current directory) |
-s, --settings DIR |
Claude settings directory (default: ~/.claude-sandbox/.claude) |
-c, --clone-from VM |
Clone from an existing VM |
-t, --template YAML |
Create from a YAML template (default: template:default) |
--voice |
Enable voice mode (proxy host mic into VM) |
| Variable | Description |
|---|---|
CLAUDE_SANDBOX_BASE_VM |
Default VM to clone from |
CLAUDE_SANDBOX_TEMPLATE |
Default template to use |
CLAUDE_SANDBOX_SETTINGS |
Claude settings directory |
CLAUDE_SANDBOX_VOICE_MODE |
Set to true to enable voice mode (see below) |
claude-sandbox- Thin bash wrapper that execs the command produced byclaude_sandbox.pyclaude_sandbox.py- Main script (pure Python stdlib; runnable directly without a venv)claude-sandbox.yaml- Lima VM settings (for reference/documentation)claude-hooks-server- Hooks server that sends desktop notificationshook.sh- Hook script that runs inside the VM and forwards events to the hooks servertools-vm.yaml- Lima VM template with dev tools and Claude Code pre-installedrebuild-tools-vm- Script to recreate the tools VM fromtools-vm.yamltests/- pytest suite (run withuv run pytestfromtests/)
The test suite lives in tests/ and is managed with uv. It runs the
claude-sandbox Python wrapper under a fake-binary harness (see
tests/fakes/) and asserts the external-tool calls (limactl, tmux, ps,
uuidgen, nohup, …) it emits.
cd tests
uv sync
uv run pytest
uv run ruff check ../claude_sandbox.py .
uv run ty check ../claude_sandbox.py .End-to-end integration tests (which boot a real Lima VM and exercise the full lifecycle) are excluded from the default run. Opt in with:
uv run pytest -m integrationThey are auto-skipped when limactl isn't on PATH or when hardware
virtualization is unavailable (no vz on macOS, no /dev/kvm on Linux).
Voice mode proxies the host Mac's microphone into the VM so Claude's /voice command works. It's disabled by default and must be opted in:
claude-sandbox --voice # One-off
export CLAUDE_SANDBOX_VOICE_MODE=true # Permanent (add to ~/.zshrc)Requires SoX on the host (brew install sox).
Since the project directory is shared between host and guest, build environments can conflict — especially when host and guest run on different architectures (e.g. macOS arm64 host vs Linux x86_64 guest). Tools like uv will install platform-specific packages into .venv, which won't work on the other side.
Add environment variables to ~/.claude-sandbox/.claude/settings.json (or the project's .claude/settings.json) to isolate the guest's build artifacts:
{
"env": {
"UV_PROJECT_ENVIRONMENT": ".venv-agent"
}
}This makes uv use .venv-agent inside the VM instead of .venv. Consider similar isolation for other tools (e.g. separate build directories, cache paths).
If you're running long tasks, prevent your Mac from sleeping with caffeinate:
caffeinate -i -t 7200This prevents the idle sleep (-i) from activating. Kill the background process when you're done.
The tools VM resource allocation is configured in tools-vm.yaml:
cpus: 11
memory: "40GiB"
disk: "100GiB"To adjust resources for your machine:
- Edit
tools-vm.yamland modify thecpus,memory, ordiskvalues - Rebuild the tools VM:
./rebuild-tools-vm
Note: Project VMs cloned from the tools VM inherit these settings. If you adjust resources, existing project VMs won't change—only new ones will use the updated configuration.
Claude settings are stored in ~/.claude-sandbox/.claude/ on the host and mounted into each VM as ~/.claude. This allows settings to persist across VM recreations and be shared between VMs. You can add a CLAUDE.md, settings.json, etc. there and they'll be available in every sandbox.