🚪 ローカル開発URLを安定化するリバースプロキシ - Portless
目次

🚪 ローカル開発URLを安定化するリバースプロキシ - Portless

複数の開発サーバーを動かしていると、localhost:3000localhost:5173 のようなポート番号が増えていきます。空きポートの探索、再起動によるURL変更、CookieやWeb Storageの混在は、人間だけでなくブラウザーを操作するコーディングエージェントにとっても厄介です。

Portlessは、可変ポートの開発サーバーをhttps://<name>.localhostという安定したURLへ割り当てるVercel LabsのオープンソースCLIです。この記事では、Portless 0.15.5とGitHubの固定コミット2e87792c0da55ee514ce1d566045877f6d86cc24を基準に、構造、データ、導入、運用上の注意点まで解説します。

記事の全体像
この記事の全体像。以下、順に解説します。

概要

Portlessは、既存のdev serverを置き換えるフレームワークではありません。dev serverを子プロセスとして起動し、その手前にローカルリバースプロキシを置くプロセスランナーです。

たとえば次のコマンドを実行すると、子プロセスには4000〜4999の空きポートが割り当てられます。一方、ブラウザーからはポート番号を覚えずにhttps://shop.localhostへアクセスできます。

portless shop next dev

名前付きホストを使うと、Web Storageやhost-only Cookieをhostname単位で分離でき、CORS originやOAuth callback URLもアプリごとに別の値になります。Domain属性を付けたCookieはサブドメイン間で共有される点に注意してください。また、OAuth providerによっては.localhost.testをcallbackへ登録できないため、その場合は所有ドメインを使ったmulti-segment TLDが必要です。Git worktreeではブランチ名を接頭辞にしたURLを生成できるため、同じアプリの複数checkoutも衝突しません。

現行版の前提

項目 Portless 0.15.5
Node.js 24以上
対応OS macOS、Linux、Windows
ライセンス Apache License 2.0
既定URL https://<name>.localhost
子アプリの自動割当ポート 4000〜4999
既定proxy port HTTPSは443、HTTPは80
runtime dependency 0件

0.15.5は2026年7月30日に公開されたpre-1.0版です。状態形式や証明書の扱いがリリース間で変わり得るため、チーム利用ではexact versionとlockfileを揃えるのが安全です。

類似ツールとの違い

ツール 得意な範囲 Portlessとの主な違い
Caddy 2 汎用リバースプロキシ、詳細なrouting、HTTP/3、productionに近い構成 backendの起動やアプリ名推論は利用者が構成
puma-dev 0.18.3 Rails・Rackアプリのアクセス時起動とidle shutdown Ruby・Rackのライフサイクル管理が中心
Laravel Valet 4.12.0 macOS上のPHP、Nginx、DnsMasq、site link・park Laravel・PHP開発環境を一体化
Portless 0.15.5 JavaScript系dev server、monorepo、worktree、AIエージェント 子プロセス起動からroute削除までを1コマンドへ統合

特徴

  • 空きポートを使いながら、アプリごとに安定した名前付きURLを提供
  • package.json、Git root、ディレクトリ名からアプリ名とdev scriptを推論
  • Vite、VitePlus、React Router、Rsbuild、Astro、Angular、Expo、React Nativeのserver commandへport・host flagを自動注入
  • ローカルCAによるHTTPSとHTTP/2、HTTP/1.1・HTTP/2のWebSocket HMRに対応
  • pnpm workspaceとnpm・Yarn・Bun workspacesを検出
  • Turborepoのtask graphを保った複数package起動に対応
  • linked worktreeのブランチ名をURL接頭辞へ変換
  • .localhost.test、複数TLD、multi-segment TLDに対応
  • 通常時はloopbackだけへbindし、明示したLAN modeだけ全interfaceへ公開
  • Tailscale Serve・Funnel、ngrokの共有URLを子プロセスのlifecycleと一緒に管理
  • doctorlistprunecleanによる診断と状態整理

