Fix Docker Pull Timeouts: Route Docker Desktop Through Clash on Windows and macOS
What This Guide Covers Key Takeaways
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, preconfigured routes, and MTU when the entire Linux side loses connectivity.
If you already work 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 as a rule looking at a layering problem—not a bad subscription. Docker Desktop on Windows and macOS works the Docker Engine in a Linux VM or slim backend.
That engine does not without you 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 it is possible to test each hop in order.
The key point: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 load Clash subscriptions and the Clash Verge Rev Windows and macOS tutorial for mixed-port and system-proxy fundamentals.
Four Layers You Must Distinguish in Brief
Layer 4 — Containers at runtime and build time. A pulled image works 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.
Layer 1 — Host applications. Browsers, countless 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 works 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 preconfigured route—see the WSL2 article when all WSL traffic is dead.
BuildKit may require explicit --build-arg for the same variables during docker build even when the daemon already proxies layer downloads.
Before You Touch Docker: Know Clash’s Local Address A Closer Look
Ensure nothing blocks loopback access from the Docker VM to that port.
Let me put it this way: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 fair few 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 fair few users set the proxy to the host’s LAN IP instead of 127.0.0.1.
Concretely: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.
Third-party firewalls or “lockdown” modes can allow the browser on the host while dropping traffic from the backend VM to the same port.
Windows: Docker Desktop Proxy UI What Matters
Apply and restart Docker Desktop when prompted. Then work docker pull hello-world or a small public image. If it succeeds, layer 2 is largely solved for registry access.
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.
Where this lands: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.
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.
Windows: daemon.json and JSON Merge Key Takeaways
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.
A fair few 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.
Do not skip this:When both appear in config, verify JSON validity—Docker refuses to spin up with a trailing comma or a typo, which looks like “Docker Desktop starting forever.” Roll back one change at a time.
Windows: WSL2 Backend Specifics in Brief
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.
With the WSL2 backend selected under Docker Desktop’s settings, the engine works where Microsoft and Docker integrated it—not in your preconfigured Ubuntu distro’s network stack unless you use that integration explicitly.
That is why “I set http_proxy in ~/.bashrc inside Ubuntu” does not without you fix Docker Desktop’s pulls: different context.
The key point:Align versions and context with docker context ls. For pure WSL networking issues—no DNS, no preconfigured route—use the dedicated WSL2 routing write-up first; proxy settings will not fix a black-holed route.
macOS: Docker Desktop Proxy UI A Closer Look
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.
On macOS, the flow mirrors Windows: Docker Desktop → Settings → Resources → Proxies. Enter the same http://127.0.0.1:PORT pattern.
If you work Clash in TUN mode with a virtual utun front end, 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 fair few traffic bypasses TUN by design.
Container Runtime: HTTP_PROXY and NO_PROXY What Matters
Your Clash port must listen on an front end reachable from that path, or bind Clash’s mixed port to 0.0.0.0 cautiously with firewall awareness.
Let me put it this way: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.
Concretely: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.
Build Time: Dockerfile and BuildKit Key Takeaways
Countless teams add an ARG block at the top of the Dockerfile for HTTP_PROXY and pass values from CI or local scripts.
docker build works 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.
If only builds fail while pulls succeed, suspect build-time proxy gaps before you reopen Clash’s core logs.
Docker Compose in Brief
Under Compose v2, set proxy-related environment in the service section or use a central .env file referenced by docker compose.
Where this lands: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 A Closer Look
In a fair few regions, registry mirrors lift how fast it is without touching Clash. In others, mirrors are unreliable and a Clash-proxied connection to the official registry is more trustworthy.
You may combine both: mirror for response time, 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 What Matters
Use small public images for tests to avoid confusing huge layer downloads with pure network failure.
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.
Do not skip this: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.
Log timestamps help distinguish DNS stalls from TCP hangs.
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 Key Takeaways
Readers who rely on Clash TUN mode for system-wide capture are best off 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 in Brief
When you want a maintained client and unambiguous download entry without hunting scattered releases, spin up from one place.
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 faults, 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.
The key point:→ Download Clash for free and experience the difference.
Still stuck on WSL-wide connectivity?
Read Clash and WSL2 on Windows 11: routing and MTU next. Go to the download page →