Hi Vercel community,
We’ve been using Portless with Conductor to run git worktrees locally without port collisions. E.g., localhost:3000, localhost:3001, localhost:3002, localhost:3003, etc.
Setup is straightforward. We added a dev:portless script alongside the regular dev command:
"dev:portless": "WORKTREE_NAME=$(basename $PWD) turbo dev:portless"
Your existing dev command stays untouched. If you’re tired of juggling localhost:XXXX across multiple environments, this sorts it out. Full install walkthrough below
Getting Portless Working with Git Worktrees
Portless replaces random port numbers with stable, named .localhost URLs. When running multiple git worktrees (e.g., via Conductor), each worktree gets its own subdomain instead of fighting over ports.
Prerequisites
- Node.js >= 20
- macOS or Linux
- A pnpm monorepo using Turborepo
Step 1: Install portless
Add portless to your root package.json:
pnpm add -w portless
Step 2: Add a root dev:portless script
In your root package.json, add a script that captures the worktree directory name and passes it through Turbo:
{
"scripts": {
"dev:portless": "WORKTREE_NAME=$(basename $PWD) turbo dev:portless"
}
}
$(basename $PWD) extracts the folder name of the current worktree (e.g., mumbai, my-feature). This becomes the subdomain.
Step 3: Register the task and env var in Turbo
In turbo.json, add WORKTREE_NAME to globalEnv so Turbo passes it to all tasks, and define the new task:
{
"globalEnv": ["WORKTREE_NAME"],
"tasks": {
"dev:portless": {
"cache": false,
"persistent": true
}
}
}
Step 4: Add per-app dev:portless scripts
In each app’s package.json, wrap the normal dev command with portless <name>:
Web app (apps/web/package.json):
{
"scripts": {
"dev:portless": "portless $WORKTREE_NAME next dev --turbopack"
}
}
This makes the web app available at http://{worktree-name}.localhost:1355.
Additional apps get a prefix to avoid collisions. For example, an imagen app (apps/imagen/package.json):
{
"scripts": {
"dev:portless": "portless imagen.$WORKTREE_NAME next dev"
}
}
This makes it available at http://imagen.{worktree-name}.localhost:1355.
Step 5: Wire it up to Conductor (optional)
If using Conductor to manage worktrees, point conductor.json at the portless script:
{
"run": "pnpm run dev:portless"
}
How it works
Worktree: ~/workspaces/website/mumbai
↑
WORKTREE_NAME = "mumbai"
Web app → http://mumbai.localhost:1355
Imagen → http://imagen.mumbai.localhost:1355
Worktree: ~/workspaces/website/tokyo
Web app → http://tokyo.localhost:1355
Imagen → http://imagen.tokyo.localhost:1355
All worktrees share port 1355. Portless runs an HTTP proxy that routes requests based on the .localhostsubdomain to the correct underlying dev server. No port conflicts, no remembering numbers.
Key points
- No config file needed — portless is configured entirely through CLI arguments in your npm scripts
- The subdomain is the first argument —
portless <name> <command>runs<command>and proxies it at<name>.localhost:1355 - Nest subdomains for multi-app repos — prefix app names (e.g.
imagen.$WORKTREE_NAME) to keep URLs unique - Keep existing
devscripts —dev:portlessis additive;pnpm devstill works with random ports for single-worktree use
