# Git Worktrees or One VM per Agent for Parallel Coding Agents?

> Use git worktrees when parallel agents only edit and test code. They separate files but share one machine's ports, hooks, home directory, credentials and services. When agents run dev servers, databases or Docker, give each one its own VM, so every agent can bind port 3000 and gets its own URL.

Source: https://sandboxer.com/guides/git-worktrees-or-one-vm-per-agent. Updated 2026-10-27.

**Short answer:** Use git worktrees when parallel agents only edit and test code. They separate files but share one machine's ports, hooks, home directory, credentials and services. When agents run dev servers, databases or Docker, give each one its own VM, so every agent can bind port 3000 and gets its own URL.

This page is the decision. For the step-by-step setup of one VM per agent, see [Run several coding agents in parallel](/guides/run-coding-agents-in-parallel).

## What a worktree separates, and what it shares

A [git worktree](https://git-scm.com/docs/git-worktree) is a second working directory attached to the same repository. Git's docs list what is per-worktree and what is shared:

| Thing | Per worktree | Shared by all worktrees |
| - | - | - |
| Checked-out files, index, `HEAD` | Yes | |
| Branches and other refs under `refs/` | | Yes, and one branch can be checked out in only one worktree |
| Object store (commits, blobs) | | Yes, in the main repository's `.git` |
| Repository config (`.git/config`) | Only if you enable `extensions.worktreeConfig` | Yes by default |
| Hooks (`.git/hooks`) | | Yes, they live in the [common directory](https://git-scm.com/docs/gitrepository-layout) |
| `~/.gitconfig`, `~/.bashrc`, `~/.ssh`, cloud credentials | | Yes: same user, same home |
| Package caches such as npm's (`~/.npm` by [default](https://docs.npmjs.com/cli/v10/using-npm/config#cache)) | | Yes |
| `node_modules` and other untracked files | Yes, so each worktree installs its own | |
| Ports, Docker daemon, local databases, CPU and RAM | | Yes: it's one machine |

Git isolates the source tree. Everything outside the source tree is still one machine.

## The collision, reproduced

We ran this on a real `small` sandbox (`$A` below is its id): one repository, two worktrees, one per agent, and a tiny Node server whose `npm run dev` listens on port 3000 and replies with its working directory.

```bash
git worktree add -b agent-a ../wt-a
git worktree add -b agent-b ../wt-b
git worktree list
```

```text
/home/user/app   514dba2 [main]
/home/user/wt-a  514dba2 [agent-a]
/home/user/wt-b  514dba2 [agent-b]
```

Agent A starts its dev server in the background. Then agent B starts the same one in its own worktree:

```bash
sbx exec $A --cwd wt-a --detach "npm run dev > ~/wt-a.log 2>&1"   # log: listening on 3000
sbx exec $A --cwd wt-b "npm run dev"
```

```text
Error: listen EADDRINUSE: address already in use :::3000
    at Server.setupListenHandle [as _listen2] (node:net:1941:16)
    …
    at Object.<anonymous> (/home/user/wt-b/server.js:4:4)
  code: 'EADDRINUSE',
```

The quieter failure is worse. Agent B's tests hit agent A's server and pass or fail for code agent B never wrote:

```bash
cd ~/wt-b && curl -s localhost:3000
```

```text
hello from /home/user/wt-a
```

Then we checked the rest of the shared state from both worktrees:

```bash
for w in ~/wt-a ~/wt-b; do (cd $w && git config user.email && git rev-parse --path-format=absolute --git-path hooks); done
```

```text
agents@example.com
/home/user/app/.git/hooks
agents@example.com
/home/user/app/.git/hooks
```

Both agents commit as the same identity and run the same hooks. When agent A wrote a `pre-commit` hook from its worktree, agent B's next commit in `wt-b` failed with exit 1 and printed `pre-commit hook from agent A: blocking`. Agent B couldn't check out agent A's branch either:

```text
fatal: 'agent-a' is already used by worktree at '/home/user/wt-a'
```

Finally, a token agent A added to `~/.bashrc` showed up in agent B's shell:

```text
wt-b sees GITHUB_TOKEN=ghp_agentA_example
```

You can work around the port by giving each worktree its own `PORT`. You then also need separate database names, Docker Compose project names, cache directories and credentials, and you have to tell every agent which ones are its own.

## The same thing, one VM per agent

We then gave agent B a second sandbox (`$B`) with the same project files on branch `agent-b`, and left agent A's server running in the first. Each VM binds port 3000 without asking the other, and `sbx host` gives each one its own HTTPS URL:

```bash
sbx exec $B --cwd app --detach "npm run dev > ~/dev.log 2>&1"   # log: listening on 3000
sbx host $A 3000 --public
sbx host $B 3000 --public
```

```text
{"ok":true,"type":"port.hosted","sandboxId":"sb_9aqguz0uq7","port":3000,"url":"https://mossy-merry-yak-3000.box.sandboxer.com","access":"public",…}
{"ok":true,"type":"port.hosted","sandboxId":"sb_f3d9nvqlt0","port":3000,"url":"https://arctic-broad-cape-3000.box.sandboxer.com","access":"public",…}
```

```bash
curl -s https://mossy-merry-yak-3000.box.sandboxer.com
curl -s https://arctic-broad-cape-3000.box.sandboxer.com
```

```text
hello from /home/user/wt-a
hello from /home/user/app
```

In the second VM, `git config user.email` returned `agent-b@example.com`, and `GITHUB_TOKEN` was unset: agent A's `~/.bashrc` edit stayed in agent A's VM. Leave out `--public` to get private URLs that carry a token instead (see [Hosting](/docs/hosting)).

## Decision table

| Your agents… | Worktrees on one machine | One VM per agent |
| - | - | - |
| Only edit code and run unit tests | Works well; free and instant | Works; costs machine time |
| Each run a dev server or preview | Port per worktree, managed by hand | Every agent uses its usual port; one URL each |
| Need a database or Docker Compose stack | Shared daemon; rename every project, volume and database | Own Docker and own database per agent |
| Install hooks, global tools or dotfiles | Changes reach every worktree | Stay in that VM |
| Should hold different credentials | Hard: same user and home directory | Pass different variables per sandbox with `-e` |
| Run for hours | Your machine has to stay awake | Keeps running while your laptop sleeps |
| Need many at once | Limited by one machine's CPU and RAM | 100 at once on the $20 plan (2 during the trial) |
| Need to share the object store to save disk | Yes, one `.git` | Each VM has its own clone |
| Must work offline | Yes | No |

A middle ground: run the worktrees inside one sandbox when the tasks share an environment, and give a VM to each task that needs its own ports or services.

## What one VM per agent costs

Sandboxes are billed per second while they run, and stopped sandboxes cost nothing. Per agent-hour:

| Size | vCPU / RAM | Per agent-hour | 5 agents for 3 hours |
| - | - | - | - |
| `small` | 2 / 4 GB | $0.018 | $0.27 |
| `default` | 4 / 8 GB | $0.036 | $0.54 |
| `large` | 8 / 16 GB | $0.072 | $1.08 |

These are machine prices only. Model tokens are billed separately by your model provider on your own key. To avoid setting up each VM by hand, prepare one sandbox, save it as a named snapshot and start each agent from it ([Turn a sandbox into a reusable template](/guides/turn-a-sandbox-into-a-reusable-template)).

## FAQ

### Can't I just give each worktree a different port?

Yes, and for one dev server per agent that's enough. It doesn't separate the shared hooks, `~/.gitconfig`, credentials, Docker daemon or databases, and every agent has to be told which port is its own.

### Do worktrees save disk compared with one clone per VM?

Yes. Worktrees share one object store, so a second worktree adds only the checked-out files and its own `node_modules`. Each VM holds its own clone, but you pay for running time, not for the clone.

### How do agents in separate VMs share their work?

Through git, the same as a team: each agent commits on its own branch and pushes. You review the branches and merge the good ones.

### Can I still use worktrees inside a sandbox?

Yes. A sandbox is an ordinary Ubuntu VM, and everything on this page ran inside one. Use worktrees there when the tasks can share that VM's ports and services.

## Related

- [Run several coding agents in parallel, one VM each](/guides/run-coding-agents-in-parallel)
- [Turn a sandbox into a reusable template](/guides/turn-a-sandbox-into-a-reusable-template)
- [Give a sandbox port an HTTPS URL](/guides/give-a-sandbox-port-an-https-url)
- [Hosting](/docs/hosting)
