Worktrees isolate the code, not the runtime: one machine, many coding agents
Worktrees solved parallel checkouts; nothing yet solves shared ports and databases. How to set up one Linux machine for many parallel AI coding agents.
Last updated
On Saturday a coding agent patched a file in my working tree while I was using it. Its task was to examine the repo, not fix it, but it inherited a permission mode that allowed writes, found a bug, and closed it, and I learned about the patch from git status. The patch was correct, which is its own story. The setup lesson stands regardless: the moment more than one actor works a checkout, the checkout is a shared mutable resource, and mine was configured like private property.
That collision was code-level, and code-level turns out to be the solved part. Before rebuilding my own setup I went through what everyone else had landed on: the vendor docs, the practitioner writeups, the postmortems people publish after their second agent tramples their first. The picture that emerges is layered: worktrees answer parallel checkouts, containers answer runtime collisions, and a sandbox-to-VM ladder answers how far you trust the agent. The most useful part is the seam that’s still open between the first two.
Worktrees won
Git worktrees are the settled answer to parallel checkouts, and the tooling has stopped treating them as a power-user move. Claude Code creates one per session behind a flag (--worktree), its desktop app creates one for every session by default, and subagents can get throwaway worktrees that delete themselves if nothing changed (worktree docs). The design goal is stated flatly: edits in one session never touch files in another. Users now file issues asking how to turn the default off, which tells you which way the ratchet moved.
The economics cooperate. Worktrees share the git object database, so five of them on a 500 MB repo cost five sets of working files, not 2.5 GB of clones.
Two defaults bite in the first week. A new worktree branches from origin/HEAD, not from the branch you’re standing on. And gitignored files stay behind (no .env, no local certs) unless a .worktreeinclude lists them. Budget an hour for both now or an afternoon for them later.
When I later set my own harness loose on its own bugs, my attempt and its attempt each ran in an isolated worktree on its own branch, and merging the best of both afterward was ordinary git. That part of the problem now takes zero discipline. The rest of this note is about the parts that still take some.
The part worktrees don’t touch
Here is the gap, and everyone running parallel agents hits it eventually: a worktree scopes the working directory, HEAD, index, and per-worktree refs. Nothing else. Ports, databases, caches, and test state are shared across every worktree on the machine. Two branches can have immaculate checkouts and still both want localhost:3000, or both run migrations against the same Postgres.
Docker Compose doesn’t close this on its own. It has the mechanisms (project names, port offsets, named volumes) and no policy for assigning them per worktree, so the assignment is yours to invent, per project, forever. Small tools are sprouting in the gap; none has won yet, and the two most alive get their own section below.
What contention actually costs is attribution. In our autonomous-build experiments, a green suite only counted if it reproduced from a clean checkout, because the agent’s own account of its environment is not evidence. Runtime contention breaks the same property from the other side: a red suite stops meaning “the code is wrong” and starts meaning “the code is wrong, or the port was taken, or a sibling agent left a stale volume.” A human pauses and checks. An agent picks one and fixes it, confidently.
What people actually assemble
The most complete single-machine setup I found written up (kenev.net, February 2026) is one devcontainer per worktree, and it earns its pieces one failure at a time. A worktree’s .git is a file holding an absolute host path; inside the container that path doesn’t exist and git dies, so the fix is symlinks inside the container that recreate the host path. The same failure appears in the devcontainers CLI, VS Code, and DevPod issue trackers; it’s structural, not one blogger’s bad Tuesday. Host ports stop colliding because nothing maps any: a single Traefik proxy listens on port 80 and routes per-worktree subdomains off Docker labels. And the machine survives N environments because the heavy parts (database server, proxy, network, images) exist once, shared, with databases partitioned logically per worktree.
The dissent is worth as much as the pattern. Arcjet abandoned devcontainers after context switches cost up to 20 minutes in rebuilds, and now clones a pre-built OrbStack VM per workspace in 5–10 seconds. But look at what survived their switch: one shared database layer, because per-workspace Postgres plus ClickHouse on a laptop is fantasy. The container camp and the VM camp disagree about everything except the compromise. Isolate compute per agent; share stateful services, partitioned.
One more choice from the same writeup matches how we run things: the agent is installed in the container, not on the host. Containerizing the app while the agent roams the host buys the overhead without the point. The point was granting the agent broader permissions inside a boundary you chose.
The routes that don’t go through devcontainers
Devcontainers are the best-documented route, not the only strong one. Four alternatives have real code behind them.
The Nix route got agent-aware this year. devenv.sh will provision a project’s entire Claude Code setup (hooks, slash commands, sub-agents, MCP servers) from one declarative option in devenv.nix, claude.code.enable = true, with no container anywhere. For the jail itself there’s agent-sandbox.nix: nix develop hands you a claude-sandboxed binary running under bubblewrap with an ephemeral tmpfs home, so the agent sees the project directory, the packages you allowlisted, and nothing else. No SSH keys, no browser sessions, no sibling projects. It’s small (a hundred-odd stars), and it’s an allowlist, which is the right polarity. One caution that travels with this territory: pinning through flake.lock helps and does not make environments identical across machines; host state still leaks in.
System containers are the heavyweight option nobody talks about. code-on-incus gives each agent an Incus container that behaves like a full machine: root, systemd, Docker inside, permission to install whatever it wants. One clean isolation layer instead of containers nested in VMs, and it’s the most actively developed tool in this list (592 stars, a thousand-plus commits, released again this week). The kernel is still shared, so read it as a fatter container, not a VM.
Podman earns a line for the reason it always does: no root daemon. The pattern documented in the wild is a rootless container whose only writable host mount is the project checkout, with git as the actual safety net (commit before the agent runs, diff after, reset --hard if you hate what you see). The published version of that setup runs --network host, which you should notice: complete filesystem isolation, zero network isolation. The ecosystem signal points the same way; DDEV made Podman support official in February, and its own matrix rates it “slow compared to Docker”. Secure default, performance tax.
And the runtime-gap tools grew names worth checking. worktree-compose gives every worktree its own Compose project with a deterministic port formula (20000 plus the service’s default port plus the worktree index) and ships an MCP server so the agent can start and stop its own stack. Coasts is the funded entrant: canonical ports on your main checkout, dynamic ports for the others, about 200MB per containerized host by the maintainer’s own numbers, macOS-first for now. Both are young, and both mean “isolation” in the port-and-project-name sense on one shared Docker daemon. Useful, and not sandboxes.
At the far end sits the no-container position, and it’s quick. Bas Nijholt’s agent-cli makes a worktree, installs dependencies with whatever the project already uses, writes the direnv .envrc, and opens a terminal with Claude Code running, in about four seconds; he reports five or six parallel sessions on one machine as routine. One practitioner describing his own tool, but it’s the clearest written version of the bare-metal stance. Anthropic’s sandbox-runtime (next section) runs standalone via npx @anthropic-ai/sandbox-runtime against any of these setups, so bare metal doesn’t have to mean unjailed.
What I couldn’t find was writing of the same quality for mise-only setups, distrobox, Lima VM-per-worktree on Linux, or DevPod’s current health. Absence of writeups isn’t absence of users, but down those roads you’ll be pathfinding.
How much cage the agent needs
This is the best-documented layer, because Anthropic wrote down where each rung of its own ladder fails.
The bottom rung is OS-level sandboxing: bubblewrap on Linux, Seatbelt on macOS, open-sourced as sandbox-runtime. Filesystem writes are scoped to the working directory; network egress goes through a proxy enforcing a domain allowlist; the rules bind every subprocess the agent spawns, not just its direct calls (engineering post). Anthropic reports an 84% drop in permission prompts internally (their figure, no published methodology). The fine print is load-bearing: the sandboxed Bash tool constrains Bash only, while MCP servers and hooks run unconstrained on the host, and the boundary has had real holes: a symlink escape (CVE-2026-39861) and a SOCKS5 allowlist bypass, both patched in 2026. A sandbox is a design, not a proof.
The middle rung is mandatory in exactly one documented case, and the vendor’s language is unambiguous: --dangerously-skip-permissions always runs inside a container, VM, or the sandbox runtime, because once per-action review is off, isolation is the only limit left. Know what a container doesn’t do: if the credential is inside the boundary and the agent can read it, the agent can ship it somewhere, and the container only decides what else is in blast range. Scoping what the agent holds is an identity problem before it’s an infrastructure problem.
The top rung, dedicated VMs and microVMs, is for code you didn’t write and don’t trust, where sharing a kernel is the exposure. It costs less than its reputation: Firecracker boots in roughly 125 ms with under 5 MiB of overhead per VM, and commercial agent sandboxes (E2B, Daytona) restore pre-warmed snapshots in tens to hundreds of milliseconds. On a workstation running your own repos, you’ll rarely climb this high.
Pick the rung from the threat model. It’s the same Least Agency arithmetic that scopes permissions, pointed at infrastructure: authority the agent doesn’t need is authority you have to defend.
The market voted with a rebrand
Gitpod spent five years selling cloud dev environments to humans. In September 2025 it renamed itself Ona, shut down the classic product that October, and rebuilt around one ephemeral isolated environment per agent (InfoQ’s coverage). The company that knew the dev-environment business best concluded the durable product is sandboxes for agents. Daytona ran the same play from the other side: pivoted its dev-environment product into a sandbox platform for AI-generated code, raised $24M on it, and closed the source in June 2026, leaving DevPod as the last self-hosted devcontainer-spec tool standing. Two vendors, one verdict.
The transferable part is the file format. Ona environments are declared in devcontainer.json, the same spec VS Code, JetBrains, Codespaces, Coder, and DevPod read, so one definition can back your laptop’s worktree containers and a cloud fleet. The vendor-specific automation sits in side files. The base spec travels with the repo.
The toolchain bracket, briefly
On toolchains, the writeups all point the same direction without much hard evidence behind any of it, so take this as consensus rather than fact: mise for pragmatists (asdf-compatible, absorbs direnv’s env-var job, ships a task runner), Nix where reproducibility has to be provable rather than probable, and mise locally with a Nix shell in CI for teams that want both. There’s also a live contrarian camp on bare metal with version managers, Docker demoted to running Postgres and Redis, arguing that a Dockerfile’s FROM line drifts under you and a full rebuild per dependency bump is a tax. For trusted solo projects, that camp is not wrong.
What I’d run
For one Linux workstation, several projects, several agents, in order of escalating need:
- Trusted repos, human watching. mise and direnv on the host, Compose for the databases, a worktree per agent session, the built-in sandbox on. Cheapest in RAM and in ceremony.
- Semi-autonomous agents, multiplying projects. A devcontainer per worktree; zero host port mappings, one proxy with per-worktree subdomains, one shared logically-partitioned database server, agent installed inside the container. Keep the devcontainer.json canonical so the definition follows the repo to any cloud runner unchanged.
- Unattended or untrusted. Container or VM without exception, per the vendor’s own instruction; microVM when the code itself is the threat.
Decide the boundaries before the fleet grows, because retrofitting isolation onto six running agents is how working trees get mangled. Sizing those boundaries (what each agent may touch, where verification runs, which failures belong to the environment and which to the code) is a large part of how we scope agentic delivery engagements. The environment is part of the harness, and the harness is what you’re actually operating.
Mine got rearranged by a single, well-behaved agent that was only supposed to look.
Questions this raises
Straight answers.
- Are git worktrees enough to isolate parallel AI coding agents?
- They isolate the checkout (working directory, HEAD, index, per-worktree refs) and nothing else. Ports, databases, caches, and test state stay shared, so two branches with immaculate checkouts can still fight over localhost:3000 or run migrations against the same Postgres. Worktrees are the right first layer. The runtime layer is a separate decision, and no standard tool makes it for you yet.
- Do I need containers to run coding agents in parallel?
- Not on day one. For trusted repos with a human watching, worktrees plus the agent's built-in OS sandbox (Claude Code uses bubblewrap on Linux, Seatbelt on macOS) is a light setup that works. Containers earn their cost when agents run semi-autonomously, when projects multiply, or when runtime collisions start making test failures ambiguous: an agent that can't tell a taken port from a real regression will fix the wrong one confidently. The pattern practitioners converge on is one devcontainer per worktree with shared stateful services behind a reverse proxy.
- When does an agent need a VM instead of a container?
- Anthropic's own documentation draws two lines. Fully unattended operation (--dangerously-skip-permissions) must always run inside a container, VM, or the sandbox runtime, because once per-action review is off, isolation is the only limit left. And untrusted code gets a dedicated VM, since containers share the host kernel. The VM tier costs less than its reputation: Firecracker microVMs boot in about 125ms with under 5 MiB of overhead each.
- What are the strongest alternatives to devcontainers for agent environments?
- Four have real code behind them. Nix: devenv.sh provisions a project's Claude Code setup (hooks, MCP servers, sub-agents) from one declarative option, and agent-sandbox.nix wraps the agent in a bubblewrap jail with an ephemeral home directory. Incus system containers: code-on-incus gives each agent a full machine (root, systemd, Docker inside) on a shared kernel. Rootless Podman with the project checkout as the only writable mount and git as the rollback. And bare metal: worktrees plus direnv, which practitioners use to run five or six parallel Claude Code sessions with about four seconds of setup per worktree. Anthropic's sandbox-runtime layers OS-level restrictions onto any of them.
- What's the most portable way to define an agent's environment?
- devcontainer.json. It's the one spec read by VS Code, JetBrains, GitHub Codespaces, Coder, DevPod, and Ona's cloud agent sandboxes, so a single definition can back a devcontainer-per-worktree setup on your machine and a sandbox-per-agent fleet in the cloud. Vendor automation layers (like Ona's automations.yml) are proprietary; the base file travels.
Agentic Delivery
The writeup has a service behind it.
If this is your situation, the agentic delivery is where it gets fixed — by the person who wrote this.