Fix Docker Pull Timeouts: Route Docker Desktop Through Clash on Windows and macOS
What This Guide Covers Demystified
Here the emphasis is on Docker Desktop’s own proxy fields and the difference between “the host can proxy” and “the daemon pulling layers can proxy.” If you still need client basics first, see how to bring in Clash subscriptions and the Clash Verge Rev Windows and macOS tutorial for mixed-port and system-proxy fundamentals.
If you already execute Clash (or a Mihomo-based GUI) and your browser reaches foreign sites, but docker pull, docker build, or curl inside a running container still times out, you are typically looking at a layering problem—not a bad subscription. Docker Desktop on Windows and macOS executes the Docker Engine in a Linux VM or lean backend.
That engine does not on its own inherit your operating-system proxy settings, and it certainly does not inject HTTP_PROXY into every container filesystem unless you configure it. This article separates four concerns—host proxy, engine proxy, WSL2 interaction on Windows, and container environment—so you are able to test each hop in order.
One more angle:We focus on routing registry traffic and typical HTTPS fetches through Clash’s local HTTP or SOCKS port. It complements our Clash and WSL2 routing guide, which targets virtual adapters, out of the box routes, and MTU when the entire Linux side loses connectivity.
Four Layers You Must Distinguish at a Glance
BuildKit may require explicit --build-arg for the same variables during docker build even when the daemon already proxies layer downloads.
Layer 1 — Host applications. Browsers, numerous IDEs, and command-line tools that honor HTTP_PROXY in your shell profile use whatever you configured in Clash: system proxy, TUN, or manual env vars. This layer can look perfect while Docker remains blind.
Layer 2 — Docker Desktop and the Docker Engine. The daemon that executes docker pull talks to registries over HTTPS.
It reads proxy settings that Docker Desktop exposes in its UI, and may merge them with /etc/docker/daemon.json inside the Linux VM depending on edition and version. Until this layer points at 127.0.0.1:your-clash-port (or the correct bridge address), layer pulls stay on the wrong path.
Layer 3 — WSL2 on Windows. When Docker Desktop uses the WSL2 backend, the engine executes in a dedicated distro or utility context. Networking still ultimately crosses the Windows host.
If Clash is only in system-proxy mode, WSL processes may not see it; if you fixed that with TUN or per-WSL proxy env, Docker Desktop might still need its separate proxy block. Conversely, fixing Docker’s proxy does not by itself fix a broken out of the box route—see the WSL2 article when all WSL traffic is dead.
Layer 4 — Containers at runtime and build time. A pulled image executes with its own network namespace. apt, npm, and pip inside the container only use a proxy if you pass HTTP_PROXY, HTTPS_PROXY, and NO_PROXY via docker run, Compose, or BuildKit build-args.
Before You Touch Docker: Know Clash’s Local Address Broken Down
Third-party firewalls or “lockdown” modes can allow the browser on the host while dropping traffic from the backend VM to the same port.
A detail worth adding:Open your Clash-compatible client and note the mixed port or separate HTTP and SOCKS ports. Docker’s proxy URL is typically http://127.0.0.1:7890 if that is where HTTP proxy listens—replace the port with yours.
If you use SOCKS only, a number of setups still expose an HTTP frontend; the Docker daemon historically prefers an HTTP proxy URL for HTTPS traffic through CONNECT. When in doubt, enable an HTTP listener in Clash for Docker-only use.
On Windows, if Docker Desktop talks to the host through a virtual switch, a number of users set the proxy to the host’s LAN IP instead of 127.0.0.1.
Said differently:Try 127.0.0.1 first from Docker’s perspective inside the VM; if it fails, Docker Desktop documentation for your version describes the correct host gateway address to reach Windows from the Linux backend—often still reachable as a special host name depending on the product generation. Treat vendor docs as authoritative when names like host.docker.internal appear.
Ensure nothing blocks loopback access from the Docker VM to that port.
Windows: Docker Desktop Proxy UI From the Ground Up
If it still hangs, confirm Clash actually allows the CONNECT method to registry hosts and that your rules send registry-1.docker.io and related CDN names to a working outbound, not a dead group.
Open Docker Desktop → Settings → Resources → Proxies (exact labels move slightly between versions). Enable manual proxy configuration and set Web Server (HTTP) and Secure Web Server (HTTPS) to your Clash HTTP URL, for example http://127.0.0.1:7890.
In practice that means:Add a sensible Bypass list for local and private ranges: localhost,127.0.0.1,::1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16 so internal registry mirrors or company hosts do not loop through Clash unnecessarily.
Apply and restart Docker Desktop when prompted. Then execute docker pull hello-world or a small public image. If it succeeds, layer 2 is largely solved for registry access.
Windows: daemon.json and JSON Merge Demystified
When both appear in config, verify JSON validity—Docker refuses to launch with a trailing comma or a typo, which looks like “Docker Desktop starting forever.” Roll back one change at a time.
A number of teams prefer daemon.json for proxy-related keys or registry mirrors. Docker Desktop on Windows surfaces engine configuration through the UI or a JSON editor in settings; avoid duplicating conflicting proxy definitions in two places.
Worth noting:If you add "registry-mirrors" for a regional mirror, remember that mirrors and proxies solve different problems: a mirror replaces the registry endpoint; a proxy routes TLS to the original endpoint through Clash.
Windows: WSL2 Backend Specifics at a Glance
Align versions and context with docker context ls. For pure WSL networking issues—no DNS, no out of the box route—use the dedicated WSL2 routing write-up first; proxy settings will not fix a black-holed route.
With the WSL2 backend selected under Docker Desktop’s settings, the engine executes where Microsoft and Docker integrated it—not in your out of the box Ubuntu distro’s network stack unless you use that integration explicitly.
That is why “I set http_proxy in ~/.bashrc inside Ubuntu” does not on its own fix Docker Desktop’s pulls: different context.
One more angle:After you set Docker Desktop proxies, if only docker from Windows PowerShell works but docker inside a WSL distro still misbehaves, check whether you invoke the Docker CLI through a context that points to the same engine.
macOS: Docker Desktop Proxy UI Broken Down
If you execute Clash in TUN mode with a virtual utun UI, the host stack may already intercept traffic; nevertheless, giving Docker Desktop an explicit HTTP proxy to the local Clash port remains the clearest way to align behavior with corporate or split rules inside Clash. Ambiguity shows up when a number of traffic bypasses TUN by design.
On macOS, the flow mirrors Windows: Docker Desktop → Settings → Resources → Proxies. Enter the same http://127.0.0.1:PORT pattern.
macOS applications often respect the system proxy, but the Docker Linux VM still needs these explicit fields. After applying, test with docker pull from Terminal.
Container Runtime: HTTP_PROXY and NO_PROXY From the Ground Up
Always pair proxies with NO_PROXY for internal service names, Kubernetes cluster IPs, and local registries to avoid sending East-West traffic through Clash by mistake.
A detail worth adding:Once pulls work, a container may still fail to download dependencies during docker run. Set environment variables for the process:
docker run -e HTTP_PROXY=http://host.docker.internal:7890 -e HTTPS_PROXY=http://host.docker.internal:7890 …
Replace the host name with what your Docker version documents for reaching the Mac or Windows host from inside a container—host.docker.internal is common on Desktop builds.
Said differently:Your Clash port must listen on an UI reachable from that path, or bind Clash’s mixed port to 0.0.0.0 cautiously with firewall awareness.
Build Time: Dockerfile and BuildKit Demystified
If only builds fail while pulls succeed, suspect build-time proxy gaps before you reopen Clash’s core logs.
docker build executes each RUN instruction in a temporary container. Exporting proxy vars in your shell on the host does not propagate into those build steps unless you pass --build-arg HTTP_PROXY=… or use BuildKit secrets and forwarding features appropriate to your Docker version.
Numerous teams add an ARG block at the top of the Dockerfile for HTTP_PROXY and pass values from CI or local scripts.
Docker Compose at a Glance
Under Compose v2, set proxy-related environment in the service section or use a central .env file referenced by docker compose.
In practice that means:Keep in mind that Compose also has a separate notion of build args versus runtime env; mirror what you learned in the BuildKit section so npm ci during image build and node at runtime both see consistent values when required.
Registry Mirrors Versus Proxies Broken Down
In a number of regions, registry mirrors raise throughput without touching Clash. In others, mirrors are unreliable and a Clash-proxied connection to the official registry is more steady.
You may combine both: mirror for delay, proxy for reachability. Document what your team standardizes; mixed setups confuse newcomers who cannot tell whether a failure is mirror auth, TLS interception, or proxy rules dropping CONNECT.
Verification Checklist From the Ground Up
Use small public images for tests to avoid confusing huge layer downloads with pure network failure. Log timestamps help distinguish DNS stalls from TCP hangs.
Work top-down: confirm Clash shows outbound traffic when you pull an image after configuring Docker Desktop. If nothing hits Clash, the daemon still bypasses the proxy.
Worth noting:If Clash shows drops or blocked rules, adjust rules for registry domains and CDNs. Inside a test container, compare curl -I https://example.com with and without proxy env to see whether the problem is container-only.
Policy. Proxying Docker traffic through personal tools may conflict with employer security or data-exfiltration policies. Use these techniques only where you are authorized.
When TUN Mode Helps the Host but Not Docker Demystified
Readers who rely on Clash TUN mode for system-wide capture ought to still configure Docker Desktop’s proxy if the engine bypasses the TUN path. Our Clash TUN mode guide explains how transparent routing interacts with apps; Docker’s VM is a special case that often needs explicit proxy fields even when Chrome “just works.”
Closing Thoughts at a Glance
When you want a maintained client and plain download entry without hunting scattered releases, launch from one place. → Download Clash for free and experience the difference.
Docker Desktop turns “my Mac or PC browser uses Clash” into “every layer of the stack does what I expect” only after you align the daemon, and often the container environment, with the same local listener.
Compared with chasing random registry failures, separating host proxy, engine settings, WSL2 behavior, and build-time variables restores predictability—and keeps your HTTP/HTTPS proxy story consistent across Windows and macOS.
Still stuck on WSL-wide connectivity? Read Clash and WSL2 on Windows 11: routing and MTU next. Go to the download page →