「Address already in use」の解決 - ポート競合の特定と解放

「Address already in use」の解決 - ポート競合の特定と解放

この記事で解決できること

  • Address already in use(EADDRINUSE)がなぜ出るのかを理解できます
  • ポートを占有しているプロセスを特定して安全に解放できます
  • 「さっき止めたのにまた出る」TIME_WAIT 由来の競合を切り分けられます

結論(最短ルート)

  1. 誰が掴んでいるか特定sudo ss -lntp | grep ':PORT '(または sudo lsof -i :PORT
  2. 正しく停止:サービスなら systemctl stop、単発プロセスなら kill PID
  3. プロセスが無いのに出る:TIME_WAIT が原因 → アプリ側 SO_REUSEADDR か数十秒待つ

前提(対象環境)

  • OS:Ubuntu(systemd 環境)
  • 対象:自前のサーバ / アプリを起動して Address already in use に当たった人
  • 例として 8080 番ポートを使用(読み替え可)

「Address already in use」とは何か?

結論: アプリが bind() で確保しようとしたポートを別のプロセスが既に握っているため、OS が確保を拒否しているエラー。

サーバ系プロセスは起動時に「このポートで待ち受ける」と OS に宣言する(bind() システムコール)。同じ IP・ポートの組を二重に確保することは原則できないため、既に使われていると bind()EADDRINUSE を返す。

典型的なメッセージ:

Error: listen EADDRINUSE: address already in use :::8080
bind: Address already in use
OSError: [Errno 98] Address already in use

Errno 98 は Linux における EADDRINUSE の番号。言語・フレームワークが違っても原因は同じ。

なぜポートが競合するのか?

結論: 原因は「①前のプロセスが生きている」「②多重起動」「③直前に落としたプロセスの TIME_WAIT」のほぼ 3 種に集約される。

  • 前のプロセスが残っている:再起動したつもりが旧プロセスが生存。デタッチ起動(& / nohup / コンテナ)で見落としやすい
  • 多重起動:同じアプリを 2 回起動、または別アプリが同じポートを使用
  • TIME_WAIT:直前に終了したプロセスのコネクションが TCP の TIME_WAIT 状態で残り、SO_REUSEADDR 未設定のアプリが再バインドに失敗

①②は「プロセスを止める」で解決する。③だけはプロセスが存在しないため別アプローチになる(後述)。

誰がポートを使っているか特定するには?

結論: ss -lntp で待受プロセスと PID を確認するのが最短。lsof / fuser も同じ情報を別表現で出す。

ss で待受プロセスを特定(推奨)

sudo ss -lntp | grep ':8080 '
LISTEN 0 511 0.0.0.0:8080 0.0.0.0:* users:(("node",pid=12345,fd=18))
  • users:(("node",pid=12345,...))pid=12345 が占有元
  • -l 待受 / -n 数値表示 / -t TCP / -p プロセス(sudo 必須)

lsof で確認

sudo lsof -i :8080
COMMAND   PID  USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
node    12345  hide   18u  IPv4  98765      0t0  TCP *:8080 (LISTEN)

fuser で PID だけ素早く

sudo fuser 8080/tcp
8080/tcp:            12345

バインド先が 127.0.0.1:80800.0.0.0:8080 かも ss で確認できる。同じポートでも 127.0.0.10.0.0.0 は別扱いになるため、競合の有無の判断材料になる。

プロセスを止めてポートを解放するには?

結論: サービス管理下なら systemctl stop、手動起動プロセスなら kill PIDkill -9 は最終手段。

サービスとして動いている場合

sudo systemctl stop myapp.service

systemd 管理下のプロセスを kill で直接落とすと、自動再起動(Restart= 設定)で即復活しポートを掴み直すことがある。必ず systemctl stop を使う。

手動起動のプロセスの場合

特定した PID に対して、まず通常のシグナル(SIGTERM)で停止を試みる:

sudo kill 12345

数秒待っても残る場合のみ、強制終了(SIGKILL):

sudo kill -9 12345

kill -9 はプロセスに後始末(接続のクローズ・一時ファイル削除)の機会を与えず即座に殺す。データ破損やロックファイル残留の原因になるため、まず kill(SIGTERM)を試し、それでも落ちないときの最終手段とする。

停止後、ポートが解放されたか確認:

sudo ss -lntp | grep ':8080 '

何も出力されなければ解放済み。

プロセスが無いのに出る(TIME_WAIT)ときは?

結論: 直前に落としたプロセスの接続が TIME_WAIT で残っている状態。アプリが SO_REUSEADDR を設定すれば即再起動でき、未設定なら数十秒で自然解消する。

ss -lntp で待受プロセスが見つからないのに Address already in use が出る場合、TIME_WAIT が疑わしい。確認:

ss -tan state time-wait | grep ':8080'

TIME_WAIT は TCP が「遅延パケットの取り違え」を防ぐための正常な状態で、通常 60 秒前後で消える。対処は次のいずれか。

  • アプリ側で SO_REUSEADDR を有効化(推奨・恒久対策):多くのサーバ実装はデフォルトで設定済み。自作サーバなら listen ソケットに設定する
  • 数十秒待ってから再起動:暫定対応

再発を防ぐには?

結論: アプリに graceful shutdown と SO_REUSEADDR を実装し、起動スクリプトで旧プロセスの停止を確実にする。

  • SO_REUSEADDR を設定:再起動時の TIME_WAIT 由来の失敗を防ぐ
  • graceful shutdown:SIGTERM を受けたら接続を閉じてからプロセス終了する。kill -9 依存を減らす
  • プロセス管理を systemd / コンテナに任せる:手動の & 起動は旧プロセスの取り残しを生みやすい
  • 起動前チェック:起動スクリプト冒頭で ss -lntp | grep ':PORT ' を確認し、占有があれば停止してから起動

やってはいけないこと

結論: 「いきなり kill -9」「ポート番号を変えて逃げる」「tcp_tw_recycle を有効化」は再発と別障害の温床。

やってはいけない1:原因を見ずに kill -9 を連打する

占有元が systemd 管理サービスなら、kill しても再起動で復活する。まず ss で正体を確認し systemctl stop する。

やってはいけない2:ポート番号を変えて回避する

8080 が競合するから 8081 にする、を繰り返すと「どのポートで何が動いているか」が崩壊する。占有元を特定して解放するのが筋。

やってはいけない3:tcp_tw_recycle を有効化する

過去のネット記事に残る対策だが、NAT 環境で接続不能を引き起こす危険な設定で、現行カーネルでは削除済み。listen 側は SO_REUSEADDR を使う。

コピペ用:特定から解放まで

# 1. 占有プロセスの特定
sudo ss -lntp | grep ':8080 '
sudo lsof -i :8080

# 2-a. サービスなら
sudo systemctl stop <service>

# 2-b. 手動プロセスなら(SIGTERM → 最終手段 SIGKILL)
sudo kill <PID>
sudo kill -9 <PID>

# 3. 解放確認
sudo ss -lntp | grep ':8080 '

# プロセスが居ないのに出る場合(TIME_WAIT)
ss -tan state time-wait | grep ':8080'

次に読む