症状

Docker コンテナ内からホストマシン上のサービス(localhost:3000 など)に接続できない。

結論:まずこれを確認

  1. host.docker.internal を使っているか確認する
  2. ホスト側のサービスが 127.0.0.1 ではなく 0.0.0.0 でリッスンしているか確認する
  3. ホスト側のファイアウォールがブロックしていないか確認する

トラブルシューティングフロー

    flowchart TD
    A[コンテナからホストに接続できない] --> B{どのアドレスを使用?}
    B -->|localhost/127.0.0.1| C[host.docker.internalに変更]
    B -->|host.docker.internal| D{名前解決できる?}
    D -->|No| E[Docker Desktop確認/extra_hosts設定]
    D -->|Yes| F{ホストのサービス起動中?}
    F -->|No| G[サービスを起動]
    F -->|Yes| H{0.0.0.0でリッスン?}
    H -->|No| I[バインドアドレスを変更]
    H -->|Yes| J{ファイアウォール?}
    J -->|ブロック| K[ルールを追加]
    J -->|許可済み| L[ネットワークモード確認]
  

よくある原因

  • localhost/127.0.0.1 を直接指定している - コンテナ内の localhost はコンテナ自身を指す
  • host.docker.internal が解決できない - Linux では追加設定が必要な場合がある
  • ホストのサービスが 127.0.0.1 のみでリッスン - 外部からの接続を受け付けない設定
  • ファイアウォールがブロック - iptables/ufw/firewalld がコンテナからの接続を遮断
  • Docker ネットワークモードの問題 - bridge モードでのルーティング設定不備
  • ホスト側のサービスが停止中 - そもそも接続先が起動していない
  • ポート番号の間違い - コンテナ側で指定したポートが実際と異なる

確認手順

ステップ1: 接続先アドレスを確認する

コンテナ内から接続しようとしているアドレスを確認する。

    # コンテナ内で実行
echo $DATABASE_URL
# または接続コマンドを確認
  

🔍 チェックポイント: localhost127.0.0.1 を使用している場合は host.docker.internal に変更する

ステップ2: host.docker.internal の名前解決を確認する

    # コンテナ内で実行
ping host.docker.internal
# または
getent hosts host.docker.internal
  

🔍 チェックポイント: IP アドレスが返ってくれば名前解決は成功

Linux で解決できない場合の対処:

docker-compose.yml に以下を追加:

    services:
  app:
    extra_hosts:
      - "host.docker.internal:host-gateway"
  

または docker run 時:

    docker run --add-host=host.docker.internal:host-gateway イメージ名
  

ステップ3: ホスト側のサービス状態を確認する

    # ホストで実行
# サービスが起動しているか
ss -tlnp | grep ポート番号
# または
netstat -tlnp | grep ポート番号
  

🔍 チェックポイント: 該当ポートで LISTEN 状態のプロセスが表示される

ステップ4: リッスンアドレスを確認する

    # ホストで実行
ss -tlnp | grep ポート番号
  

出力例:

    LISTEN  0  128  127.0.0.1:3000  *:*  users:(("node",pid=1234,fd=10))
  

🔍 チェックポイント: 127.0.0.1:ポート ではなく 0.0.0.0:ポート または *:ポート であること

127.0.0.1 の場合の対処:

サービスの設定でバインドアドレスを変更する:

    # 例: Node.js
app.listen(3000, '0.0.0.0')

# 例: Python Flask
app.run(host='0.0.0.0', port=3000)
  

ステップ5: ファイアウォールを確認する

    # ホストで実行(Linux)
# iptables の場合
sudo iptables -L -n | grep ポート番号

# ufw の場合
sudo ufw status

# firewalld の場合
sudo firewall-cmd --list-all
  

🔍 チェックポイント: Docker ネットワーク(通常 172.17.0.0/16)からの接続が許可されている

ステップ6: コンテナから接続テストを行う

    # コンテナ内で実行
# curl がある場合
curl -v http://host.docker.internal:ポート番号

# nc がある場合
nc -zv host.docker.internal ポート番号

# telnet がある場合
telnet host.docker.internal ポート番号
  

🔍 チェックポイント: 接続が確立されること(Connection refused や timeout ではない)

NG行動(やってはいけないこと)

  • –network=host を安易に使う - セキュリティリスクが高く、ポートの分離ができなくなる
  • ファイアウォールを全面的に無効化する - セキュリティ上問題。必要なルールのみ追加する
  • 0.0.0.0 でリッスンしたまま本番環境にデプロイ - 意図しない外部公開につながる可能性がある
  • コンテナ内から直接ホストの IP を指定する - 環境依存になり移植性が下がる

よくある質問(FAQ)

Q1: host.docker.internal は Docker Desktop 以外でも使える?

A: Linux ではデフォルトで使えない場合がある。--add-host=host.docker.internal:host-gateway オプションまたは docker-compose.yml の extra_hosts 設定が必要。Docker Engine 20.10 以降で対応。

Q2: WSL2 環境で接続できない場合は?

A: WSL2 では追加のネットワーク設定が必要な場合がある。Docker Desktop for Windows を使用している場合は host.docker.internal が自動設定される。WSL2 内で Docker Engine を直接使用している場合は extra_hosts 設定を追加する。

Q3: コンテナ同士の通信も同じ方法?

A: コンテナ同士は同じ Docker ネットワーク内であればサービス名で通信できる。host.docker.internal はホストマシンへの接続専用。

関連するエラー・症状

  • (関連記事準備中)
  • (関連記事準備中)
  • (関連記事準備中)

解決しない場合

公式ドキュメント

確認すべきログ

    # Docker デーモンログ
sudo journalctl -u docker.service

# コンテナログ
docker logs コンテナ名
  

次に調べるキーワード

  • docker bridge network routing
  • docker iptables rules
  • docker host networking mode