ワークツリーのポート被り、portlessで自動解消!
AI・ワークツリーを使った開発に手を染めてから、 ポート被り問題 が面倒でした。
「このプロジェクトはAPIが3000。ダッシュボードが3001……、アプリが……」のようになんとなくは頭に入っているものの、ワークツリーで並行作業していると衝突します。
だいたいは実行してからエラーが出て被っていることに気づきます。
PORT=5858 fooのように 手動で指定するのも面倒 です。タスクランナーにPORT=を複数用意して起動するのも「今使っていないのはどれだっけ?」となります。
AIにやらせても「被ったらPORT=5858を加えて……」という部分は変わらないです。要するに面倒です。
そこで以前TLで見かけたツールportlessを思い出しました。
## portlessとは
portlessは 空いているポートを自動で割り当ててくれる CLIです。
ただポート番号が変わってしまうと、ブラウザやコマンド履歴が使い回せなくて不便ですよね。
portlessはそれを見越してhttps://<ブランチ名など>.<プロジェクト名>.localhostに ローカルホストのURLを割り振ってくれます 。
↓こんな感じでブランチ毎にURLが割り振られます。
| どこのワークツリー | 用意されるURLの例 |
|---|---|
| メインツリー | https://example-project.localhost |
feature/foo | https://foo.example-project.localhost |
feature/bar | https://bar.example-project.localhost |
たとえば、localhost:3000からlocalhost:4000に変わったとしても、そのブランチではhttps://piyo.foo.localhostのように 固定アクセス できるのです。
ポート番号と違って URLが意味を持つ文字になる ので分かりやすいですね。
## 使い方
v0.13.1時点での情報です。
### インストールと初期設定
npmで公開されているため、好きなパッケージマネージャーで入れてください。
npm install -g portlessportless内で独自のCAを作っている都合で、このままだと自己署名証明書の関係でブラウザにて警告が出ます。
ブラウザでのポチポチが増えて面倒なので、次のコマンドで信頼ストアに追加しておきましょう。
portless trustLocal CA added to system trust store.Browsers will now trust portless HTTPS certificates.### チュートリアル
※筆者はパッケージマネージャーとしてBunを使ってますが適宜npmやpnpmに読み替えてください。
#### サンプルプロジェクトの用意
まずはサンプルプロジェクトを作ります。
bun create next-app example-project作成したら普通に起動してみましょう。
cd example-projectbun run dev▲ Next.js 16.2.7 (Turbopack)- Local: http://localhost:3000...ポート3000を使って起動しました。
#### portlessを付けてみる
終了して、次にpackage.jsonのscriptsでportless runを付けてもう一度起動します。
"scripts": { "dev": "next dev", "dev": "portless run next dev", "build": "next build", "start": "next start", "lint": "eslint" },bun run dev$ portless run next dev
portless
-- Proxy is running-- example-project.localhost (auto-resolves to 127.0.0.1)-- Name "example-project" (from package.json)-- Using port 4809
-> https://example-project.localhost
Running: PORT=4809 HOST=127.0.0.1 PORTLESS_URL=https://example-project.localhost NODE_EXTRA_CA_CERTS="/Users/eetann/.portless/ca.pem" next dev
▲ Next.js 16.2.7 (Turbopack)- Local: http://localhost:4809...今回はポート4809を使って、https://example-project.localhostというURLが割り当てられました。
#### どっちのURLでもOK
https://example-project.localhostとhttp://localhost:4809の どちらでもアクセスできます 。
今回はscriptsを書き換えましたが普通にportless run next devを実行しても動きます。
#### 別のワークツリーで起動
今度はワークツリーで実行してみましょう。
git worktree add -b feature/bar ../barcd ../barbun install# 今度はpackage.jsonを書き換えないで実行してみるportless run next devブランチ名を抽出して、https://bar.example-project.localhostというURLが割り振られました。
portless
-- Proxy is running-- bar.example-project.localhost (auto-resolves to 127.0.0.1)-- Name "example-project" (from package.json)-- Prefix "bar" (from git branch)-- Using port 4881
-> https://bar.example-project.localhost
Running: PORT=4881 HOST=127.0.0.1 PORTLESS_URL=https://bar.example-project.localhost NODE_EXTRA_CA_CERTS="/Users/eetann/.portless/ca.pem" next dev
▲ Next.js 16.2.7 (Turbopack)- Local: http://localhost:4881...### 対応オプションが無くても使える
Next.jsのように環境変数PORTがあるやつはportlessが自動で付けてくれます。が、ない場合でも手動で指定できます。
run sh -c経由で実行し、$PORTの値を渡してやることで「任意の環境変数や引数」に「portlessが用意したポート番号」を渡せます。
portless run sh -c 'MY_PORT=$PORT pnpm --filter foo dev'portless run sh -c 'bunx next dev --port $PORT'### ソースコードの書き換えが必要なケースもある
ソースコードや.envなどで直接URLを指定している場合は書き換えが必要です。
const BASE_URL = "http://localhost:3000"「環境変数を指定していたらそちらを優先」のようになど実装を変更しましょう。
const PORT = process.env["PORT"] ?? "3000"// ...### 警告が出た場合
.localhostの方で立ち上げた際、証明書に関して警告されることがあります。
これは前述のとおりportless trustを実行しましょう(そのまま無視して進めても普通に動きます)。

### 使い方まとめ
PORTに対応している:portless runを付けるPORTに対応してない:portless run sh -c 'コマンド'で$PORTを使う
## 注意
ブラウザのタブ名は変わらないので、並行してたくさん開く人はURLをよく見ましょう。
以上、ポート衝突問題を解消してくれるportlessの布教でした。
単にポート被りの解消だけじゃなく、URLを割り振ってくれるのが好きです。
「E2E用に別途ポートを手動で変更してコンテナを立ち上げる」なんてことを過去にやっていたので、あのときportlessあったらなぁと思いました。