自動flag注入には境界があります。compound command、環境変数prefix、別scriptへの委譲、末尾コメント、既存の--を含むscriptなど、安全に分類できない場合は変更しません。また、自動注入は単一アプリのnamed・run・package-script経路に限られます。bare portlessによるmulti-app direct spawnとTurbo経路では環境変数だけが渡されるため、PORTを無視するframeworkはscript側でportを指定します。

構造

Portlessの中核は、コマンド実行を管理するCLI、Hostヘッダーで中継先を選ぶ常駐proxy、ホスト名とポートを共有するroute registryです。外部共有はローカルproxyを経由せず、Tailscaleやngrokが子サーバーのloopback portを直接公開します。

システムコンテキスト図

名前と実行対象を指定 安定 URL で接続 HTTP HTTPSWebSocket ホスト名で宛先選択 起動登録 信頼設定名前解決更新 任意の共有経路登録 共有 URL から接続 開発者 コーディングエージェント Web クライアント Portlessローカル開発ルーティング ローカル開発サーバー OS 基盤起動 証明書名前解決 共有ネットワークまたは公開トンネル

利用者はPortlessへアプリ名と実行対象を渡します。ローカルクライアントはPortless proxyへ接続しますが、Tailscaleやngrokの共有URLは共有edgeから子dev serverへ直接転送されます。

コンテナ図

パッケージ探索並列起動 経路の登録と解除 起動と健全性確認 環境変数と実行引数 パッケージ別環境注入 任意の公開登録 永続起動の構成生成 経路表を変更通知 TLS 文脈と解決名更新 HTTP HTTPSWebSocket ループバックHTTP WebSocket ループバックHTTP 開発者またはエージェント CLI オーケストレーター設定解決とプロセス管理 ワークスペースオーケストレーター ルートレジストリ共有状態 常駐リバースプロキシ TLS と名前解決統合 OS 起動サービス統合 共有アダプター私域網と公開トンネル 子開発サーバー実行群 Web クライアント OS サブシステム

CLIオーケストレーターは設定解決、proxyの自動起動、route登録、子プロセスの終了処理を担当します。ワークスペースオーケレーターはpackageを発見して直接起動またはTurbo経由で実行し、TLS・名前解決・常駐サービスはOS固有の機能と連携します。

コンポーネント図

ループバックプロキシ ループバックトンネル ファイル変更監視 cli.tsコマンドディスパッチ config.ts と auto.ts設定名前解決 workspace.tsパッケージ発見 turbo.ts環境ローダーマニフェスト cli.tsプロキシライフサイクル routes.tsRouteStore routes.jsonルート状態 proxy.tsホストルーティング certs.tsローカル CA とSNI 証明書 hosts.ts と mdns.tsホスト同期とLAN 発行 service.tsOS サービスアダプター tailscale.ts と ngrok.ts共有アダプター 子コマンド開発サーバー

主要コンポーネントの役割は次のとおりです。

実装 役割
cli.ts コマンド分岐、proxyと子プロセスのlifecycle、共有経路の編成
config.ts / auto.ts 設定優先順位、script、アプリ名、worktree接頭辞の解決
workspace.ts / turbo.ts workspace探索、package別環境変数の受け渡し
routes.ts routes.jsonの排他更新、route登録・解除・stale判定
proxy.ts HostまたはauthorityによるHTTP・WebSocket転送
certs.ts ローカルCA、既定証明書、SNI hostname別証明書の管理
hosts.ts / mdns.ts hosts同期とLAN modeのmDNS公開
service.ts launchd、systemd、Task Scheduler向け定義の生成
tailscale.ts / ngrok.ts 子portの共有登録と終了時cleanup

ネットワーク構成図

HTTP からHTTPS HTTPS ホスト名をポートへ変換 LAN モードHTTP または HTTPS ローカル HTTP直接転送 ローカル HTTP直接転送 ローカルWeb クライアント ループバック DNSまたは hosts 任意の HTTP リダイレクトリスナー best-effort HTTPS HTTP2リバースプロキシ メモリ上のルート表 子開発サーバーループバック HTTP LAN 内Web クライアント mDNSローカル名解決 私域ネットワーククライアント 公開インターネットクライアント 私域ネットワークHTTPS エッジ 公開トンネルHTTPS エッジ

