The symptom: pulls time out while “the internet works”

Ask any platform engineer for a frustrating Monday morning story and you will hear about docker pull hanging halfway through a layer download, containerd reporting i/o timeout, or a CI job that passes every health check except the one that needs a multi-gigabyte base image. Meanwhile, a browser tab loads news sites and a speed test looks fine. That mismatch is the classic signature of split routing problems: the traffic you care about is not taking the path you think it is, or it is taking a path that is correct for web browsing but wrong for long-lived, large-object HTTPS sessions to registry CDNs.

This article complements our walkthrough on split routing for AI web apps in Clash. That piece focused on chatty browser workloads; here we focus on developer proxy scenarios where the client is often a daemon, not a tab. The underlying Clash idea is the same: named policy groups, explicit domain rules, and rule order that runs before broad geography shortcuts catch the wrong IP ranges. If you need the big picture of YAML routing first, keep the YAML and Fake-IP guide open in another window—this article assumes you understand that the first matching rule wins.

Why a blanket “proxy everything” profile hurts Docker

Turning on system proxy or TUN and sending all TCP flows through a distant exit can accidentally optimize the wrong problem. Registry traffic is bulk HTTPS with large windows; domestic mirrors or office next-hop paths may be dramatically faster than hair-pinning everything through a congested international relay. At the same time, some regions reach Docker Hub or vendor registries more reliably through a stable offshore node than through a flaky default route. A single global knob cannot express “send my browser and IDE through group A, send registry blobs through group B, keep RFC1918 and the internal Harbor host on DIRECT.” That is exactly what split tunneling style profiles are for.

Another subtle issue is authentication fan-out. Pulling nginx:latest is rarely one TCP session to a single hostname. Modern registries use token endpoints, redirects, and CDNs that may live on different DNS names than the human-readable registry prefix you typed. If your rules only match a pretty hostname while the heavy download jumps to a CDN label classified by GEOIP as onshore, you can end up with a fast manifest fetch and a stalled layer fetch—or the reverse—because Clash evaluated two connections under different policies.

How pulls work at a high level (and what to match)

Most readers think in terms of docker.io or ghcr.io, but the engine resolves names, negotiates TLS, and may follow HTTP redirects before it ever sees a layer blob. For Docker Hub–style flows, you should expect traffic to names such as registry-1.docker.io, auth.docker.io, and various CDN hosts that can change over time. Google Artifact Registry, Amazon ECR, Microsoft Container Registry, Quay, and GitHub Container Registry each have their own DNS patterns; private Harbor or Nexus endpoints may sit on your LAN. The routing implication is simple to state and fiddly to maintain: your registry rules must cover the whole chain, not just the pretty prefix in your Dockerfile.

On Kubernetes nodes, containerd or cri-o performs the same work under the hood; kubelet does not magically simplify DNS for you. If you are tuning a workstation and a cluster, keep separate mental buckets for “human browsing” and “node pulls,” but reuse the same Clash discipline: explicit groups, ordered rules, and logs that show which policy matched which connection.

Design a registry policy group you can reason about

Start by naming a dedicated outbound bucket. Call it REGISTRY, DOCKER, or whatever you will recognize at two in the morning. Populate it with a select or url-test group containing nodes that tolerate long downloads and high throughput. Keep the list short; registry pulls fail in noisy ways when you flip exits mid-stream. Parallel to that, preserve a general-purpose international group for browsing, a domestic DIRECT path for known fast mirrors, and an explicit DIRECT (or corporate) path for RFC1918 registry hosts inside your VPN.

The goal is not to “maximize anonymity” for CI blobs; it is to minimize surprise. Operations teams often want different nodes for personal laptops versus build agents. That is fine—use two profiles or two tagged groups—but avoid reusing the same ambiguous label for both “watching video” and “pulling five-hundred megabyte layers,” because congestion profiles differ. If you also maintain streaming splits, keep them separate from registry knobs; see the streaming split guide for why mixed buckets become hard to debug.

Rules: build a funnel with registry rows above GEOIP

Your rule section should read like a funnel, not a junk drawer. Place suffix or keyword rules for registry-related domains above catch-all GEOIP lines that might send traffic to DIRECT based on resolved IP. Many community profiles include aggressive GEOIP,CN,DIRECT shortcuts for latency; those are helpful until they swallow a CDN edge that your registry session depends on. The fix is not “delete GEOIP,” it is precedence: pin registry names to REGISTRY (or to DIRECT when you intentionally use a domestic mirror) before the geography heuristic runs.

When you maintain remote RULE-SET providers, set sane refresh intervals and watch update logs. A silent failure there means you are still running yesterday’s internet. For first-party short lists, prefer tight DOMAIN-SUFFIX entries for stable organizational boundaries and document any one-off host you add after a vendor incident. Illustrative YAML belongs in tutorials; the portable idea across Clash Meta and mihomo cores is unchanged: reference your registry group in the rows that matter, keep custom lines above inherited blocks, and leave yourself comments that survive the next profile merge.

Domestic mirrors versus offshore exits

