Running parallel Claude Code agents on Laravel with Orca: my setup and archive scripts A developer shared scripts for running parallel Claude Code agents on Laravel using Orca, an agent development environment that creates isolated git worktrees. The setup uses SQLite databases and unique local domains per worktree to prevent collisions between agents, with lifecycle hooks that automate environment configuration. David Hemphill wrote a great post about getting started with Conductor using Laravel https://davidhemphill.com/getting-started-with-conductor-using-laravel — how to wire up per-worktree setup and archive scripts so each parallel agent gets its own clean environment. I run the same idea, but with Orca instead of Conductor. This is my take, with the two scripts I actually use. The moment you run more than one coding agent at a time, they start fighting over shared state. Two agents against the same database means one's migrate:fresh --seed wipes the other's work mid-task. Two Laravel apps on the same local domain, the same queue, the same Reverb port — collisions everywhere. Git worktrees fix the files part: each branch gets its own directory on disk. But a Laravel app isn't just files. It's a database, a signed local domain, a websocket port, a built frontend. If you don't isolate those too, "parallel" is a lie. Orca is an agent development environment: it runs Claude Code, Codex, Gemini and other CLI agents in parallel, each in its own real git worktree . One worktree = one branch = one workspace. You review the diff, ship a PR, then archive the worktree in one click. The part that makes it usable for Laravel is two lifecycle hooks in the repository settings: Inside those scripts Orca hands you two environment variables I lean on heavily: ORCA ROOT PATH — the path to the main repo the "real" checkout, where my root .env lives . ORCA WORKSPACE NAME — the name of the current workspace/branch.Both of my scripts fall back to plain git if those aren't set, so the exact same file also works when I run it by hand outside Orca. My whole convention fits in one sentence: inside a worktree, the database is always an isolated SQLite file, never the project's shared database. A local file that gets created with the worktree and deleted with it. No seed collisions between parallel agents, ever. Local serving is Laravel Herd https://herd.laravel.com/ , one isolated, HTTPS-secured site per worktree. So agent A works on myapp-feature-invoices.test while agent B is on myapp-fix-webhooks.test , each on its own PHP version, its own database, its own everything. This runs on worktree creation. I keep the real thing in ~/.scripts/laravel/worktree-setup.sh and reference it from each project with a tiny shim more on that at the end . Here's what matters. Find the main repo, copy its .env: if -n "${ORCA ROOT PATH:-}" ; then main root="$ORCA ROOT PATH" else main root="$ dirname "$ cd "$ git rev-parse --git-common-dir " && pwd " " fi if -f "$main root/.env" ; then cp "$main root/.env" .env else cp .env.example .env fi set -a && source .env && set +a The root .env holds my real secrets and API keys, so every worktree inherits a working config with zero manual copy-paste. set -a exports everything so I can use those values later in the script. Build a unique, cert-safe site name: workspace="${ORCA WORKSPACE NAME:-$ git rev-parse --abbrev-ref HEAD }" workspace slug="$ echo "$workspace" | sed 's ^a-zA-Z0-9 - g' | tr ' :upper: ' ' :lower: ' " repo="$ basename "$main root" " site name="${repo}-${workspace slug}" max site len=58 if "${ site name}" -gt "$max site len" ; then ws hash="$ printf '%s' "$workspace" | cksum | awk '{print $1}' " keep=$ max site len - ${ ws hash} - 1 site name="$ printf '%s' "$site name" | cut -c1-"$keep" -${ws hash}" fi That 58-character cap is a gotcha I learned the hard way: the site name becomes the Common Name of the TLS certificate Herd generates, and a CN longer than 64 characters is invalid. Long branch names feature/some-very-descriptive-thing blow past that, so past the limit I truncate and append a short cksum hash to keep it unique. Point .env at the isolated SQLite DB and neutralize mail: sqlite path="$ pwd /database/database.sqlite" set env "APP URL" "https://${site name}.test" set env "DB CONNECTION" "sqlite" set env "DB DATABASE" "${sqlite path}" set env "MAIL MAILER" "log" set env is a small helper that updates a key if present or appends it — BSD sed , since this is macOS. Mail goes to the log so an agent can't accidentally send real email from a workspace. A deterministic Reverb port, only if the project uses Reverb: if -f config/reverb.php || grep -q '"laravel/reverb"' composer.json 2 /dev/null; then hash val="$ echo -n "$site name" | cksum | awk '{print $1}' " reverb port=$ 8100 + hash val % 100 set env "REVERB SERVER PORT" "${reverb port}" set env "REVERB PORT" "${reverb port}" set env "VITE REVERB PORT" "${reverb port}" fi Two worktrees can't both bind 8080 . Deriving the port from a hash of the site name gives each worktree a stable, collision-resistant port without me tracking anything. Link the site to Herd, isolate the PHP version, secure it: php ver="$ grep -oE '"php" ^,} ' composer.json 2 /dev/null | grep -oE '8\. 0-9 +' | head -1 || true " herd link "$site name" if -n "$php ver" ; then herd isolate "$php ver" --site="$site name" || true fi herd secure "$site name" herd restart The PHP version is read straight from composer.json , so a legacy project on 8.2 and a new one on 8.4 each get the right runtime automatically. Fresh database, dependencies, and a built frontend: mkdir -p "$ dirname "$sqlite path" " rm -f "$sqlite path" touch "$sqlite path" copy git-ignored MCP config so the agent has its tools -f "$main root/.mcp.json" && cp "$main root/.mcp.json" .mcp.json herd composer install --no-interaction --prefer-dist --optimize-autoloader php artisan config:clear php artisan key:generate --force --no-interaction php artisan migrate:fresh --seed --force php artisan storage:link 2 /dev/null || true if -f yarn.lock ; then yarn && yarn build elif -f pnpm-lock.yaml ; then pnpm install && pnpm build elif -f package.json ; then npm install && npm run build fi A couple of details I care about: I copy the git-ignored .mcp.json from the root so the agent inherits my MCP servers, and I detect the frontend package manager from the lockfile instead of hardcoding npm . I also gate private Composer auth Flux Pro behind "is it actually a dependency and are the creds present" so the script stays generic across projects. By the time this finishes, the agent opens onto a fully working https://myapp-feature-x.test with its own seeded database. Zero manual setup. This runs before Orca destroys the worktree. Its whole job is to undo what setup did — and to never block the deletion, so every command is best-effort with || true . bash /usr/bin/env bash set -uo pipefail if -n "${ORCA ROOT PATH:-}" ; then main root="$ORCA ROOT PATH" else main root="$ dirname "$ cd "$ git rev-parse --git-common-dir " && pwd " " fi same site name derivation as setup workspace="${ORCA WORKSPACE NAME:-$ git rev-parse --abbrev-ref HEAD }" workspace slug="$ echo "$workspace" | sed 's ^a-zA-Z0-9 - g' | tr ' :upper: ' ' :lower: ' " repo="$ basename "$main root" " site name="${repo}-${workspace slug}" same 58-char truncation as setup if -z "$workspace slug" ; then echo "⚠️ Empty workspace name, cleanup aborted for safety." exit 0 fi herd unsecure "$site name" || true herd unisolate "$site name" || true herd unlink "$site name" || true rm -f "$ pwd /database/database.sqlite" || true herd restart || true It rebuilds the exact same site name with identical logic, then tears the Herd site down unsecure → unisolate → unlink , deletes the SQLite file, and restarts Herd to purge the config. The empty-name guard matters: if the branch name ever resolves to nothing, I'd rather abort than run herd unlink "" and nuke something I didn't mean to. I don't paste these scripts into every project. The source of truth lives once in ~/.scripts/laravel/ , and each repo carries a two-line shim: bash scripts/worktree-setup.sh /usr/bin/env bash exec "$HOME/.scripts/laravel/worktree-setup.sh" "$@" In Orca's repository settings I point the Setup Script at scripts/worktree-setup.sh and the Archive Script at scripts/worktree-archive.sh . Improve the shared script once and every project — and every future worktree — gets the fix. The scripts auto-detect PHP version, frontend manager, Reverb and Flux, so the same file really does work everywhere. If you're running agents in parallel on Laravel, the isolation is the whole game: one SQLite file and one Herd site per worktree, created on setup and torn down on archive. Once that's in place, spinning up a third or fourth agent costs nothing, and none of them can corrupt each other's state. Credit where it's due — the pattern is straight out of David Hemphill's Conductor post https://davidhemphill.com/getting-started-with-conductor-using-laravel ; I just adapted it to Orca and my stack. If you've got a sharper version of any of these scripts, I'd genuinely like to see it. Baptiste — freelance web dev for 20 years, building in Laravel/PHP and shipping small open-source tools.