Using Portless with Conductor & git worktrees

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 argumentportless <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 dev scriptsdev:portless is additive; pnpm dev still works with random ports for single-worktree use
8 Likes

This is superb! Thanks for sharing the step by step guide here :raising_hands: Can’t wait to try

2 Likes

Tell us how you get on. One thing we haven’t figured out yet, that I would love to solve, is agent orchestration with git worktrees and how best to fire something off like that in one go. Think a less destructive Gastown, that you’d be comfortable throwing at a production repo. This is a simplified workflow, but should give you the idea.