Node.jsのEADDRINUSEを安全に解決する|portを使うprocessと起動元を特定

Node.jsのEADDRINUSEをhost・port・listenerの起動元から調べ、停止対象の判断、再発防止、起動後の確認へ進めます。

AIものさし編集部

Node.jsのEADDRINUSEは、サーバーが使おうとしたlocal addressを別のサーバーがすでに使っていて、bindできないときのエラーです。port番号だけを見てprocessを強制終了せず、address、port、実行場所、listenerの起動元を確認します。同じ名前のNode processでも、別projectの開発サーバーや自動起動されたworkerかもしれません。

この記事のコマンド例は、lsofが使えるmacOSやUnix系の環境を想定します。Windowsのprocess調査やcontainer内部は、その環境に合う管理方法で確認してください。以下のport・PIDは架空例で、実端末のport調査、process停止、Node サーバー起動は行っていません。

エラーからaddressとportを取り出す

Node.jsのErrors資料(新しいタブで開く)でEADDRINUSEの意味を確認し、実際のエラーのcode、syscall、address、portを記録します。EACCESや接続先からのECONNREFUSEDは、同じ停止手順へ進めません。

表は横にスクロールできます

エラーや観測最初の確認混同しないこと
EADDRINUSE / listenbindしようとしたaddressとport外部APIへの接続失敗
EACCESその環境の権限・許可条件既存processを止めれば解決
ECONNREFUSED接続先の受付と経路自分のlisten port競合
起動後に別portへ切替frameworkの自動選択予定portで起動したと扱う

127.0.0.1、0.0.0.0、IPv6の::など、どのaddressで待ち受けようとしたかも残します。広いaddressへのbindやIPv4/IPv6の扱いは環境に影響されるため、同じportでも「localhostの一行しか見ていない」状態で空きと決めません。

listenerを読取だけで確認する

lsofが導入済みで利用可能なら、port 3000のTCP listenerを見る例は次です。3000は実際のエラーにあるportへ置き換えます。このコマンドは一覧の読取例で、停止操作は含みません。

lsof -nP -a -iTCP:3000 -sTCP:LISTEN

lsofのmanpage(新しいタブで開く)で、TCPの選択とLISTEN状態の指定を確認できます。出力のCOMMAND、PID、USER、NAMEに相当する欄から対象を絞ります。権限やOSによって見える範囲は異なるため、結果が空でも他ユーザーや別namespaceのlistenerが存在しない証明にはしません。

表は横にスクロールできます

読み取る項目確認する問いまだ分からないこと
PIDどのprocessがlistenerを持つか停止してよいか
USER誰のprocessかprojectや起動元
COMMANDどの実行物か同名processの用途
address/portどのendpointを使うかproxyやcontainer越しの関係

host側の公開portとcontainer内のlisten portは別です。エラーがDockerの公開port設定から出ているのか、container内のNodeから出ているのかを先に分けます。別namespaceの調査結果を同じport空き判定に使わないようにします。

PIDだけでなく起動元を調べる

PID 12345を調べる架空例です。実際には直前に確認した対象PIDを使います。processは入れ替わる可能性があるため、古いメモのPIDを停止操作へそのまま使いません。

ps -p 12345 -o pid=,ppid=,user=,comm=

親process、起動したterminal、IDE task、package script、process managerやservice管理を確認します。引数全文にはsecretや接続文字列が含まれる場合があるため、調査結果を共有するときは必要な項目だけにします。

watcherやprocess managerが自動で再起動しているなら、子processだけを終了しても再びportを使います。元の起動元で停止する方針を選び、同じappをterminalとIDEの両方から二重起動していないかも確認します。

表は横にスクロールできます

判明した状態次の選択元へ戻すための記録
自分の古い開発サーバー元のterminal/taskから正常停止起動コマンドと作業場所
別projectの必要なサーバー停止せず自分のport変更を検討変更前のportと参照先
service管理下のprocess管理元と所有者を確認service名と通常の開始・停止方法
所有者・用途が不明診断情報を渡して確認address/port・PID・発生時刻

process名でNodeを一括終了したり、portに一致したPIDへ無条件に強制終了を送ったりする手順にはしません。元のサーバーを止める場合も、未保存の作業や処理中の要求がないか確認します。

portを変える場合は参照先を追う

自分のサーバーを別portで起動する場合、API base URL、proxy、テスト設定、開発用callbackなどが古いportを参照していないかを調べます。サーバーが起動しても、browserや別serviceが旧portへ接続していれば、古いappを見続けることがあります。

変更した設定を記録し、うまくいかなければその変更だけを戻せるようにします。OAuthのcallbackや外部連携先が関係する場合は、単にport番号を書き換えて終わりにせず、その設定の所有者と許可条件も確認します。

再起動後に正しいprocessの応答かを確認する

目的のサーバーを起動したら、起動ログのaddress/port、現在のlistener、アプリの既知の画面や版情報を対応付けます。エラーが消えたことと、意図したprojectが応答していることを別々に確認します。

再び競合するなら、自動起動元の重複、同じapp内の複数listen、watcher再起動時の終了処理などへ調査を進めます。一時的に空いたportへ移るだけで原因解消とせず、通常の起動・停止を繰り返して同じ構成が再現できることを検証計画に含めます。

確認した公式資料

質問から自分に合うAIを探す