How I Run Multiple Claude Code Sessions in Parallel
Running several Claude Code agents at the same time without them stepping on each other — using git worktrees and a small shell tool called cmux, one agent per branch, on both my Mac and my always-on home Linux server, all on a single Max subscription
Ingredients
- Claude Code — the terminal AI I use for everything on this site and my server projects (Max subscription)
- Git worktrees — a built-in git feature that lets one repo have several working copies at once, each on its own branch (free, already in git)
- cmux — a small open-source shell tool that manages the whole worktree lifecycle with one-word commands (free)
- A terminal with tabs or splits — iTerm2 on my Mac, plain terminal tabs on the Linux box — so I can watch several sessions side by side (free)
- My always-on home Linux server — the same headless machine from the earlier posts, so long-running agents keep going after I close my laptop (already set up)
The Problem: One Agent at a Time Is Slow
Claude Code runs in a terminal. You open it in a folder, you talk to it, it edits files. That’s the whole thing, and it’s great — until you notice that most of the time you’re the bottleneck, not the AI. The agent goes off to write a feature, and you sit there watching it work. You can’t start the next thing, because if you open a second Claude Code session in the same folder, both agents edit the same files and you get a tangle: one overwrites the other’s work, tests fail for reasons that have nothing to do with the code, and you can’t tell which session broke what.
I kept hitting this. I’d have a small bug fix, a new blog post, and a refactor all queued up — three independent jobs, no reason they couldn’t happen at the same time — and I’d do them one after another because running them together was a mess.
The fix turned out to be a git feature I’d never used, plus a tiny tool that makes it painless.
The Idea: One Working Copy Per Agent
The reason two agents collide is that they share one set of files. So don’t share the files. Give each agent its own copy.
That’s exactly what a git worktree is — a second working copy of the same repo, checked out on its own branch, in its own folder, but still connected to the same underlying project history. You can have five of them. Each one is isolated: edits in one don’t touch the others. When a branch is done, you merge it back into the main copy like any normal branch.
So the mental model becomes: one worktree per agent, one branch per task. Agent A works in.worktrees/fix-footer on a branch called fix-footer. Agent B works in.worktrees/new-post on new-post. They never see each other’s files. When each finishes, I merge its branch and delete the worktree.
The thing that made this click for me: a worktree isn’t a duplicate folder you have to keep in sync by hand. It’s the same repo, viewed on a different branch, in a different place on disk. Git handles the plumbing. You just get an isolated sandbox per task, for free, already built into a tool I use every day.
The Tool: cmux (tmux for Claude Code)
Worktrees are powerful but fiddly. The raw git commands are long, you have to invent a folder layout, remember which branch lives where, and manually launch Claude in each one. I didn’t want to type git worktree add ../whatever -b whatever ten times a day.
So I set up cmux — a small open-source shell tool (its tagline is “tmux for Claude Code”) that wraps the whole worktree lifecycle in one-word commands. It’s not a program you install into a folder — it’s a shell function you load in your terminal config. On my Mac that’s a line in ~/.zshrc; on the Linux server it’s a line in ~/.bashrc. Because it’s just a function, which cmux shows nothing — you check it withtype cmux instead. That surprised me the first time.
cmux was introduced on r/ClaudeCode, and that thread is a good window into how other people are wiring it into their Claude Code workflow — worth a skim to see the range of setups before you commit to one.
The important part for cost: cmux makes zero AI calls of its own. Every session it launches is a normal Claude Code session running on my Max subscription. There’s no separate API key, no per-token billing stacked on top — running four agents at once costs the same subscription as running one.
🔧 Developer section: the commands I actually use
cmux new <branch>— creates a new worktree and branch, runs a setup hook, and launches Claude Code inside it. This is 90% of my usage.cmux start <branch>— reopens an existing worktree and resumes the conversation withclaude --continue, so the agent still remembers where it wascmux ls— lists all the worktrees so I can see what’s in flightcmux cd [branch]— jumps into a worktree folder (or back to the repo root with no argument)cmux merge [branch]— merges a finished worktree’s branch back into the main checkout (with an optional--squashto collapse it into one commit)cmux rm [branch]— removes the worktree and deletes its branch when I’m done (--allnukes every worktree, but makes you type a confirmation phrase first)
A typical morning looks like: cmux new fix-footer in one terminal tab,cmux new new-post in a second, cmux new refactor-search in a third. Three Claude Code agents, three isolated working copies, all going at once. I glance between tabs, answer whichever one has a question, and let the others run.
What the Name Gets Wrong (and Why It Still Works)
The name “tmux for Claude Code” had me expecting it to split my screen for me. It doesn’t. Under the hood, cmux new just creates the worktree and then runsclaude in the terminal you’re already in — one session, front and center. The “parallel” part is on me: I open the sessions in separate terminal tabs (or iTerm2 splits on the Mac). cmux manages the worktrees; my terminal manages the windows.
Once I understood that split, it stopped being confusing. cmux’s job is to make sure each agent has a clean, isolated place to work and a one-word way to get in and out. Arranging the panes is a terminal problem, and my terminal already solves it.
The Setup Hook: Making a Fresh Worktree Actually Runnable
Here’s the catch nobody warns you about. A brand-new worktree is a clean checkout — which means it’s missing all the stuff that isn’t committed to git. My .env.localfile with API keys? Gitignored, so it’s not there. My node_modules folder with all the installed packages? Not there either. So the agent starts up, tries to run the site, and immediately fails because it has no secrets and no dependencies.
cmux solves this with a setup hook — a little script at .cmux/setupthat runs automatically every time a new worktree is created, from inside that new worktree. Mine does two things: symlinks the gitignored secrets from the main copy, and installs dependencies.
🔧 Developer section: what my setup hook does
- Finds the main repo root from inside the worktree (
git rev-parse --git-common-dir, then take its parent) - Symlinks
.env.localfrom the main copy — a symlink is a pointer to the real file, so the worktree shares the same secrets without ever copying them into a new place - Runs the package install so
node_modulesexists and the site can actually build - Commit
.cmux/setupto the repo — then every future worktree gets it automatically, on both machines
There’s even a cmux init command that asks Claude to generate that setup script by reading your repo and guessing what it needs — which secrets to symlink, which package manager to use. It shows you the script, lets you edit or regenerate it, and only saves when you accept. I tweaked what it produced, but it got the shape right on the first try.
Symlinking secrets instead of copying them means there’s still exactly one real.env.local on disk, in the main repo. Rotate a key once and every worktree sees the new value. If cmux had copied the file into each worktree, I’d have five stale copies of my credentials scattered around — more places to leak from, more places to forget to update.
The Same Tool on Both Machines
I run cmux in two places. On my Mac, it’s for the interactive stuff — building this site, poking at a feature, three tabs open while I bounce between them. On my always-on home Linux server, it’s for work that needs to keep running after I close my laptop: a long refactor on one of my bots, or an agent grinding through a big migration overnight.
The setup is identical on both — same shell function, same commands, same .cmux/setuphook committed to each repo — which is the whole point. I don’t have to remember a different workflow depending on which machine I’m on. The only real difference is that on the server I start the session inside a persistent terminal (so it survives me disconnecting), and check back on it later with cmux start <branch> to pick the conversation back up right where it left off.
When Parallel Actually Helps (and When It Doesn’t)
The honest lesson from a week of using this: parallel isn’t always better. Running three agents at once only helps when the three tasks are genuinely independent. If task B depends on task A finishing first, parallelizing them just means A finishes, and then I have to merge A into B’s branch before B’s work even makes sense — which is more coordination, not less.
Where it shines: unrelated jobs in the same repo. A CSS fix, a new writing post, and a data script — three lanes, no overlap, all done by the time I’ve finished my coffee. Or trying two approaches to the same problem in two worktrees and keeping whichever one comes out cleaner. Where it hurts: anything where I need to hold the whole thing in my head. Watching three agents at once splits my attention, and a subtle mistake in one is easier to miss when I’m context- switching between tabs. For focused, careful work, I still just run one claude in one folder like normal.
Three isolated agents can’t conflict while they work — but their branches can conflict when they merge, if two of them happened to touch the same file. cmux won’t let me merge a worktree with uncommitted changes, which forced me into a good habit: get each branch committed and clean before merging, and merge them back one at a time so I can read what each one actually changed instead of trusting a big combined blob.
The Workflow, Start to Finish
Create, work, merge, remove — the whole lifecycle in four one-word commands.
What went fast
- Install and setup — it’s a shell function, so “installing” is one
sourceline in my shell config. Same line on the Mac and the server. Done in minutes on each. - The setup hook —
cmux initasked Claude to read my repo and write the.cmux/setupscript for me. It figured out which secret to symlink and which package manager to use, and I only had to tweak it. - Resuming work —
cmux startreopens a worktree and continues the same conversation. I can walk away from a half-finished session and pick it up hours later with the agent’s memory intact. - No extra cost — every session runs on the Max subscription I already pay for. Parallelism was free.
What needed patience
- The clean-checkout gotcha — my first fresh worktree failed instantly because it had no secrets and no installed packages. Nothing is broken; a new worktree just doesn’t inherit gitignored files. The setup hook is the fix, but I had to hit the wall before I understood why it exists.
- The name expectation — “tmux for Claude Code” made me think it would arrange my windows. It manages worktrees, not panes. Once I stopped waiting for it to split my screen and just opened my own tabs, it made sense.
- Knowing when NOT to parallelize — the real skill isn’t running four agents, it’s recognizing that three of your four tasks actually depend on each other and should just be one focused session. I over-parallelized at first and spent more time coordinating merges than I saved.
- Merge discipline — isolated agents can’t collide while working, but their branches can when they land. I learned to commit each worktree clean and merge them back one at a time, reading each change, instead of trusting a big combined merge.
The best part isn’t the speed, though the speed is real. It’s that a whole category of annoyance just disappeared — the “I can’t start that until this one finishes” feeling. Now independent work happens independently. Three lanes, three agents, one subscription, and a clean merge at the end. I create a worktree, hand a task to an agent, and go start the next one.