Isolation
Run the same project in several places at once — your checkout plus a
review worktree, or five coding agents on one laptop — without a single port collision.
fed already scopes containers, volumes, and state to the directory; one command,
fed isolate enable, gives a checkout its own ports too.
Ports must be parameters
Isolation can only remap ports it knows about — the ones declared as type: port parameters and interpolated with {{...}}.
A literal 4321 in a command or healthcheck is invisible to fed and
will collide across workspaces. Write every port the fed way:
parameters:
APP_PORT:
type: port
default: 4321
DB_PORT:
type: port
default: 5433
services:
database:
image: postgres:16-alpine
ports: ["{{DB_PORT}}:5432"] # never "5433:5432"
environment:
POSTGRES_PASSWORD: postgres
app:
process: npm run dev -- --port {{APP_PORT}}
startup_message: 'http://localhost:{{APP_PORT}}'
environment:
DATABASE_URL: 'postgres://localhost:{{DB_PORT}}/app'
healthcheck:
httpGet: 'http://localhost:{{APP_PORT}}/health'
entrypoint: app
In normal (non-isolated) mode fed prefers the configured defaults; if one is occupied,
it can allocate an alternative. Under isolation each workspace gets random ports from
the outset — but only for ports declared this way. fed warns once at startup when it
finds literal localhost:<port> references or literal host ports in the
config.
Directory scoping
Everything fed tracks is scoped to the directory containing your service-federation.yaml:
- Port allocations
- Docker container names
- Docker volumes
- The SQLite state file
Two directories are two independent stacks. There's nothing to configure, and it
doesn't matter who created the directory — a worktree made by fed ws new,
by hand, or by a coding agent behaves the same.
Isolation mode
Directory scoping keeps containers and state apart, but two checkouts of the same project still want the same default ports. Isolation mode fixes that — run it once in the second checkout:
fed isolate enable # Random ports + unique container names (persisted)
fed isolate status # Show isolation state and port allocations
fed isolate rotate # Re-roll ports and isolation ID
fed isolate disable # Return to the directory's normal scope
fed isolate enable prints an isolation ID and the full port table, then
persists both. Every fed command you run in that directory afterwards — start,
scripts, tests, psql — uses the isolated ports and containers, across
restarts, until you fed isolate disable.
CLI output calls the normal namespace the shared scope. It is shared by runs in this directory, not by different worktrees: normal container names, volumes, and state are still directory-scoped.
Quick isolation
fed start --isolate # Enable isolation + start (one command)
On a new or stopped checkout, this has the same persistent result as
fed isolate enable && fed start. Use the explicit command when services
may already be running: it checks and offers to stop them before changing scopes.
What isolation does
- Ports: all port-type parameters get randomized values instead of defaults. Literal ports (a hardcoded
4321in a command or healthcheck) are not remapped — see Ports must be parameters - Direct Docker containers:
image:services get the isolation ID in their container names - Direct Docker named volumes: named volumes on
image:services are scoped to the isolation ID; bind mounts are unchanged
Compose-backed services are already directory-scoped through a project name derived
from the Compose file path. fed isolate enable does not replace that project
name with the isolation ID, and fed isolate rotate does not create fresh
Compose volumes.
Port management
fed ports list # Show current allocations
fed ports list --json # Machine-readable output
Allocations stay put until you fed isolate disable — or re-roll them with fed isolate rotate.
Git worktrees
Each worktree is a separate directory, so each worktree is a separate stack. Enable isolation right after creating one, before you run anything else:
# Main checkout — default ports
~/project $ fed start
# Second worktree — its own ports, containers, and volumes
~/project-review $ fed isolate enable
~/project-review $ fed start
Both stacks run at the same time on the same machine — this scales past two. A
docker ps on a busy laptop shows the main checkout on default ports and
three isolated worktree stacks beside it, each under its own isolation ID.
Cookies and other shared namespaces
Ports isolate; localhost cookies don't — they're port-agnostic, so two
stacks of the same app expire each other's sessions on every login. Scope the cookie
name with the built-in
FED_PROJECT_ID variable,
which embeds the isolation ID, and each stack keeps its own session. The same trick
namespaces anything else parallel stacks would share behind one hostname.
If you skip isolation, fed detects occupied defaults. An interactive start prompts for
a response; a non-interactive start can fall back to alternative ports. Explicitly
enabling isolation avoids that ambiguity and randomizes every declared port. Never use
fed start --replace in a worktree: it can take the port by killing the other
checkout's services.
Workspace management (beta)
fed ws (alias for fed workspace) automates git worktree
creation and switching:
fed ws setup # One-time: install shell integration (zsh or bash)
fed ws new my-feature -b # Create branch + worktree, cd into it
fed ws list # Show all worktrees with service status
fed ws cd main # Switch to another worktree
fed ws rm my-feature # Stop services and remove worktree
fed ws prune # Clean up worktrees for deleted branches
Worktrees are created as siblings to the repo in a <repo>-worktrees/ directory. Shell integration enables auto-cd — without it, fed ws new and fed ws cd print the path instead.
Coding agents
When your coding-agent workflow uses one git worktree per task, each worktree is its own directory and can run its own full stack — database, backend, tests — without touching yours. One rule makes it predictable: isolate first.
Don't leave that to the agent's judgment. Put it in your AGENTS.md (or CLAUDE.md) so every agent starts every worktree the same way:
## Worktrees
Run `fed isolate enable` before any other fed command in a new worktree.
It persists: every fed command after it — start, tests, scripts — gets
the worktree's own ports, containers, and volumes.
Why fed isolate enable rather than fed start --isolate?
Agents rarely open with fed start — the first fed command is usually an
install, a code generator, or a test run. fed isolate enable isolates the
directory once, for whatever comes next; the flag only helps when the first command
happens to be start.
The rule also protects the rest of your machine. An agent that skips it hits a port
conflict on fed start — and an agent improvising its way out of that can
reach for fed start --replace, which frees the port by killing another
checkout's services. Agents follow written instructions more reliably than they
recover from collisions.
No plugin or integration needed. Isolation is directory-scoped, so it works the same
whether the worktree came from Cursor, fed ws new, or git worktree add.
Isolated scripts
Scripts can opt into a throwaway child context with isolated: true — fresh
ports, isolated direct image-backed containers and named volumes, and automatic cleanup.
Compose limitations are covered in Scripts.