Run AI coding agents in sandboxed LXD containers. Your project directory is bind-mounted in, so the agent sees your files but runs fully isolated from your host.
Use terminal agents like Claude Code, Codex, and Aider from the host — or launch GUI agents like Cursor directly inside the container with X11/Wayland forwarding. Either way, your code stays on your machine and the agent stays in its sandbox.
# Build from source
git clone https://github.com/popey/lincubate
cd lincubate
just build
# Copy ./lb somewhere on your PATH
# Launch Claude Code in a container for the current project
cd ~/Projects/MyApp
lb claude
# Drop into a bare shell inside the container
lb
# Launch Cursor IDE inside the container (GUI)
lb cursor-guiOn first run, lb builds a base image with Node.js, common packages, and all agents pre-installed. This takes a few minutes — subsequent launches are fast.
- One container per project. Container names are derived from your working directory, so
lbfrom the same directory always targets the same container. - Bind-mounted files. Your project directory is mounted into the container at
/home/ubuntu/projectwith UID mapping, so file ownership just works. - Credential forwarding. API keys are forwarded via environment variables. Auth tokens (OAuth, config files) can be optionally copied or mounted read-only — all off by default.
- GUI support. Pass
--gui(or usecursor-guiwhich enables it automatically) to forward X11/Wayland and GPU access into the container.
| Agent | Type | Command |
|---|---|---|
| Claude Code | Terminal | lb claude |
| OpenAI Codex | Terminal | lb codex |
| Aider | Terminal | lb aider |
| Gemini CLI | Terminal | lb gemini |
| GitHub Copilot | Terminal | lb copilot |
| OpenCode | Terminal | lb opencode |
| Cursor (CLI) | Terminal | lb cursor |
| Cursor (IDE) | GUI | lb cursor-gui |
You can also just run lb with no agent to get a shell inside the container.
- Linux with LXD installed and running
- Go 1.26+ (building from source)
lb works with zero configuration — sensible defaults are built in. To customise things, generate a config file and edit it:
lb generate-config
# Writes ~/.config/lincubate/lb.toml — edit this file to change behaviourThe generated file is fully commented and includes all available options. Here are some common things you might want to change:
The base image comes with curl, git, build-essential, and unzip. If your project needs extra tools, add them:
apt_packages = ["curl", "git", "build-essential", "unzip", "postgresql-client", "redis-tools", "jq"]After changing this, rebuild the base image: lb base-destroy then run lb again.
By default all agents are installed in the base image. If you only use a couple, you can speed up the initial build:
base_agents = ["claude", "aider"]
# Or disable all: base_agents = "none"API keys and tokens are forwarded from your host into the container. The defaults cover the main providers, but you can add your own:
credential_env = [
"ANTHROPIC_API_KEY",
"OPENAI_API_KEY",
"GEMINI_API_KEY",
"GITHUB_TOKEN",
"AWS_ACCESS_KEY_ID",
"AWS_SECRET_ACCESS_KEY",
"DATABASE_URL",
]Any variable in this list that is set on your host will be available inside the container.
Credential sharing is off by default. If you want an agent to reuse your host login (so you don't have to re-authenticate inside the container), enable it:
# IMPORTANT: [sections] must come last in TOML — all top-level keys above.
[claude]
allow_auth = true # Copy OAuth credentials into container
[cursor]
allow_auth = true # Copy Cursor auth into containerOr use CLI flags for one-off sessions without changing the config:
lb claude --allow-claude-authIf you want something to happen every time a container starts (e.g. pull latest dotfiles, update tools), use updaters:
updaters = ["cd ~/dotfiles && git pull -q", "pip install --upgrade mytools"]Config is loaded from the first match:
-c FILEflaglb.tomlnext to the binary (handy for per-project configs)$XDG_CONFIG_HOME/lincubate/lb.toml(default:~/.config/lincubate/lb.toml)- Built-in defaults
| Command | Description |
|---|---|
lb [agent] |
Launch agent or shell in the project container |
lb stop |
Stop the current project's container |
lb all-stop |
Stop all lincubate containers |
lb upgrade |
Run upgrade commands in the current container |
lb base-upgrade |
Upgrade the base image in-place |
lb snapshot |
Stop, publish as base image, restart |
lb destroy |
Delete the current project's container |
lb base-destroy |
Delete the base image (next run rebuilds) |
lb generate-config |
Write default config file |
just build # Build the binary
just test # Run tests
just release # Cross-compile linux/amd64 + linux/arm64