Readers in regions with well-run mirror ecosystems often get the fastest pulls by staying DIRECT to those mirrors, especially for popular base images that are already cached locally at the edge. In that scenario, forcing the same traffic through an international node can reduce throughput for no security benefit. Conversely, if your mirror is flaky or incomplete, a measured path through REGISTRY to upstream Docker Hub may be more reliable than retry storms against a partial cache. The Clash answer is not dogma; it is two explicit policies you can switch between without rewriting your entire profile.

Be careful with mirror configuration inside Docker itself: when you point registry-mirrors at a third party, you are trusting that intermediary’s availability and tamper controls. Network policy belongs in Clash; supply-chain policy belongs in image signing and organizational governance. This article stays on the network side—routing mechanics, not registry security audits.

Docker daemon paths: HTTP proxy environment, TUN, and the desktop split brain

On Linux, the Docker daemon is typically a systemd service that does not inherit your shell’s HTTP_PROXY variables unless you configure them explicitly in the unit drop-in or /etc/docker/daemon.json companion settings. Clash TUN mode can intercept the daemon’s outbound connections if routing is set up so those packets hit the tun interface—but misaligned DNS or bypass lists can still leak a lookup outside the tunnel. On macOS and Windows with Docker Desktop, there is an additional translation layer: the Linux VM that runs the engine may see different networking than your host browser, especially when VPN-like clients compete for routes.

Practical debugging sequence: confirm whether the engine’s connection shows up in your Clash connection panel, which policy matched, and whether the destination SNI lines up with your rules. If the panel never sees the flow, you are not fighting precedence; you are fighting who owns the route table. Resolve that before you swap nodes. Our Windows TUN troubleshooting guide walks through overlapping VPNs, firewall blocks, and DNS conflicts that can make “everything proxied except Docker”—or the opposite—feel mysterious.

Kubernetes nodes and CI runners

For cluster workers, treat image pulls as part of node readiness. If you rely on a cluster-wide HTTP proxy for egress, ensure that proxy configuration matches what containerd expects, and that your Clash rules on adjacent management hosts do not fight kubelet’s assumptions. In CI systems such as GitHub Actions self-hosted runners, the same split principles apply: isolate registry domains to the policy that matches your compliance story, keep internal artifact hosts on fast DIRECT paths, and avoid global rules that send large blob traffic through choke points meant for interactive SSH.

When builds run in ephemeral VMs, remember that parallel jobs multiply concurrency. A url-test group that looks fine for a single laptop browser may thrash when twenty pulls start at once. Prefer stable relays for CI, and cap parallelism where your operator guide says to—Clash cannot fix upstream rate limits.

DNS alignment with Fake-IP setups

Articles about Fake-IP explain why local DNS interception exists; see the documentation hub if you need diagrams. For registry traffic, the key is coherence: the name your rules evaluate should be the same name your TLS stack uses. In Fake-IP mode, suffix rules usually behave well—until something in fake-ip-filter excludes a critical name or a secondary resolver bypasses the client. When debugging, temporarily simplify to one trusted upstream, verify the registry split end-to-end on a single image, then reintroduce complexity with comments that justify each exception.

Verification beyond a speed test

Throughput to a nearby speed-test server does not predict multi-connection registry performance. Use your GUI’s connection list to confirm that manifest and blob hosts map to REGISTRY (or your chosen DIRECT mirror path), not an unexpected DIRECT hop caused by a higher GEOIP line. Raise logging during an incident, reproduce once, capture which rule matched, then return log verbosity to normal. If you change mirrors or vendors, re-run the check: CDNs shift, and the first casualty is always the “we did not update the suffix list” assumption.

Quick mapping: what you see versus what to inspect

What you see Inspect first
Manifest fetch succeeds; layer download stalls Whether CDN/blob hostnames share the same outbound; stray GEOIP lines above them
Works on host browser; fails inside Docker Desktop VM Host vs VM routing, Desktop proxy settings, competing VPN routes
Flaky only on corporate Wi-Fi Captive portals, split-horizon DNS, HTTP proxy auto-config interfering with daemon
Breaks after profile update Rule provider fetch errors; renamed groups; new GEOIP ordering

A brief note on responsibility

Proxy tools are neutral; registry terms, export controls, and local regulations are not. This guide explains routing mechanics for engineers who legitimately manage cross-border development environments. It does not encourage violating provider acceptable-use policies, bypassing authentication, or misrepresenting residency where contracts forbid it. Treat compliance as a first-class requirement.

Closing: make registry routing intentional

Docker and Kubernetes are not going to shrink their dependency on remote registries, and AI-assisted development only increases the number of bespoke base images teams pull every week. A disciplined Clash configuration gives container registry traffic a clear policy home, keeps split routing rules ahead of blunt geography shortcuts, and pairs DNS with TCP so layers do not stall for mysterious reasons. Compared with opaque clients that hide the routing graph, Clash-family cores reward you with logs you can read when the next CDN migration lands.

When you want that stack on a maintained desktop build with a readable connection view, start from our download page so the core, GUI, and updater stay aligned. Understanding YAML still matters, but you should not fight packaging just to test a REGISTRY group. → Download Clash for free and experience the difference.