CodeLima
June 13, 2026 · View on GitHub
Sandbox agentic coding in real Lima VMs instead of on your host machine.
CodeLima is a Go CLI and shell-first TUI for managing lineage-aware projects, Lima-backed nodes, and reusable environment configs from one control plane. It helps you run coding agents, dev environments, and repo-specific toolchains inside real Linux VMs while keeping your macOS or Linux workstation cleaner, safer, and easier to reason about.
The Problem
Modern coding workflows are powerful, but the default setup is messy:
- running agents with broad permissions directly on the host
- accumulating
apt,npm, language runtimes, and shell-state drift across repos - conflicting toolchains between projects
- accidental writes outside the repo you meant to touch
- slow context switching when you juggle many repos, many environments, and many terminal sessions
How CodeLima Solves It
CodeLima is a thin workflow layer on top of Lima, not a replacement for it. You register host workspaces as projects, create Lima-backed nodes for those projects, choose whether each node gets an isolated guest copy or a live writable mount of the repo, and bootstrap nodes with reusable environment configs.
That gives you:
- real VM isolation for risky agent sessions
- two workspace modes:
copyfor safer experimentation andmountedfor immediate host sync - shared environment configs for Codex, Claude Code, and custom Linux toolchains
- global, project, and node Lima command overrides when a repo or VM needs non-default
limactlflags - one control plane for many repos and many nodes
- a shell-first TUI that keeps reusable project-local and node terminal sessions available while the TUI is running
- direct Lima escape hatches whenever you want to use
limactlyourself
What That Feels Like In Practice
- Run
codexorclaudeinside the VM without giving the same reach to your host. - Keep package installs, shell state, and experiments off your workstation.
- Move between projects and nodes quickly from the CLI or the TUI.
See The Value In 60 Seconds
Once codelima is on your PATH, you can register a repo, create a sandboxed node, and drop into the VM:
codelima project create \
--slug payments \
--workspace /Users/you/src/payments \
--env-config codex \
--agent-profile codex-cli
codelima node create --project payments --slug payments-sandbox --workspace-mode copy
codelima node start payments-sandbox
codelima shell payments-sandbox
Then, inside the VM shell:
codex
Use copy when you want the strongest sandbox boundary around agent actions. Use mounted when you intentionally want the VM to act directly on your host workspace with immediate sync.
Install
Install the latest packaged release from Homebrew:
brew tap brianrackle/codelima
brew install codelima
The Homebrew formula installs the packaged codelima binary plus the bundled libghostty-vt runtime library, and declares git and lima as runtime dependencies.
Supported Systems
Current packaged releases are built for:
- macOS
arm64 - macOS
amd64 - Linux
arm64 - Linux
amd64
Repository-local development and CI are exercised on macOS and Linux.
Prerequisites
- macOS or Linux
curl,tar,git, andmake- a working C toolchain for Go's
cgopath (clangvia Xcode Command Line Tools on macOS, or the equivalent build tools on Linux) - Lima installed and working on the host
make init installs the Go toolchain, golangci-lint, Zig, and a patched libghostty-vt build locally under .tooling/<os>-<arch>; system Go or Zig installs are not required. It also refreshes .tooling/ghostty-vt/current as a compatibility link for the cgo bridge. The per-platform layout avoids host and guest toolchain collisions when the same repository is used from both macOS and a Linux VM.
Build From Source
make init
make build
The platform-native binary is written to ./bin/<os>-<arch>/codelima, for example ./bin/linux-aarch64/codelima or ./bin/darwin-arm64/codelima. make build also refreshes ./bin/codelima as a compatibility symlink to the current platform's build.
To build a distributable archive for the current platform:
make package PACKAGE_VERSION=1.2.3 DIST_DIR=./tmp/dist
That writes a Homebrew-ready tarball plus a JSON manifest under ./tmp/dist.
Quick Start
The examples below assume codelima is installed and available on PATH.
For repository-local development, use make run ARGS="...", the platform-scoped binary under ./bin/<os>-<arch>/codelima, or the refreshed ./bin/codelima compatibility symlink.
Create a project from a host repo:
codelima project create --slug api --workspace /Users/you/src/api
Create a Lima-backed node for that project:
codelima node create --project api --slug api-copy --workspace-mode copy
Start the node and open a shell inside it:
codelima node start api-copy
codelima shell api-copy
Open the TUI instead of running a subcommand:
codelima
Use copy when you want an isolated guest workspace. Use mounted when you want guest edits to appear on the host immediately.
Core Concepts
CodeLima manages:
- projects and immutable workspace snapshots under
CODELIMA_HOME - Lima-backed nodes delegated through
limactl - reusable environment configs that bootstrap new nodes
- a canonical shell surface that passes through to
limactl shell - a shell-first TUI that keeps reusable project-local and node terminal sessions while the TUI process is running
Capabilities
- register host workspaces as lineage-aware projects
- capture immutable snapshots on demand for lineage-aware project workflows
- create, start, stop, clone, inspect, and delete Lima-backed nodes, choosing either an isolated copied workspace or a writable mounted workspace per node
- detect and clean up incomplete node metadata directories left by failed node creation attempts
- create reusable environment configs and assign them to multiple projects as shared bootstrap defaults, including built-in
codexandclaude-codeinstallers - open an interactive shell or run one-off commands inside a node, starting in a guest-local copy of the project workspace that keeps the same absolute path
- browse the project tree, manage selected projects and nodes, and jump between preserved project-local and node sessions in a Ghostty-backed embedded terminal by running
codelimawith no command - switch a focused node terminal to the host-local project terminal and back without losing the selected node context
- turn the existing TUI top bar red while the focused node terminal is temporarily switched to the host machine
- start with one default TUI terminal tab, then explicitly open, switch, and close per-project and per-node tabs with Option keybindings
- keep navigating the tree or focus another preserved project or node terminal while long-running project or node mutations continue in the background
- keep the TUI project tree refreshed automatically while preserving expansion state, selection, and open terminals
- paste multiline text, resize the host window, and sync OSC 52 guest clipboard writes to the host clipboard in the embedded terminal
- inspect local control-plane health with
doctorand resolved defaults withconfig show - view project lineage with attached project nodes via
project tree
Command Structure
Most commands follow this shape:
codelima [--home PATH] [--json] <group> <command> [flags]
Running codelima with no command opens the TUI:
codelima [--home PATH]
Useful global flags:
--home PATHpoints the CLI at a specificCODELIMA_HOME--jsonreturns structured output for automation--log-level LEVELreserves a verbosity setting for future CLI logging
project list renders a compact table by default with slug, uuid, workspace_path, runtime, and agent. node list adds workspace_mode and vm_status so both the workspace binding strategy and live VM state are visible without switching to node show. node cleanup-incomplete also renders a compact table, showing each incomplete node directory plus any recovered Lima instance name. Use --json when you need the full structured payload for scripts.
TUI Quick Start
The TUI opens when you run codelima with no command:
codelima
Basic layout:
+---------------------------+---------------------------------------------+
| Projects / Nodes | [Info] Terminal |
| | |
| ▼ api | Project controls |
| • api-copy STOPPED | Slug: api |
| • api-mount RUNNING | Workspace: /Users/you/src/api |
| ▼ billing | Press i for terminal preview |
| • billing-a RUNNING | |
+---------------------------+---------------------------------------------+
Fast key reference:
Option+\`` orF6`: toggle between tree focus and terminal focusOption+Shift+Backtick: switch a selected node's fullscreen terminal to its host-local project terminal and backOption+t: open a fresh terminal tab for the selected project or node without leaving tree focusOption+Left/Option+Right: switch among the selected project's or node's open terminal tabsOption+w: close the active terminal tab and focus the adjacent tabi: toggle the right pane between info and terminal while the tree is focused- macOS note: the terminal emulator must send Option as Alt/Meta for these bindings (in Ghostty set
macos-option-as-alt = true). CodeLima also accepts the raw macOS Option text sequences (†forOption+t,∑forOption+w, andEsc f/Esc bforOption+Right/Option+Left) when Option is not remapped. q: quit the TUIUp/Down: move selection in the treeLeft/Right: collapse or expand projects in the tree[a]: add project[g]: manage reusable environment configs- on a selected project:
[n]create node,[u]update project,[x]delete project - on a selected node:
[s]start or stop node,[d]delete node,[c]clone node - mouse: click tree entries to select, click links to open them, and wheel-scroll local terminal scrollback when the guest is not capturing the mouse; use your terminal emulator's host-selection bypass gesture for terminal text selection/copy while mouse-aware apps keep receiving guest mouse input
In tree focus, selecting a project or node shows its info pane by default. Press i to switch the split pane to that project's host-local shell or the selected node's guest terminal preview without changing fullscreen terminal focus behavior; stopped nodes still show a terminal-oriented placeholder until you start them.
Project and node forms, menus, and selectors replace the right pane instead of opening centered modals, so the tree stays visible while you work through them. Long-running project and node mutations run in the background, render transient task state in the tree and details pane, and leave the rest of the TUI usable while they finish. The tree also refreshes periodically, so out-of-process node status or metadata changes appear without restarting the TUI.
On launch, CodeLima opens one initial terminal tab for the starting project or running node when that shell can be started, while keeping tree focus and the info pane visible. Additional terminal tabs are explicit and belong to a single project or node. Selecting or visiting items in the tree never opens another tab; press Option+t with a project or running node selected to open a fresh embedded terminal tab for it. Each press opens another tab for that same item, Option+Left/Option+Right switch among the focused item's tabs, and Option+w closes the active one. Closing a tab focuses the next higher-numbered adjacent tab when one exists, otherwise the previous lower-numbered tab. The terminal pane border shows only the focused item's tabs (numbered when there is more than one, with host-local project shells labeled host:<project> and the active tab bracketed); tabs for other projects and nodes stay hidden until their item is focused again, and each item remembers its active tab. Use Option+\`` or F6when you want the active terminal fullscreen. When a node terminal is switched to the host-local project shell withOption+Shift+Backtick`, the existing TUI top bar turns red until you switch back.
Guest applications that emit OSC 52 clipboard writes sync that text to the host clipboard; ordinary visible-text selection still uses your terminal emulator's host-selection bypass gesture.
Create Project form in the right pane:
+--------------------------- Create Project ----------------------------+
| Project Slug: api |
| Workspace Path: /Users/you/src/api |
| Environment Configs: codex |
+--------------------------------------------------------------------+
Create Node form in the right pane:
+---------------------------- Create Node -----------------------------+
| Selected project: api |
| Node Slug: api-copy |
| Workspace Mode: copy: isolated guest workspace copy |
+--------------------------------------------------------------------+
Workflow 1: Manage Many Codebases From One Control Plane
Use one CODELIMA_HOME to track many host workspaces while giving each codebase its own projects and nodes.
CLI:
codelima project create --slug api --workspace /Users/you/src/api
codelima project create --slug billing --workspace /Users/you/src/billing
codelima project create --slug docs --workspace /Users/you/src/docs
codelima project list
codelima project tree
TUI view after registering several repos:
+---------------------------+---------------------------------------------+
| Projects / Nodes | [Info] Terminal |
| | |
| ▼ api | Project controls |
| • api-copy STOPPED | Slug: api |
| ▼ billing | Workspace: /Users/you/src/billing |
| • billing-a RUNNING | |
| ▼ docs | Press i for terminal preview |
| | |
+---------------------------+---------------------------------------------+
Why this helps for agentic coding:
- one place to switch between many repos quickly
- per-project default environments and agent profile metadata
- project and node terminals that stay attached while you move around the tree
Global Lima command defaults live in CODELIMA_HOME/_config/config.yaml.
Project-specific overrides live in the project metadata file shown in the TUI project info pane under CODELIMA_HOME/projects/<project-id>/project.yaml.
Node-specific overrides live in CODELIMA_HOME/nodes/<node-id>/node.yaml, and the TUI node info pane links that file directly.
When a project or node has no overrides yet, its metadata file includes a commented example lima_commands block you can uncomment and edit.
Each lima_commands action is an ordered command list. CodeLima executes the list in order, and higher-precedence overrides replace the whole list for that action. lima_commands.bootstrap runs during the first successful node start and replaces the older project-level environment_commands field.
Global default example:
lima_commands:
create:
- "{{binary}} create -y --name {{instance_name}} --cpus=2 --memory=4 --disk=20 {{template_path}}"
start:
- "{{binary}} start -y {{instance_name}}"
bootstrap: []
workspace_seed_prepare:
- "sudo rm -rf {{target_path}} && sudo mkdir -p {{target_parent}} && sudo chown \"$(id -un)\":\"$(id -gn)\" {{target_parent}}"
clone:
- "{{binary}} clone -y {{source_instance}} {{target_instance}}"
Project override example:
lima_commands:
bootstrap:
- "./script/setup"
- "direnv allow"
workspace_seed_prepare:
- "install -d {{target_parent}} && rm -rf {{target_path}}"
copy:
- "{{binary}} copy --backend=rsync{{recursive_flag}} {{source_path}} {{copy_target}}"
create:
- "{{binary}} create -y --name {{instance_name}} --cpus=6 --memory=12 --disk=80 --vm-type=vz {{template_path}}"
start:
- "{{binary}} start {{instance_name}} --vm-type=vz"
clone:
- "{{binary}} clone -y {{source_instance}} {{target_instance}}"
Node override example:
lima_commands:
start:
- "{{binary}} start {{instance_name}} --tty=false"
copy:
- "{{binary}} copy --backend=rsync{{recursive_flag}} {{source_path}} {{copy_target}} --checksum"
Global, project, and node overrides follow normal precedence in that order. For the very first node create or node clone, use --lima-commands-file or the TUI Lima Commands File field when you need node-specific create, clone, or template_copy overrides to apply before the node metadata file already exists on disk.
Available placeholders depend on the command and include:
{{binary}}{{locator}}{{instance_name}}{{template_path}}{{source_instance}}{{target_instance}}{{source_path}}{{target_path}}{{target_parent}}{{copy_target}}{{recursive_flag}}{{workdir_flag}}{{command_args}}
Workflow 2: Choose How VM Changes Sync Back To The Host
Node creation gives you two workspace modes:
copy: safest for experimentation; the host repo is copied into the VM on first start and guest edits stay in the VMmounted: fastest feedback loop; the host repo is mounted writable and guest edits appear on the host immediately
When a project needs non-default copy-mode seeding, override lima_commands.workspace_seed_prepare for the guest-side directory prep and lima_commands.copy for the host-to-guest transfer itself. The TUI create-node and clone-node dialogs also accept an optional Lima Commands File path for first-create per-node overrides.
CLI:
codelima node create --project api --slug api-copy --workspace-mode copy
codelima node create --project api --slug api-mounted --workspace-mode mounted
codelima node create --project api --slug api-tuned --lima-commands-file ./tmp/api-node-lima.yaml
codelima node start api-copy
codelima node start api-mounted
codelima node list
ASCII comparison:
copy mode mounted mode
--------- ------------
host repo ----copy once----> VM host repo <----live writable----> VM
safe isolation immediate sync to host
best for risky agents best for tight edit/test loops
Practical rule:
- use
copywhen you want the strongest sandbox boundary around a coding agent - use
mountedwhen you intentionally want the VM to act directly on your host workspace
Workflow 3: Run Codex Or Claude In A Sandboxed VM
Fresh homes include:
- environment config
codex - environment config
claude-code - agent profiles
codex-cliandclaude-code
The environment config installs the tool in the VM. The agent profile records which command that project or node expects to run. The built-in Codex and Claude profiles use the plain executable names by default.
The built-in codex environment config installs the Codex npm package under the VM user's ~/.local prefix instead of using sudo npm. After bootstrap, update Codex inside the VM with:
npm update -g @openai/codex
Codex example:
codelima project create \
--slug payments \
--workspace /Users/you/src/payments \
--env-config codex \
--agent-profile codex-cli
codelima node create --project payments --slug payments-codex --workspace-mode copy
codelima node start payments-codex
codelima shell payments-codex
Then, inside the VM shell:
codex
Claude example:
codelima project create \
--slug frontend \
--workspace /Users/you/src/frontend \
--env-config claude-code \
--agent-profile claude-code
codelima node create --project frontend --slug frontend-claude --workspace-mode copy
codelima node start frontend-claude
codelima shell frontend-claude
Then, inside the VM shell:
claude
TUI view after creating the project and node:
+---------------------------+---------------------------------------------+
| Projects / Nodes | Project: payments |
| ▼ payments | Node: payments-codex Mode: copy |
| • payments-codex RUNNING| Environment configs: codex |
| | Open terminal, then run `codex` in the VM |
+---------------------------+---------------------------------------------+
Why this is safer:
- agent actions happen inside the VM
- package installs and shell state stay off the host
- if the agent breaks the VM, you can stop, delete, and recreate the node
copymode keeps accidental file damage out of the host workspace
Workflow 4: Build Custom Environments For Any Agent Or Linux Package Set
Reusable environment configs are just ordered command lists that run when a new node is first bootstrapped. Use them to install editors, CLIs, package managers, or custom agent wrappers.
CLI:
codelima environment create \
--slug devbox \
--bootstrap-command "sudo apt-get update" \
--bootstrap-command "sudo apt-get install -y ripgrep fd-find jq gh" \
--bootstrap-command "curl -fsSL https://mise.run | sh"
codelima environment update devbox \
--bootstrap-command 'mkdir -p "$HOME/.local/bin"' \
--bootstrap-command 'npm config set prefix "$HOME/.local"' \
--bootstrap-command 'PATH="$HOME/.local/bin:$PATH" npm install -g @anthropic-ai/claude-code'
codelima environment show devbox
codelima project create \
--slug tooling \
--workspace /Users/you/src/tooling \
--env-config devbox
TUI flow:
[g] Env Configs
-> Create Config
-> Add Bootstrap Command
-> Move Bootstrap Command
-> Remove Bootstrap Command
[a] Add Project
-> choose Environment Configs: devbox
Good uses for custom environments:
- installing Linux packages for a repo-specific toolchain
- installing your preferred coding agent if it is not one of the built-ins
- installing helper tools such as
gh,just,direnv,uv,pnpm, ordockerclients - encoding repeatable setup once instead of repeating it in every VM by hand
Lima Fallback Examples
Because CodeLima uses Lima instead of inventing a separate VM backend, you can always fall back to limactl if you need something CodeLima does not expose yet:
limactl list
limactl shell <instance-name>
limactl copy <instance-name>:/var/log/cloud-init-output.log ./cloud-init-output.log
limactl copy ./local-config <instance-name>:/tmp/local-config
limactl stop <instance-name>
limactl delete -f <instance-name>
That makes CodeLima a higher-level workflow layer on top of Lima rather than a dead-end abstraction.
CLI Commands At A Glance
Health and config:
codelima doctor
codelima config show
Reusable environments:
codelima environment create --slug NAME --bootstrap-command '...'
codelima environment list
codelima environment show NAME
codelima environment update NAME --bootstrap-command '...'
codelima environment delete NAME
Projects:
codelima project create --slug NAME --workspace /path/to/repo
codelima project list
codelima project show NAME
codelima project update NAME --workspace /new/path
codelima project delete NAME
codelima project tree
codelima project fork SOURCE --slug CHILD --workspace /path/to/child
Global Lima command defaults are stored in CODELIMA_HOME/_config/config.yaml under lima_commands.
Project-specific overrides live in each project's project.yaml under the same key.
Node-specific overrides live in each node's node.yaml under the same key.
Use config show to inspect the effective global defaults, project show to inspect project-specific overrides, node show to inspect node-specific overrides, and the TUI details pane to find the on-disk metadata files quickly.
Nodes:
codelima node create --project PROJECT --slug NODE [--workspace-mode copy|mounted] [--lima-commands-file PATH]
codelima node list
codelima node show NODE
codelima node start NODE
codelima node stop NODE
codelima node clone NODE --node-slug NEW-NODE [--lima-commands-file PATH]
codelima node delete NODE
codelima node status NODE
codelima node logs NODE
codelima node cleanup-incomplete
codelima node cleanup-incomplete --apply
Shell entry:
codelima shell NODE
codelima shell NODE -- uname -a
Make Shortcuts
make run ARGS="doctor"
make run ARGS="config show"
make run ARGS="environment create --slug shared-dev --bootstrap-command ./script/setup"
make run ARGS="project create --slug root --workspace ./test-project-dir --env-config shared-dev"
make run ARGS="node create --project root --slug root-node"
make run ARGS="node start root-node"
make run ARGS="shell root-node -- uname -a"
make tui ARGS="--home /tmp/codelima-dev/.codelima"
make package PACKAGE_VERSION=1.2.3 DIST_DIR=./tmp/dist
make package-formula PACKAGE_VERSION=1.2.3 RELEASE_TAG=v1.2.3 RELEASE_REPO=brianrackle/codelima DIST_DIR=./tmp/dist FORMULA_OUTPUT=./tmp/dist/Formula/codelima.rb
Tooling
make fmt
make lint
make test
make build
make package PACKAGE_VERSION=1.2.3 DIST_DIR=./tmp/dist
make package-formula PACKAGE_VERSION=1.2.3 RELEASE_TAG=v1.2.3 RELEASE_REPO=brianrackle/codelima DIST_DIR=./tmp/dist FORMULA_OUTPUT=./tmp/dist/Formula/codelima.rb
make verify
Local builds are platform-scoped under ./bin/<os>-<arch>/codelima so a macOS host and Linux guest can share one checkout without overwriting each other's executable. The ./bin/codelima path remains a convenience symlink to the platform that last ran make build.
Releases
Local release packaging uses the same make targets as CI:
make package PACKAGE_VERSION=1.2.3 DIST_DIR=./tmp/dist
make package-formula \
PACKAGE_VERSION=1.2.3 \
RELEASE_TAG=v1.2.3 \
RELEASE_REPO=brianrackle/codelima \
DIST_DIR=./tmp/dist \
FORMULA_OUTPUT=./tmp/dist/Formula/codelima.rb
make package builds a platform-native archive that contains:
bin/codelimaas a small launcher that pointsCODELIMA_GHOSTTY_VT_LIBat the packaged Ghostty librarybin/codelima-realas the compiled Go binarylib/libghostty-vt.{dylib,so}as the runtime terminal backend<asset>.jsonas the manifest used to generate the Homebrew formula
The repository ships two GitHub Actions workflows:
.github/workflows/ci.ymlrunsmake verifyon Ubuntu and macOS for pushes tomainand pull requests.github/workflows/release.ymlbuilds release archives fordarwin-amd64,darwin-arm64,linux-amd64, andlinux-arm64, creates or updates the GitHub release for the tag, uploads the archives and manifests, and then updates a Homebrew tap when the required repository settings are present
To enable automatic tap updates, configure:
- repository variable
HOMEBREW_TAP_REPO, for examplebrianrackle/homebrew-codelima - optional repository variable
HOMEBREW_TAP_BRANCH, which defaults tomain - repository secret
HOMEBREW_TAP_TOKENwith permission to push to the tap repository
Once those are in place, releasing a new Homebrew version is:
git tag v1.2.3
git push origin v1.2.3
The release workflow publishes the assets and updates Formula/codelima.rb in the tap. End users then upgrade with brew update && brew upgrade codelima.
Documentation
Keep README.md focused on user-facing setup, capabilities, workflows, and command examples.
Internal documentation for design, maintenance, and tooling should live in BUILD.md.
Smoke Test
The smoke test uses the real limactl binary and the repository fixture in test-project-dir to create and manage three VM layers inside one project:
make smoke
The script:
- creates a root project bound to
test-project-dir - creates and starts a root Lima-backed node
- clones that node into a second node in the same project
- clones the second node into a third node in the same project
- prints the resulting project tree and node list
Metadata Layout
By default the CLI stores metadata in ~/.codelima:
~/.codelima/
_config/
_locks/
_index/
projects/
nodes/
Override the location with --home or CODELIMA_HOME.
Notes
config showdisplays the active defaults and resolved paths.- Built-in
codex-cliandclaude-codeagent profiles define the default launch command names. Pair them with the matchingcodexorclaude-codeenvironment config so the executable is actually installed in the VM.