ワークツリーのポート被り、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/foohttps://foo.example-project.localhost
feature/barhttps://bar.example-project.localhost

たとえば、localhost:3000からlocalhost:4000に変わったとしても、そのブランチではhttps://piyo.foo.localhostのように 固定アクセス できるのです。
ポート番号と違って URLが意味を持つ文字になる ので分かりやすいですね。

## 使い方

v0.13.1時点での情報です。

### インストールと初期設定

npmで公開されているため、好きなパッケージマネージャーで入れてください。

Terminal window
npm install -g portless

portless内で独自のCAを作っている都合で、このままだと自己署名証明書の関係でブラウザにて警告が出ます。
ブラウザでのポチポチが増えて面倒なので、次のコマンドで信頼ストアに追加しておきましょう。

Terminal window
portless trust
Local CA added to system trust store.
Browsers will now trust portless HTTPS certificates.

### チュートリアル

※筆者はパッケージマネージャーとしてBunを使ってますが適宜npmやpnpmに読み替えてください。

#### サンプルプロジェクトの用意

まずはサンプルプロジェクトを作ります。

Terminal window
bun create next-app example-project

作成したら普通に起動してみましょう。

Terminal window
cd example-project
bun run dev
▲ Next.js 16.2.7 (Turbopack)
- Local: http://localhost:3000
...

ポート3000を使って起動しました。

#### portlessを付けてみる

終了して、次にpackage.jsonのscriptsportless runを付けてもう一度起動します。

"scripts": {
"dev": "next dev",
"dev": "portless run next dev",
"build": "next build",
"start": "next start",
"lint": "eslint"
},
Terminal window
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.localhosthttp://localhost:4809どちらでもアクセスできます

今回はscriptsを書き換えましたが普通にportless run next devを実行しても動きます。

#### 別のワークツリーで起動

今度はワークツリーで実行してみましょう。

Terminal window
git worktree add -b feature/bar ../bar
cd ../bar
bun 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が用意したポート番号」を渡せます。

Terminal window
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あったらなぁと思いました。