典型例:ブラウザは通るのにDockerだけレジストリに届かない

Clashをデスクトップで動かし、ブラウザからregistry-1.docker.ioやプライベートレジストリのHTTPSにアクセスできる状況でも、同じマシン上のDocker CLIからのプルだけがi/o timeoutTLS handshake timeoutconnection resetなどで失敗することがあります。これは単に「Dockerがプロキシを知らない」か、「知っているつもりの設定がdockerdやBuildKitに届いていない」かのどちらかであることが多いです。

また、docker compose upで立ち上がったアプリケーションコンテナが、ホストでcurlしたときと同じ出口を使っていない——というケースも頻出です。デフォルトのbridgeでは、コンテナは独自の名前空間とルーティングを持ち、ホストの環境変数は自動では引き継がれません。 「pullは直ったのにapt-getだけ失敗する」は、この層のズレが典型です。

前提:ホスト側ClashのmixedポートとAllow LAN

HTTP(S)トラフィックをClashへ送る基本形は、http://<PROXY_HOST>:<PORT>です。Clash系クライアントではmixedのリスンがHTTP CONNECTとSOCKSの両方を扱いやすく、HTTP_PROXYALL_PROXYを揃えやすいです。DockerやGit、npmなど他ツールとの横断設定は開発者向け分流記事NO_PROXY思考と併せて読むと、ローカルregistryや社内ホストの除外がブレにくくなります。

プロキシが127.0.0.1だけにバインドされ、LANからの接続を拒否していると、仮想化層や別ネットワーク名前空間から届きません。Allow LAN相当を有効にし、必要ならOSのファイアウォールでTCPポートを許可してください。WindowsでWSL2やDocker Desktopと併用する場合は、WSL2記事で述べたとおり、127.0.0.1が「どちら側のループバックか」を先に固定すると手戻りが減ります。

購読リンクとノードは別レイヤ Clashの購読URLでノード一覧を取り込む処理と、Dockerがコンテナイメージをレジストリから取得する処理は別です。本稿は後者の経路づくりが主題で、YAMLの細部はルール記事側で扱います。

どのプロセスがプロキシを読むか:CLI・dockerd・BuildKit

大枠として、(A) ユーザーが叩くdockerコマンド、(B) 常駐するdockerd(Linuxでは多くがsystemd管理)、(C) 有効な場合のBuildKitビルダー、(D) 実行中コンテナ内のアプリ——が、それぞれ別の設定源を持ちます。シェルにexport HTTP_PROXY=...しただけでは(B)に届かないことがあり、結果としてdocker pullの挙動が変わらない、という報告が繰り返されます。

逆に、docker run -e HTTP_PROXY=...は主に(D)向けです。(B)を変えずに(D)だけ直しても、プルやイメージ解決は改善しないことがあります。切り分けの最初の一歩は、「失敗ログがCLIの出力か、デーモンのjournalか、BuildKitのログか」を見分けることです。

シェル環境変数と~/.docker/config.jsonproxies

多くのクライアントはHTTP_PROXYHTTPS_PROXYNO_PROXY(小文字のhttp_proxy系も)を参照します。HTTPS宛の通信でも、プロキシURLスキームはhttp://が一般的です(CONNECTでTLSトンネルを張る)。NO_PROXYにはlocalhost127.0.0.1、社内レジストリホスト、リンクローカルなdocker内部ホスト名などを入れ、意図せぬループや遅延を避けます。

Docker CLIは~/.docker/config.jsonproxiesブロックでデフォルトのプロキシを指定できます(Dockerのドキュメントに例があります)。ここは「CLIがdockerdへ送る前のクライアント側の挙動」に関わるため、環境変数との優先や併用ルールは実際のDockerバージョンの説明に従ってください。設定を変えたら、シェルセッションの再ログインや、デーモン再起動が必要な層まで含めて確認します。

# Example: replace HOST/PORT with your Clash mixed listener
export HTTP_PROXY="http://HOST:PORT"
export HTTPS_PROXY="$HTTP_PROXY"
export NO_PROXY="localhost,127.0.0.1,::1"

Linux:dockerdへ環境変数を渡す(systemdドロップイン)

LinuxネイティブDockerでは、イメージのプルやレジストリへの通信がdockerd側で行われるため、デーモンの環境HTTP_PROXYなどを入れる必要がある場面があります。典型的には/etc/systemd/system/docker.service.d/以下にhttp-proxy.confのようなドロップインを置き、[Service]Environment=で指定し、systemctl daemon-reloadのうえdockerを再起動します。

社内ポリシーやディストリビューションによってパスが異なるため、必ず自環境のunitファイルとDistributionのドキュメントを確認してください。Docker Desktop for Mac/Windowsは管理UIや内部VMのため、ホストのsystemdとは別レイヤになります。Desktop利用時は公式の「プロキシ」設定と、本稿の環境変数のどちらが実際のプルに効いているかをログで照合するのが安全です。

Docker Compose:services.environmentbuild.args

Composeファイルでは、実行時にコンテナへ渡す変数をservices.<name>.environmentに列挙します。ここにHTTP_PROXYを書くと、そのサービス内のプロセス(例: aptpip、アプリのHTTPクライアント)が参照します。一方、イメージビルド時にレジストリへアクセスする処理はdocker compose buildの文脈にあり、build.argsやBuildKitの仕組みとセットで考えます。

複数サービスで同じ値を繰り返す場合は、トップレベルのenvironment.envファイルとの合成ルール(Composeのバージョン差)に注意してください。チームで共有するなら、<HOST>:<PORT>をどのIPで表すか(後述のbridge/host)をREADMEに明記し、WSL2のapt向け手順と混同しないよう、Docker用の表記を分けます。