通常modeではproxyが127.0.0.1::1だけで待ち受けます。LAN modeでは0.0.0.0::へ公開し、mDNSの.local名を使います。TLS有効時のport 80 redirect listenerはbest-effortであり、bindに失敗しても主proxyは起動します。

データ

Portlessのデータは、プロジェクト設定、ユーザー単位の状態、OSの常駐サービス定義、子プロセスへ渡す実行時環境に分かれます。中心となる保存先は~/.portlessですが、PORTLESS_STATE_DIRでroute、proxy marker、certificate、service logの保存先を変更できます。

0.15.5には例外があります。Turbo用のturbo-env-loader.cjsdev-manifest.jsonはcustom state directoryを使わず、invoking userの~/.portlessへ固定されます。

概念モデル

プロジェクト設定 ワークスペース 実行時データ OS サービス定義 StateDirectory 固定 USER_STATE_DIR PortlessConfig AppConfig WorkspacePackage ProxyConfig ChildEnvironment ServiceConfig StateDirectory RouteMapping CertificateArtifact TurboUserStateDirectory TurboManifest

PortlessConfigportless.jsonまたはpackage.jsonportlessキーから読みます。RouteMappingはhostname、子port、所有PIDを結び付け、CertificateArtifactはCAとSNI証明書を表します。Turbo経路ではpackageの絶対パスをキーにした一時manifestから子環境を復元します。

情報モデル

PortlessConfig name? script? appPort? proxy? apps? turbo? AppConfig name? script? appPort? proxy? WorkspacePackage dir name? scripts ProxyConfig useHttps customCertPath? customKeyPath? lanMode lanIp? tlds useWildcard ServiceConfig stateDir proxyPort useHttps lanMode tlds useWildcard StateDirectory dir environmentOverride? TurboUserStateDirectory fixedUserStateDir RouteMapping hostname port pid tailscaleUrl? tailscaleHttpsPort? tailscaleFunnel? ngrokUrl? ngrokPid? TurboManifest loaderPath manifestPath entries ChildEnvironment PORT HOST? PORTLESS_URL NODE_EXTRA_CA_CERTS? PORTLESS_LAN? PORTLESS_TAILSCALE_URL? PORTLESS_NGROK_URL? apps 1 many resolves 1 0..1 influences 0..1 many reconstructs 1 1 governs 1 many routes-json 1 many dev-manifest 1 0..1 absolute-path-key 1 0..1 entries 0..1 many

公開設定で特に重要なのはAppConfigです。namescriptは空でない文字列、appPortは1〜65535の整数、proxyはbooleanです。RouteMappingの必須fieldはhostnameportpidで、静的aliasはpid: 0を使います。共有中はTailscaleのURL・HTTPS port・Funnel判定や、ngrokのURL・PIDも同じentryへ追加されます。

データの保存先と寿命

データ 保存先 寿命
PortlessConfig / AppConfig portless.jsonまたはpackage.json プロジェクトに永続
WorkspacePackage 起動時にpackage metadataから構築 CLI実行中
ProxyConfig CLI内とproxy.* marker proxy稼働中、異常終了時はmarkerが残る場合あり
RouteMapping routes.json route登録中
CA・server certificate state directoryとhost-certs 複数実行で再利用
ServiceConfig OSのサービス定義 uninstallまで
TurboManifest 固定~/.portless/dev-manifest.json Turbo multi-app実行中
ChildEnvironment 子プロセス環境 子プロセス実行中

状態ディレクトリの実ファイル

