Port and database collisions when parallel AI agents run your app
Git worktrees stop parallel agents from editing the same files. They do not stop them from sharing everything outside the repository: the port the dev server listens on, the local database, a cache directory in your home folder.
That is where a second, quieter kind of collision happens. One session starts the app and gets the port. Another session starts the app, sees "address already in use", and either gives up or picks a new port without telling you. Or two sessions run integration tests at the same time and one wipes the database the other is reading. The result is a test that fails for a reason that has nothing to do with the code the agent just wrote — and an agent that will happily try to "fix" it.
The three things agents usually share by accident
- Ports. Dev servers, API mocks, Storybook, a local queue — anything that listens on a fixed number.
- Databases. A local Postgres or SQLite file that tests reset, migrate or seed.
- Caches and temp folders. Build caches, downloaded fixtures, a
tmp/path hard-coded outside the repo.
Files inside a worktree are separate by design. These three are separate only if you make them so.
Give each session its own slot
The simplest pattern is a slot number per session, and everything else derived from it.
- Port: session 1 uses 3001, session 2 uses 3002, and so on. Many dev servers read a
PORTenvironment variable or accept a--portflag; check what yours supports. - Database: one database name or file per slot, e.g.
app_test_1,app_test_2, ortest-1.sqlite. - Cache / temp: keep them inside the worktree (and in
.gitignore) instead of a shared path in your home directory.
Put the values in a small, git-ignored env file at the root of each worktree, so the setting travels with the folder rather than living in someone's memory:
# .env.local in the worktree for session 2
PORT=3002
DATABASE_URL=postgres://localhost/app_test_2
Then tell the agent once, in its brief: "Use the port and database in .env.local. Never kill a process you didn't start."
Two lines worth adding to every brief
- "If a port is in use, stop and report it." Without this, an agent may kill whatever is holding the port — which can be another agent's server — or silently switch ports and then test against the wrong process.
- "Only reset the database named in
.env.local." Destructive test setup is fine when it is scoped. It is a problem when it reaches the shared default database.
These fit naturally into the "forbidden area" part of a four-part brief.
Finding out who holds a port
When something is already listening, check before killing it. On macOS:
lsof -nP -iTCP:3002 -sTCP:LISTEN
The output shows the process ID and command. If it belongs to another session's worktree, the fix is a different slot, not kill.
Where Agent Tile fits
Slots only work if you can tell which session is which. Agent Tile is a one-time $10 macOS app that shows each Claude Code session as a tile with its project and folder visible, so "session 2 = the worktree with port 3002" is something you can see rather than remember. After a restart it relaunches each session with --resume in its original directory, so each one comes back next to its own .env.local. It does not assign ports for you — that part is the env file.
Takeaway
Worktrees separate files; you still have to separate ports, databases and caches. Give each session a slot number, store the values in a git-ignored env file in the worktree, and tell agents to report a busy port instead of killing it.
Agent TilePractical guides for running many Claude Code sessions in parallel on macOS — orchestration, review workflows, and staying sane with a fleet of AI coding agents.
Get Agent Tile — full source, $10 once →
- IT-QA — Technical Q&A with AI-assisted answers and point bounties — for the questions that come up while you're running a fleet.