# compose.yaml fragment (illustrative)
services:
  app:
    build:
      context: .
      args:
        HTTP_PROXY: ${HTTP_PROXY}
        HTTPS_PROXY: ${HTTPS_PROXY}
    environment:
      HTTP_PROXY: ${HTTP_PROXY}
      HTTPS_PROXY: ${HTTPS_PROXY}
      NO_PROXY: ${NO_PROXY}

BuildKitとbuildx:ビルドステージのARGとレジストリ取得

docker buildx buildやBuildKit有効時のdocker buildでは、各ステージでARG HTTP_PROXYを受け取り、RUNの前にENVへ反映するパターンが一般的です。自動でホストのプロキシを注入する機能は環境やバージョンに依存するため、明示的に--build-argを渡す方が再現性が高いです。

マルチステージビルドでは、依存取得ステージとランタイムステージで必要な変数が異なることもあります。ベースイメージのプル自体が失敗する段階なら、まずホスト側のdocker pullとdockerd設定を直し、DockerfileのFROMが解決してからステージ内のRUNを追います。開発者向けのドメイン分流はCursor/npm記事と補完的ですが、対象は主にホスト上のツールチェーンであり、コンテナ内のnpmまで同じルールが自動では効きません。

bridgeとhost:127.0.0.1とゲートウェイの落とし穴

デフォルトbridge上のコンテナから見て、127.0.0.1コンテナ自身です。ホストでListenしているClashに届けるには、(1) hostネットワークモードでホストのネットワークスタックを共有する(Linuxで利用可否は実行環境に依存)、(2) host.docker.internal(対応プラットフォーム)、(3) docker0ブリッジのゲートウェイIP(例として172.17.0.1がよく挙がるが環境固有)——のいずれかで「ホスト側のプロキシポート」へルーティングします。

network_mode: hostにすると名前解決やポートバインドの挙動が変わるため、本番相当のComposeではセキュリティと可搬性のトレードオフを確認してください。逆に、bridgeのままでは「ホストのClashだけ通したい」場合でも、コンテナから到達可能なIPとポートを一つに決め打ちし、NO_PROXYでローカル開発用のバックエンドを除外する、という二段構えが安定しやすいです。

検証の順序(チェックリスト)

(1) ホストのシェルでcurl -v --proxy http://HOST:PORT https://example.comによりプロキシ単体を確認。(2) 同じ変数でdocker pull hello-world。(3) 失敗時はdockerd/journal側のログで接続先と拒否理由を確認。(4) Composeはdocker compose configで実際に注入される環境を確認。(5) buildxは--progress=plainでビルドログを詳細に。(6) コンテナ内ならdocker compose run --rm app shenv | grep -i proxy——の順が扱いやすいです。

DNSがClashのfake-ip系と干渉する場合は、DNS記事でモードの差を確認してください。レジストリ名が社内DNSのみ解決するケースでは、Docker側のdns設定も別途検討が必要です。

症状早見表

症状 想定原因 対処の方向性
シェルではcurlOK、docker pullだけNG 設定がdockerdに未反映 Linuxはsystemdドロップイン、Desktopは公式プロキシ設定
ビルドのRUNだけ外部取得NG ビルドステージにPROXY未注入 --build-arg、DockerfileのARG/ENV
コンテナ内のaptだけNG bridgeでホストPROXYが未設定 Composeのenvironment、適切なPROXY_HOST
127.0.0.1:PORTに接続できない(コンテナ内) 127がコンテナ自身を指す ゲートウェイ/host.docker.internal/hostモード
TLS handshake timeout 経路・MTU・中間機器 プロキシ経由で再試行、別ノード、MTU/TUN確認

よくある質問

Q. docker pullは通るのにコンテナ内のaptだけ失敗します

A. ホストのシェルに設定したHTTP_PROXYは、デフォルトのbridge上のコンテナには自動では引き継がれません。Composeのservices.environmentやビルド時のargsで明示するか、コンテナからホストのプロキシへ届くアドレスを選んでください。

Q. 127.0.0.1のClashにコンテナからどう繋げばよいですか

A. bridgeでは127はコンテナ自身です。Docker Desktopではhost.docker.internalが使える場合があります。Linuxではdocker0ゲートウェイや明示的なホストIPの利用が一般的です。環境ごとにip routedocker network inspectで確認してください。

Q. HTTP_PROXYを設定したのにdocker pullだけ変わりません

A. プルはdockerdが行うため、シェル変数だけでは足りないことがあります。LinuxではsystemdのEnvironment=、併せてconfig.jsonproxiesを確認し、変更後はデーモン再起動が必要かどうかを見ます。

Q. buildxのビルド中のレジストリだけ失敗します

A. BuildKitはステージごとに環境が分かれます。docker buildx build --build-arg HTTP_PROXY=...やComposeのbuild.args、DockerfileのARGを揃え、実行時のenvironmentとは別レイヤだと理解してください。

まとめ:ホストClashをDockerの各レイヤに明示的に接続する

Clashがブラウザで「見えている」経路を、Docker CLI・dockerd・BuildKit・各コンテナへ同じ論点でコピーすると、タイムアウトやTLS失敗の切り分けが速くなります。WSL2の開発ツール連携はWSL2記事、単体Linuxサーバはヘッドレス記事と役割分担してください。クライアント本体の入手はダウンロードページを優先し、購読リンクの運用は各プロバイダの規約に従ってください。

Clashを無料ダウンロードし、mixedポートとLAN許可を固めたうえで本稿のとおりHTTP_PROXY階層を揃えると、他の開発ツールと同じ出口へDockerも乗せやすくなります。ブラウザ専用プロキシとCLIのズレが減り、イメージ取得とビルドの再現性も上がります。

ほかの記事は技術コラム、一般的な案内はヘルプを参照してください。