DockerでBind for 0.0.0.0:80 failed: port is already allocatedエラーを修正する方法

beginner🐳 Docker2026-03-17| Docker Engine 20以降、Docker Compose v2、Linux/macOS/Windows(WSL2)

Error Message

Bind for 0.0.0.0:80 failed: port is already allocated
#docker#ポート#バインド

シナリオ

docker compose up または docker run -p 80:80 ... を実行すると、次のエラーが表示されます:

Error response from daemon: driver failed programming external connectivity on endpoint my_container:
Bind for 0.0.0.0:80 failed: port is already allocated

別の何かがポート80を先に確保しています。Dockerはそれを奪うことができないため、コンテナが起動できません。

実際に何が起きているのか

Dockerがポートをマッピングする際、ホスト上の0.0.0.0:80にバインディングを作成し、トラフィックをコンテナに転送します。このバインディングを同時に保持できるプロセスは一つだけです。別のコンテナ、システムのnginx、起動したまま忘れた開発サーバーなど、他の何かが先にそこを確保していると、Dockerは負けてこのエラーをスローします。

よくある原因:

  • 正常に停止しなかった以前のDockerコンテナ(SIGKILL、OOMキル、電源切断など)
  • ホストに直接インストールされたシステムのnginxまたはApache
  • すでにポート80で実行中の別のdocker composeプロジェクト
  • 別のターミナルでまだ実行中のNode.jsまたはPythonの開発サーバー

ステップ1 — ポートを使用しているものを特定する

Linux / macOS の場合

sudo ss -tlnp | grep :80
# または
sudo lsof -i :80

ssのサンプル出力:

LISTEN  0  128  0.0.0.0:80  0.0.0.0:*  users:(("nginx",pid=1234,fd=6))

lsofの場合:

COMMAND   PID     USER   FD   TYPE  DEVICE SIZE/OFF NODE NAME
nginx    1234     root    6u  IPv4   12345      0t0  TCP *:http (LISTEN)

どちらもプロセス名とPIDを表示してくれます。必要な情報はそれだけです。

Windows(PowerShell)の場合

netstat -ano | findstr :80

次にPIDを調べます:

tasklist | findstr <PID>

別のDockerコンテナか確認する

docker ps --format 'table {{.Names}}\t{{.Ports}}'

Portsカラムで:80にマッピングされているものを探します。

ステップ2 — 素早い解決策

Dockerコンテナの場合

# 特定のコンテナを停止する
docker stop <container_name_or_id>

# または実行中のすべてを停止する
docker stop $(docker ps -q)

その後、元のコマンドを再実行します。

システムサービス(nginx、apache)の場合

# Linux — systemd
sudo systemctl stop nginx
sudo systemctl stop apache2

# macOS — brew services
brew services stop nginx

不明なプロセスの場合

# lsof/ssの出力からPIDを使って強制終了する
sudo kill -9 <PID>

ステップ3 — ポートが解放されたか確認する

sudo ss -tlnp | grep :80

出力がなければポートは解放されています。コンテナを起動します:

docker compose up -d

正しくバインドされているか確認します:

docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'

期待される出力:

NAMES       STATUS         PORTS
my_app      Up 3 seconds   0.0.0.0:80->80/tcp

恒久的な解決策 — 競合の再発を防ぐ

方法A:composeファイルのホストポートを変更する

ホストのポート80は必須ではありません。代わりに8080にマッピングしてしまいましょう:

services:
  app:
    image: nginx
    ports:
      - "8080:80"  # ホスト8080 → コンテナ80

http://localhost:8080でアクセスできます。ロードバランサーの背後にある本番環境では、ホストポートはほとんど関係ありません。

方法B:競合するシステムサービスを恒久的に無効化する

# nginxを停止し、起動時に自動起動しないようにする
sudo systemctl disable --now nginx

# 無効化せずに今すぐ停止するだけ
sudo systemctl stop nginx

方法C:すべての前にリバースプロキシを配置する

ポート80を必要とする複数のサービスを実行していますか?直接公開せず、一つのプロキシコンテナがポート80を受け持ち、内部でトラフィックをルーティングすれば、他のサービスはホストネットワークに露出させる必要がありません。

services:
  proxy:
    image: traefik:v3
    ports:
      - "80:80"
  app:
    image: my-app
    # ポートは公開しない — トラフィックはプロキシ経由でルーティングされる

エッジケース:ポートは空きと表示されるがDockerがまだ失敗する

これは意外に気づきにくい問題です。停止したコンテナでも、特にハードキルやOOMイベントの後はポートバインディングを保持したままになることがあります。解決策はコンテナを停止するだけでなく、完全に削除することです:

# 停止中のコンテナも含めてすべて表示する
docker ps -a

# スタックしているコンテナを削除する
docker rm <container_name_or_id>

Docker Composeが正常に終了しなかった後は、手動のクリーンアップをスキップして次を実行します:

docker compose down
docker compose up -d

compose downはネットワークを正しく解体してバインディングをクリアします。単純なdocker stopはそのステップをスキップするため、ゴーストバインディングが残り続けます。

クイックリファレンス

  • ポート所有者を調べる(Linux):sudo ss -tlnp | grep :80
  • ポート所有者を調べる(macOS):sudo lsof -i :80
  • 競合するDockerコンテナを停止する:docker stop <name>
  • 競合するシステムサービスを停止する:sudo systemctl stop nginx
  • 完全なクリーンアップ:docker compose down && docker compose up -d

Related Error Notes