Run many isolated dev environments on one machine, one per git worktree. Each worktree gets its own services, its own environment variables, and its own database cloned from a template. No Docker, no /etc/hosts, no port juggling, and it's built for the parallel AI
coding agents.
Git worktrees let you check out several branches at once. But the moment you run them, they collide.
Picture three things in flight: an AI agent refactoring on one branch, a spike on
another, a hotfix on main.
- They all try to bind
:3000and:5432. - They migrate and seed the same dev database, so one branch's schema change breaks another, and a destructive test on one wipes data the others still need.
- You're juggling
.envfiles, hunting down stray processes, and guessing which server is which. - Every fresh worktree starts empty: dependencies to install, migrations to run, code to generate, all by hand before it will even start.
isola gives each worktree its own running environment: processes, ports,
*.localhost URLs, injected env vars, and its own database (cloned from a
template, dropped with the worktree). It runs each service's setup step first, so
a fresh worktree installs, migrates, and comes up on its own. So N branches, or N
agents, run fully isolated, side by side, and clean up after themselves.
- Per-worktree isolation: every worktree runs as its own environment, with its own processes, ports, URLs, environment variables, and database
- Isolated databases: every worktree gets its own database, created when it starts and removed when the worktree goes away
- Automatic ports: each service gets a stable port, with no clashes between worktrees
- Subdomain proxy: open each worktree in the browser at its own URL, over HTTP or HTTPS, with no hosts-file setup
- Environment injection: each service is handed the port and URLs it needs to reach the others, so nothing is hardcoded
- Automatic setup: each service can declare a setup step (install dependencies, run migrations, generate code) that isola runs before starting it, in the worktree's own environment, so a fresh worktree is ready with no manual prep. A top-level
setupcovers repo-root steps that belong to the whole worktree (a root install that wires up git hooks, generating a shared client), run once before any service - AI-agent friendly: machine-readable output plus an installable agent skill, so coding agents can drive it
- TUI dashboard: a terminal dashboard to start, stop, and monitor everything at once
- Zero dependencies: just one binary to install, no Docker
- Per-worktree overrides: change commands, ports, or environment per branch
Run your agent in your project, paste this, and you're done:
Set up isola for this project.
1. Add the isola skills: `npx skills add cyucelen/isola`.
2. Use the `isola-init` skill to do the setup. If it isn't available in this session, follow its guide: https://github.com/cyucelen/isola/blob/main/skills/isola-init/references/setup.md
Next project?
/isola-initin any new repo.
# macOS (Homebrew)
brew install cyucelen/tap/isola
# Linux (.deb / .rpm are attached to each release):
# https://github.com/cyucelen/isola/releases/latest
sudo dpkg -i isola_*_linux_amd64.deb # Debian/Ubuntu
sudo rpm -i isola_*_linux_amd64.rpm # Fedora/RHEL
# Arch (AUR)
yay -S isola-bin
# Any platform with Go
go install github.com/cyucelen/isola@latest
# Or build from source
git clone https://github.com/cyucelen/isola.git
cd isola
make buildSee the install guide for prerequisites and platform notes.
cd your-project
isola init
# Creates .isola.toml in the repo rootEdit .isola.toml to match your project. Each service declares its own env;
values can reference isola-provided sources with ${...}:
# Top-level, above any [section]: a repo-root step run once per worktree on up,
# before any service (e.g. a root install that also sets up git hooks).
setup = "pnpm install"
[services.web]
command = "pnpm run dev"
setup = "pnpm install" # runs before command on each up (deps, migrations)
dir = "web"
port_range = { min = 3100, max = 3199 }
proxy_port = 3000
# Injected into the process, and written to the service's env file:
[services.web.env]
API_URL = "${services.api.url}"
[services.api]
command = "go run ./cmd/server"
dir = "api"
port_range = { min = 8100, max = 8199 }
proxy_port = 8000
[services.api.env]
DATABASE_URL = "${accessories.database.url}"
# A background worker: no port_range/proxy_port, so no $PORT and no URL.
# isola still runs, manages, and injects env into it.
[services.worker]
command = "pnpm run worker"
dir = "api"
# A per-worktree database, cloned from a seeded template:
[accessories.database]
kind = "postgres"
server_url = "postgres://postgres@localhost:5432/postgres"
clone_from = "myapp_dev"
name = "myapp_${ISOLA_BRANCH_SLUG}"Reference a sibling's URL or port, an accessory's connection string, or a
built-in with ${...}, and isola resolves each to this worktree's real values.
Full walkthrough in the isola-init setup guide.
isola up # this worktree's services (also auto-starts the proxy)
isola up --all # every worktree's servicesYour services are now reachable at
http://<branch-slug>.<project>.localhost:<proxy_port> (e.g.
http://main.myapp.localhost:3000), where <project> is this repo's name
(default: its directory). One machine-wide proxy serves every project and runs
until isola proxy stop; HTTPS and opting out live under
[proxy]. Manage everything interactively with isola dash.
If you set up by hand, install the agent skill too so your agents can drive isola
day to day: npx skills add cyucelen/isola.
Each worktree's environment needs to be started (isola up) and, when the
worktree goes away, stopped. Do that by hand, or have isola do it automatically.
Run isola up in the worktree to start it, isola down to stop it:
git worktree add ../my-feature -b my-feature
cd ../my-feature && isola upInstall a post-checkout hook once and every new worktree comes up on its own:
isola hooks install # just this clone
isola hooks install --shared # share with your teamAnything that makes a worktree runs git worktree add underneath (your editor, a
parallel-agent tool like Orca or Herdr,
or plain git), so this one hook covers them all. It runs isola up on new
worktrees only, and does nothing without a .isola.toml.
- Skip once:
ISOLA_NO_UP=1 git worktree add ... - Using Orca?
isola orcawires it intoorca.yamlinstead, and the git hook stands down so nothing runs twice. - Check or remove:
isola hooks status,isola hooks uninstall.
Git has no worktree-removal hook, so isola reconciles: a removed worktree is torn
down automatically, its services stopped and the per-worktree databases it
provisioned dropped, on the next isola up and by the shared proxy in the
background. It only ever drops resources isola created. isola down --prune runs
the same teardown on demand, and isola destroy tears down the current worktree.
Each service declares its own env. Use ${...} references and isola fills in
this worktree's real values, so nothing is hardcoded per branch. On a branch
feat-login:
[services.web.env]
API_URL = "${services.api.url}" # http://feat-login.myapp.localhost:8000
DATABASE_URL = "${accessories.database.url}" # postgres://.../myapp_feat-loginReference other services, accessories, and built-ins; isola injects the values
into the process. It also writes them into the service's .env file, updating an
existing file rather than creating one, so tools that read .env directly get the same isolated values. Full
list in the Configuration Reference.
An accessory is an isolated stateful dependency isola creates per worktree,
brought up on isola up and dropped on isola down --prune. isola connects to
your existing server (it never manages the server itself) and exposes a
connection string that a service references explicitly. Two kinds ship today:
| Kind | Each worktree gets |
|---|---|
postgres |
Its own database, physically cloned from a seeded template (via pgx, no psql needed) |
redis |
Its own numbered logical DB, allocated collision-free and flushed on reset/drop |
[accessories.database]
kind = "postgres"
server_url = "postgres://postgres@localhost:5432/postgres"
clone_from = "myapp_dev" # seeded template, cloned per worktree
name = "myapp_${ISOLA_BRANCH_SLUG}"
[accessories.cache]
kind = "redis"
server_url = "redis://localhost:6379"A service opts into an accessory by referencing its URL under whatever env key it wants; the same value goes to the process env and the service's env file:
[services.api.env]
DATABASE_URL = "${accessories.database.url}"
REDIS_URL = "${accessories.cache.url}"Manage them out of band with isola accessory ls|up|reset|drop [name]. See the
Configuration Reference for every field
of each kind, and Writing a new accessory to add
your own.
Everything lives in a single .isola.toml at the root of your repository:
services (with their env), per-worktree accessories, the reverse proxy, and file
handling. The Configuration Reference documents
every block and field; the Quick Start above covers the common
case.
| Command | Description |
|---|---|
isola init |
Create a .isola.toml configuration file |
isola up |
Start services for the current worktree |
isola up --all |
Start services for all worktrees |
isola up --service |
Start a specific service only |
isola down |
Stop services for the current worktree |
isola down --all |
Stop services for all worktrees |
isola down --prune |
Stop services, then drop deleted worktrees' state and databases |
isola destroy |
Stop the current worktree's services and drop its database (opposite of up) |
isola ls |
List all worktrees, services, ports, status, and URLs |
isola logs [worktree] |
Tail a worktree's service logs |
isola accessory |
Manage per-worktree databases: ls, up, reset, drop |
isola dash |
Open the interactive TUI dashboard |
isola proxy start |
Start the machine-wide reverse proxy (auto-started by up) |
isola proxy stop |
Stop the reverse proxy |
isola trust |
Install the CA certificate into the system trust store |
isola hooks install |
Install a git hook that runs isola up when a worktree is created (--shared to commit it) |
isola orca |
Add isola up to Orca's orca.yaml worktree setup hook |
isola doctor |
Run diagnostic checks on config and ports |
isola version |
Print version information |
HTTPS is enabled per project with [proxy] https = true in .isola.toml (not a flag).
project "myapp" project "api"
┌───────────────────────┐ ┌───────────────────────┐
│ main frontend :3100 │ │ main web :3140 │
│ feat frontend :3117 │ │ dev web :3151 │
└───────────┬───────────┘ └───────────┬───────────┘
│ (each project registers in ~/.isola/registry)
└──────────────┬───────────────────┘
┌──────────────▼──────────────┐
│ isola proxy (machine-wide) │
│ binds :3000, :8000, ... │
│ routes by <branch>.<proj> │
└──────────────┬──────────────┘
▼
main.myapp.localhost:3000 main.api.localhost:3000
feat.myapp.localhost:3000 dev.api.localhost:3000
- Port allocation: each service gets a backend port via
FNV32(branch:service) % range, skipping ports already in use so projects never share one. Stable across restarts. - Process management: services run as child processes with process groups. Logs go to
.isola/logs/. - Shared proxy: one machine-wide daemon reads
~/.isola/registry, binds the union of every project'sproxy_ports, and routes each request by its<branch>.<project>.localhosthost to that project's backend (resolved live from the project's own state). **.localhost**: per RFC 6761, modern browsers resolve*.localhost(including<branch>.<project>.localhost) to127.0.0.1automatically.
Launch it with isola dash:
isola supports shell completion for bash, zsh, fish, and PowerShell.
bash:
source <(isola completion bash)
# Or for persistent use:
isola completion bash > /etc/bash_completion.d/isolazsh:
isola completion zsh > "${fpath[1]}/_isola"
# You may need to start a new shell for this to take effect.fish:
isola completion fish | source
# Or for persistent use:
isola completion fish > ~/.config/fish/completions/isola.fishPowerShell:
isola completion powershell | Out-String | Invoke-Expression
# Or for persistent use:
isola completion powershell > isola.ps1
# and add ". isola.ps1" to your PowerShell profile.Start with isola doctor, which checks your config and ports. The
Troubleshooting guide covers the common failures and
their fixes: services that won't start, port conflicts, stale processes, proxy
routing, and HTTPS.
| Platform | Status | Notes |
|---|---|---|
| macOS | Fully supported | Primary development platform |
| Linux | Fully supported | Tested on Ubuntu, Debian, Fedora |
| Windows | Experimental | Basic functionality works; file locking uses alternative implementation. Please report issues. |
Yes. isola up starts your services on their allocated ports; reach them
directly at localhost:<port>. The proxy (and its *.localhost routing) is
optional; disable auto-start with [proxy] enabled = false.
Two places. Per repository, in .isola/ at the main worktree root: runtime
state (service PIDs, port assignments, accessory handles) in .isola/state.json
(file-locked for concurrent access), and service logs at
.isola/logs/<branch-slug>.<service>.log.
Machine-wide, in ~/.isola/: the shared proxy's registry (registry.json)
and daemon state (proxy.json), the proxy log (logs/proxy.log), and the
auto-generated HTTPS CA and certificates (certs/), so one proxy and one trusted
CA cover every project.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing) - Commit your changes (
git commit -m 'feat: add amazing feature') - Push to the branch (
git push origin feature/amazing) - Open a Pull Request
# Development
make build # Build binary
make test # Run tests with race detector
make lint # Run golangci-lint
make all # fmt + vet + lint + test + buildisola is a fork of portree by fairy-pitta, and builds directly on its worktree-aware process management, hash-based port allocation, and *.localhost reverse proxy. Huge thanks to the portree authors for that foundation.
If you just need per-worktree dev servers (port allocation and subdomain routing, without databases or other stateful isolation), portree is the simpler, focused tool. isola extends that model toward full per-worktree environment isolation: databases and other stateful accessories, cloned from a template and torn down together with the worktree.
MIT License. See LICENSE for details.