パス 内容 注意点
routes.json hostname、port、所有PID、共有metadataの配列 複数CLIで共有
routes.lock 書き込み排他用ディレクトリ 10秒より古いlockはstale扱い
proxy.pid / proxy.port 実行中proxyのPIDとport 正常停止で削除
proxy.tls / proxy.custom-cert TLSとcustom certificateのmarker 正常停止で削除
proxy.tld / proxy.tlds primary TLDと複数TLD 非既定TLDで作成
proxy.lan LAN modeの直近IP LAN無効化で削除
ca-key.pem / ca.pem CA秘密鍵と公開証明書 秘密鍵を共有・commitしない
server-key.pem / server.pem 既定server証明書 SAN不足や期限で再生成
host-certs/* SNI hostname別の証明書 有効なcacheを再利用
ca.trusted 信頼済みCAのfingerprint WSLではwsl: prefix付き
turbo-env-loader.cjs Turbo用CommonJS loader 0.15.5では固定~/.portless
dev-manifest.json package絶対パスから子環境へのmap Turbo終了時に削除

routes.jsonroutes.lockで書き込みを調停します。運用スクリプトから直接編集すると、route更新との競合や不完全JSONを作るため避けてください。

設定解決の優先順位

対象 高い順
単一アプリ名 run --nameportless.json、なければpackage.json.portless → 推論
単一アプリscript --script → プロジェクト設定 → dev
子アプリport --app-portPORTLESS_APP_PORT → app設定 → 自動割当
monorepo package設定 package個別package.json.portless → rootのapps → 推論
自動起動proxyのTLS・TLD・LAN 明示環境変数 → 異常終了後の残存marker → 既定値
proxy startのport --portPORTLESS_PORT → protocol既定値

0.15.5では、異常終了後に残った非既定proxy.portが自動起動時のPORTLESS_PORTより優先される例外があります。また、wildcard用の通常state markerはありません。再起動後も維持する場合はservice install --wildcardでOSサービス定義へ保存します。

TLSの解決も項目固有です。--no-tlsまたはPORTLESS_HTTPS=0/falseのどちらかがあるとHTTPになります。--httpsだけでは無効化環境変数を打ち消しません。ただしcustom --cert--keyの組はHTTPSを有効にします。

サービス設定の永続化形式

OS 定義パス 形式
macOS /Library/LaunchDaemons/sh.portless.proxy.plist launchdのProgramArgumentsEnvironmentVariables
Linux /etc/systemd/system/portless.service systemdのExecStartEnvironment=
Windows %ProgramData%\portless\service\portless-service.cmdportless-task.xml SYSTEMで動くTask Scheduler定義

構築方法

前提条件

  • Node.js 24以上
  • macOS、Linux、Windowsのいずれか
  • 既定HTTPSで証明書を生成する場合はPATH上のOpenSSL CLI
  • 共有機能を使う場合だけTailscale CLIまたはngrok CLI

Portless自身を開発する場合はpnpm 11も必要ですが、利用するだけならnpmで導入できます。

グローバルインストール

公式READMEはグローバルインストールを推奨しています。標準portへのbind時に権限昇格があり得るため、npx portlesspnpm dlx portlessによる一時取得コードの実行はCLIが拒否します。

npm install -g portless@0.15.5
portless --version
# 0.15.5

チームでversionを固定する場合はdev dependencyにできます。

npm install -D --save-exact portless@0.15.5
{
  "scripts": {
    "dev": "portless run next dev"
  },
  "devDependencies": {
    "portless": "0.15.5"
  }
}

初回HTTPSとローカルCA

HTTPSとHTTP/2は既定で有効です。初回にPortlessがローカルCAとserver certificateを生成し、CAをOSのtrust storeへ追加します。Node.jsはOSのtrust storeを直接使わないため、条件を満たす子プロセスにはNODE_EXTRA_CA_CERTSとしてca.pemのpathも渡します。

portless myapp next dev

# 初回の信頼登録を後から行う場合
portless trust

443へのbindはmacOSとLinuxで権限昇格を伴います。port bindのsudoを避けたい場合は、1024以上を指定します。

portless proxy start --port 1355
portless myapp next dev
# https://myapp.localhost:1355

非rootではport 80のredirect listenerが起動できない場合があります。http://myapp.localhostからの転送を前提にせず、HTTPS URLへ直接アクセスしてください。再起動後も1355を維持する運用では、実行中markerではなくservice definitionへ保存します。

portless service install --port 1355

TLSも権限昇格も不要なら、HTTPと非特権portを組み合わせます。

portless proxy start --no-tls --port 1355
portless myapp next dev
# http://myapp.localhost:1355

利用方法

3つの起動モード

# zero-arg: package.jsonのdev scriptと名前を推論
portless

# run: 名前を推論し、子commandを明示
portless run next dev

# named: 完成済みの名前を明示
portless shop next dev

portless run --name shopはbase nameを上書きしつつworktree prefixを維持します。一方、portless shop ...は完成済みの名前として扱い、worktree prefixを付けません。Portless自身のflagは子commandより前へ置きます。

固定portとframework注入

portless run --app-port 4173 vite preview

# 同じ指定を環境変数で行う
PORTLESS_APP_PORT=4173 portless run vite preview

固定portの優先順位は--app-portPORTLESS_APP_PORT、app設定、自動割当です。通常の子プロセスにはHOST=127.0.0.1を渡し、LAN modeでもchildはloopbackへbindします。例外はExpoのLAN modeで、Expo自身のdiscoveryを妨げないようHOSTを省略します。

URL取得と静的alias

BACKEND_URL="$(portless get backend)"
export BACKEND_URL

portless alias admin-ui 8080
portless list
portless alias --remove admin-ui

aliasはPortlessが起動しない既存のplain HTTP・WebSocket backendに名前を付けます。proxy transportはHTTPとWebSocketであり、PostgreSQLやRedisなどのraw TCP protocolは中継できません。

portless.json

{
  "name": "shop",
  "script": "dev:app",
  "appPort": 3100,
  "proxy": true,
  "turbo": false
}

これは単一アプリ用の例です。portless.jsonは配置したディレクトリだけを読み、親へ探索しません。代わりにpackage.jsonportlessキーも使えます。同じディレクトリに両方ある場合、0.15.5はportless.jsonを優先します。

monorepoとTurborepo

workspace rootでzero-argを実行すると、対象scriptを持つpackageをまとめて起動します。pnpm-workspace.yamlを先に確認し、次にpackage.jsonのworkspacesを確認します。

{
  "apps": {
    "apps/web": { "name": "shop" },
    "apps/api": { "name": "api.shop" },
    "packages/typecheck": { "proxy": false }
  },
  "turbo": true
}

appsがある場合は、一致したpackageのentryだけがそのpackageの設定になります。トップレベルのnamescriptappPortproxyapps配下へ共通defaultとしてmergeされないため、必要な値は各entryへ明記します。

turbo.jsonがありturbofalseでなければ、packageごとのPORTHOSTPORTLESS_URLをmanifestへ書き、Node loaderをNODE_OPTIONSへ追加します。turbo: falseではPortlessが各packageを直接spawnします。

package script自体からPortlessを呼ぶ場合は、実際のdev serverを別scriptへ分けて再帰を避けます。

{
  "scripts": {
    "dev": "portless",
    "dev:app": "next dev"
  },
  "portless": {
    "name": "shop",
    "script": "dev:app"
  }
}

Git worktree

linked worktreeではbranch名の最後のsegmentがprefixになります。

# main worktree
portless run --name shop next dev
# https://shop.localhost

# branch feature/fix-ui の linked worktree
portless run --name shop next dev
# https://fix-ui.shop.localhost

portless get backend --no-worktree

custom TLDとLAN mode

portless proxy start --tld test
portless shop next dev
# https://shop.test

portless proxy stop
portless proxy start --tld localhost --tld dev.example.com

--tldは繰り返し指定でき、longest matchで解決します。.testはIANA予約済みです。.localはmDNS、.devはHSTSと衝突するため避けます。

LAN modeはTLDを.localへ切り替え、custom TLDとは併用しません。

portless proxy start --lan

# LAN IPを固定
portless proxy stop
portless proxy start --lan --ip 192.168.1.42

macOSはdns-sd、Linuxはavahi-publish-addressを使います。0.15.5のLAN modeはWindows非対応です。別端末からgenerated CAのHTTPSを使う場合は、公開証明書ca.pemだけを端末へ配布してtrust storeへ登録します。ca-key.pemは配布しません。

Tailscale、Funnel、ngrok

# tailnet内
portless shop --tailscale next dev

# public Funnel
portless shop --funnel next dev

# public ngrok
portless shop --ngrok next dev

Tailscale Serveは既存設定を含む使用中portを調べ、443、8443、8444…の優先列から未使用portを選びます。Funnelの候補は443、8443、10000です。実効URLはportless listで確認してください。

共有edgeはchild portへ直接転送します。ローカルPortlessのTLSやhostname routingを共有URLの認証境界と考えてはいけません。Portlessはアプリ認証を追加しないため、ACL、provider側のaccess policy、アプリの認証・認可が必要です。

一時的にPortlessを使わない

PORTLESS=0 pnpm dev
PORTLESS=0 portless run next dev

公開ドキュメントが案内する値はPORTLESS=0です。0.15.5は互換値としてfalseskipも受け付けますが、自動化では0に揃えると意図が明確です。

運用

日常確認とforeground診断

portless list
portless doctor
portless service status

listは生存中の動的routeと静的aliasを表示します。doctorはNode.js、state directory、proxy応答、route、名前解決、CA trust、LAN前提条件を読み取り専用で診断します。proxy停止はwarningなので、終了codeだけでなく表示内容を確認してください。

詳細ログが必要ならforegroundで起動します。

portless proxy stop
portless proxy start --foreground --port 1355

OS起動サービス

portless service install
portless service status
portless service uninstall

install時のport、TLS、LAN、IP、TLD、wildcard、custom certificate、state directoryはOSのservice definitionへ保存されます。shellで一時指定した環境変数とは別の永続経路なので、設定変更後は再installしてdoctorで確認します。

routeと孤児プロセス

動的routeは所有CLIのPIDを記録し、正常終了時に自分のrouteを削除します。静的aliasはpid: 0で、死活判定から除外されます。

portless pruneには重要な注意点があります。所有CLIが死んだrouteを除去した後、そのrouteに記録されたportの現在のlistenerへSIGTERMを送ります。元のdev serverとの同一性や親子関係は確認しません。portが別processに再利用されていると、無関係なprocessを停止する可能性があります。

portless listはstale routeを表示しません。prune前にPORTLESS_STATE_DIR/routes.json、未指定なら~/.portless/routes.jsonを読み取り専用で確認し、記録portのlistenerをlsof -i :<port>またはWindowsのnetstat -anoで照合してください。prune --forceは同じlistenerへSIGKILLを送るため最終手段です。

CA、hosts、stateの清掃

portless trust
portless hosts sync
portless hosts clean
portless clean

hosts cleanはPortless管理blockだけを除去します。cleanはproxy、service、Tailscale登録、CA trust、hosts block、allowlistに含まれるstate artifactを整理します。custom certificateの入力ファイルやstate directory内の未知のファイルは削除しません。

ベストプラクティス

versionとstateをチームで揃える

pre-1.0ではversion差がstate formatやCA trustへ影響する可能性があります。global installの更新時期を揃えるか、dev dependencyをexact versionで固定してください。更新後はportless --versiondoctor、主要routeのHTTPSとWebSocket HMRを確認します。

通常・LAN・public共有を分離する

経路 到達範囲 Portless local TLS 主な対策
通常proxy 原則として同一host 適用 CA秘密鍵を保護、loopbackを維持
LAN mode 同一network proxy設定に従う 信頼できるLANと必要時間に限定、端末へCA公開証明書のみ配布
Tailscale Serve tailnet policyの範囲 経由しない ACL・grantsとアプリ認証を確認
Tailscale Funnel Internet 経由しない debug endpointやsource mapを除き短時間だけ公開
ngrok Internet 経由しない provider policyとアプリ認証を設定

Funnelとngrokの生成URLは「秘密URL」ではありません。開発環境のdebug endpoint、環境情報、認証bypassが露出しないかを公開前に確認してください。

state directoryを手編集しない

routes.jsonは複数CLIが共有します。route conflictは対象を確認して--force、stale routeはlistenerを照合してprune、全体初期化は削除範囲を確認してcleanを使います。ca-key.pemやhost certificate keyをリポジトリや共有ストレージへ置かないでください。

CIではproxyを事前準備する

proxy未起動で443のbindやCA trustにsudo promptが必要な場合、TTYのない環境は早期終了します。TLSが不要なCIでは、非特権portかつHTTPのforeground proxyを先に起動します。HTTPSが必要なら、権限のある事前setupでCA生成とportless trustを済ませてからproxyを起動してください。

portless proxy start --foreground --no-tls --port 1355 &
PORTLESS_PORT=1355 portless run npm run dev

# proxy不要のbuildやtest
PORTLESS=0 npm run build

トラブルシューティング

症状 主な原因 対処
TTYなしで初回起動できない 443 bindまたはCA trustにsudo promptが必要 TLS不要なら--no-tls --port 1355、HTTPS必須なら権限のある事前setupでCA生成とtrustを完了
証明書警告 CA未信頼、CA更新、markerとOS storeのずれ doctor後にtrustを再実行。WSLはWindows側も確認
custom TLDだけ名前解決できない hosts同期失敗、resolver差 hosts syncを実行し権限を確認
404 route未登録、終了済み、strict modeの未登録subdomain listでexact hostnameを確認し再登録。必要時だけ--wildcard
502 child停止、別portでlisten、loopbackから到達不能 child logとdoctorを確認しPORTまたは--app-portを反映
508 upstream proxyがPortlessへ戻るloop Vite等のproxy設定を見直し、再帰routeを解消
LAN mode失敗 OS非対応、IP未検出、LinuxのAvahi不足 doctoravahi-utils--lan --ipを確認
Tailscale共有失敗 CLI未接続、HTTPS capability、Funnel permission不足 tailscale statusと管理画面を確認
ngrok共有失敗 CLI未導入、authtoken未設定 ngrok config add-authtoken <token>後に再実行
dev serverが残る CLI異常終了とstale route routes.jsonと現在listenerを照合してからprune--forceは最終手段
proxy設定が期待と違う 実行中・残存marker、service definition service statusdoctorを確認し、必要ならserviceを再install
routes.json破損 異常終了や外部編集 稼働アプリを止め、clean後に再登録。手修復しない

custom state directoryを使う場合は、診断commandにも同じPORTLESS_STATE_DIRを渡してください。指定を忘れると別のstateを読んでいるように見えます。

PORTLESS_STATE_DIR="$PWD/.portless-debug" \
  portless proxy start --foreground --port 1355

まとめ

Portlessは、可変portのdev serverを安定した名前付きHTTPS URLへ置き換え、アプリ起動、route登録、monorepo、worktree、共有URLまでを一つのCLIへまとめます。複数プロジェクトやAIコーディングエージェントを並行稼働させる環境では、ポート探索をなくし、ブラウザー状態をhostname単位で分離できる点が大きな利点です。

一方で、0.15.5はNode.js 24以上を要求するpre-1.0版です。Turboのcustom state directory例外、共有edgeがlocal proxyを迂回する認証境界、pruneが現在のport占有者を停止する仕様など、実装上の制約を理解して導入してください。通常はloopbackを維持し、LAN・Funnel・ngrokは必要な時間だけ明示的に有効にする運用が適しています。

この記事が少しでも参考になった、あるいは改善点などがあれば、ぜひリアクションやコメント、SNSでのシェアをいただけると励みになります!

参考リンク