シナリオ
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

