Why Docker Hub Pulls Behave Differently Behind Clash
A Docker image pull is not a single request to one website. When you run docker pull nginx:latest, the Docker client asks the Docker daemon to resolve the image name, authenticate against a registry service, obtain a manifest, select platform-specific layers, and download several large blobs. The daemon may run as a background service under root, while your terminal runs as an ordinary user. That separation explains why a browser can open Docker Hub normally while Docker reports i/o timeout, context deadline exceeded, TLS handshake timeout, or net/http: request canceled.
The first hostname is commonly registry-1.docker.io, but a successful pull can also involve auth.docker.io, hub.docker.com, and a content delivery hostname returned by the registry. Layer downloads may be redirected to a CDN whose domain is not obvious from the command you typed. A rule that only sends docker.io through Clash can therefore route authentication correctly and still leave the large layer transfer on a failing direct path.
There is another important distinction between a normal system proxy and a transparent proxy. A system proxy changes settings consumed by browsers and proxy-aware applications. Docker Engine does not automatically inherit every desktop proxy setting. A transparent setup instead captures traffic at the network layer, allowing the daemon to reach Clash even when it has no explicit HTTP proxy configuration. On Linux, that usually means Mihomo TUN mode, a virtual interface, and suitable DNS and auto-route settings. On desktop clients, the equivalent may be a service-mode TUN implementation supplied by Clash Verge Rev, Mihomo Party, or another Mihomo-based client.
Keep the layers separate: Docker daemon proxy variables, Clash system-proxy mode, and Mihomo TUN capture are three different mechanisms. You can use one or combine them, but enabling a desktop HTTP proxy does not prove that a root-owned Docker service is using it.
Before changing rules, record the exact failure. Note the image name, whether the failure happens during authentication or layer download, the host operating system, the Docker Engine version, and whether the daemon is local, remote, or inside a virtual machine. If the same Docker command works on a different network, that is useful evidence, but it does not identify whether DNS, TCP routing, TLS, or CDN classification is responsible.
Prepare Mihomo for Docker-Aware Transparent Routing
Use a current Mihomo-compatible client and confirm that the selected profile actually runs the Mihomo core. The labels differ between clients, but the relevant concepts are consistent: a mixed listener or SOCKS listener for explicit tests, TUN mode for transparent capture, a controlled DNS engine, and rules that can classify registry and CDN traffic. A profile imported from an older Clash installation may still contain valid proxy groups while lacking the TUN or DNS fields required for dependable daemon traffic.
Start with a small, understandable profile rather than adding a large community rule pack immediately. The following fragment illustrates the important shape; adapt the proxy group names and DNS server addresses to your own environment.
mixed-port: 7890
allow-lan: false
mode: rule
log-level: info
tun:
enable: true
stack: mixed
auto-route: true
auto-detect-interface: true
strict-route: true
dns:
enable: true
listen: 0.0.0.0:1053
ipv6: false
enhanced-mode: fake-ip
fake-ip-range: 198.18.0.1/16
nameserver:
- https://1.1.1.1/dns-query
- https://8.8.8.8/dns-query
fallback:
- tls://1.1.1.1
- tls://8.8.8.8
rules:
- DOMAIN-SUFFIX,docker.io,DOCKER
- DOMAIN-SUFFIX,docker.com,DOCKER
- DOMAIN,registry-1.docker.io,DOCKER
- DOMAIN,auth.docker.io,DOCKER
- DOMAIN-SUFFIX,amazonaws.com,DOCKER
- MATCH,DIRECT
The example is intentionally conservative. strict-route helps prevent packets from escaping through an unexpected interface, while auto-detect-interface avoids hard-coding an interface name that changes between Wi-Fi, Ethernet, and a virtual machine. The mixed-port is useful for command-line diagnostics, but it is not what makes the Docker daemon transparent. TUN capture and route installation perform that job.
Do not blindly proxy every hostname ending in amazonaws.com in a production network. Docker Hub layer redirects can use cloud infrastructure, but many unrelated services also use the same suffix. A broad suffix rule may increase latency, send internal workloads to an unsuitable node, or make troubleshooting harder. Begin with the exact redirected hostname shown in Mihomo’s connection log, then decide whether a narrow domain rule or a provider-maintained CDN rule is safer.
Create a dedicated DOCKER proxy group instead of pointing registry traffic directly at a volatile auto-test group. Docker layers are large and pulls can last several minutes. A group that changes its node during a download can terminate active TCP sessions and make a stable registry look unreliable. A practical group can contain a preferred node, a fallback node, and a URL-test option used only before a pull begins. During diagnosis, selecting one known-good node manually is often more informative than testing ten nodes at once.
Choose stability over headline latency. Docker Hub pulls stress sustained throughput, TLS session stability, and CDN compatibility. A node with a slightly higher ping but clean five-minute downloads is usually better than a fast health-check result that fails when a 300 MB layer starts.
Configure TUN Capture and DNS Without Creating a Loop
Enable TUN only after understanding which process owns the traffic. On a Linux host, Docker Engine normally opens outbound sockets from the host network namespace, although containers may use bridge or overlay namespaces depending on the deployment. On Windows and macOS, Docker Desktop places the daemon inside a lightweight virtual machine. The host client’s TUN interface may capture desktop traffic without automatically capturing every packet generated inside that VM. This is why the same configuration can work with native Docker Engine on Linux and fail with Docker Desktop.
For native Linux Docker, launch the Mihomo client with the permissions required to create a TUN device and install routes. The exact service unit differs by distribution, but the operational checks are similar:
- Confirm the TUN interface exists: use
ip linkand look for the interface created by Mihomo, commonly namedMihomoorutundepending on the platform and client. - Check the route table: run
ip routeand verify that TUN has become the intended path without replacing the route needed to reach the Clash control endpoint. - Open the Mihomo connection view: keep it visible while reproducing a pull so you can see whether registry, authentication, and redirected CDN connections are captured.
- Test one image: avoid simultaneous compose builds, parallel pulls, and automatic image updates until the single-image path is understood.
DNS is often the hidden cause of a Docker Hub timeout. With fake-IP mode, Mihomo returns synthetic addresses and maps them back to domain names when the connection is captured. This is valuable for domain rules, but only when the application’s DNS request and subsequent connection remain visible to Mihomo. If the Docker daemon uses an external resolver that returns real addresses outside the expected DNS path, a domain rule may not match as you assume. Conversely, if a container receives a fake address but its traffic bypasses the TUN interface, the connection can fail because the synthetic address has no meaning on the direct path.
Inspect the resolver configuration from both the host and a temporary container. Commands such as resolvectl status, cat /etc/resolv.conf, and docker run --rm busybox nslookup registry-1.docker.io can reveal that the host and container are using different DNS servers. Docker’s embedded resolver commonly listens on an internal address inside containers and forwards queries according to the daemon configuration. That is normal, but it means a host-only DNS test does not fully validate container resolution.
Prevent a DNS loop. Do not point Mihomo’s upstream DNS at a listener that forwards back into Mihomo, and do not make Docker use a resolver address that is reachable only through the very TUN route being established. Keep one clear chain: Docker or the host sends a query to its configured resolver, Mihomo resolves it through its selected upstreams, and the resulting connection follows the same routing policy as the query. If you use fake-IP, exclude local infrastructure, the Mihomo control panel, and any required Docker bridge ranges according to your client’s supported syntax.
Do not proxy the proxy itself. If Mihomo’s API, DNS listener, or upstream transport is routed through its own TUN capture without an explicit bypass, startup can become unstable or produce a recursive connection loop. Keep control-plane addresses and local gateway ranges in a deliberate direct or bypass policy.
Choose Between Transparent Capture and Daemon Proxy Variables
Transparent routing is convenient because it covers applications that ignore proxy environment variables. However, Docker provides an explicit daemon proxy mechanism that can be easier to audit on a Linux server. These approaches solve different operational problems. A daemon HTTP proxy tells Docker where to send registry requests. Mihomo TUN captures packets regardless of application proxy support. Running both may be valid, but it can also create double-proxying, confusing logs, or a proxy endpoint that is reachable only from the host and not from Docker Desktop’s VM.
For a Linux systemd-managed Docker Engine, an explicit proxy drop-in commonly resembles the following:
sudo mkdir -p /etc/systemd/system/docker.service.d
sudo tee /etc/systemd/system/docker.service.d/proxy.conf <<'EOF'
[Service]
Environment="HTTP_PROXY=http://127.0.0.1:7890"
Environment="HTTPS_PROXY=http://127.0.0.1:7890"
Environment="NO_PROXY=localhost,127.0.0.1,::1,.local,registry.local"
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
sudo systemctl show --property=Environment docker
Use this method only if the Docker service can reach the listener at that address. On a native Linux host, 127.0.0.1:7890 may be correct. In a containerized Docker daemon, Docker Desktop VM, or rootless arrangement, that loopback address can refer to a different network namespace. In those cases, bind the listener to an intentionally reachable address, apply access controls, and avoid exposing an unauthenticated proxy to the LAN.
After changing a daemon proxy, remember that the daemon may reuse cached layers. A successful pull does not necessarily prove that every registry hostname is routed correctly, because no network request is made for an already-present layer. Test with an uncached tag, remove only the relevant test image, or use a temporary image name. Do not delete production images merely to create a test; inspect the connection log and daemon journal first.
NO_PROXY deserves careful attention. Internal registries, private Git servers, service discovery names, and local package mirrors should normally remain direct. A suffix such as .local may be appropriate for mDNS-style names but does not cover every private DNS zone. Conversely, putting docker.io into NO_PROXY defeats the intended route. Keep public Docker Hub domains out of the bypass list unless you have a specific, tested reason.
Build Selective Rules for Registry, Auth, and CDN Redirects
Use the live connection log as the source of truth. Start a pull and identify every connection created during the operation. You will usually see a registry request, an authentication request, and one or more layer-download destinations. The rule column should show the intended proxy group for all public Docker Hub traffic. If the log shows DIRECT for a redirected CDN while registry-1.docker.io uses DOCKER, the problem is not the node selection; it is incomplete classification.
Rule order matters. Mihomo evaluates rules from top to bottom and stops at the first match. A broad GEOIP rule, a provider rule-set loaded before your custom rules, or an early MATCH,DIRECT line can win before Docker-specific entries are evaluated. Place your deliberate Docker rules above broad regional or catch-all rules. Then check that the profile’s rule providers are actually loaded and not failing due to their own network path.
A useful policy separates public registry traffic from private infrastructure:
- Send
registry-1.docker.ioandauth.docker.iothrough the dedicated Docker group. - Proxy the exact CDN host returned during a failing pull, rather than assuming every cloud hostname is part of Docker Hub.
- Keep private registry domains such as
registry.corp.exampleon a direct or internal route. - Bypass Docker bridge, overlay, loopback, and management subnets when your TUN client requires explicit local-network exclusions.
- Keep the final
MATCHrule intentional; useDIRECTfor a split policy or the Docker group for a narrowly scoped test profile.
Do not mix rule providers that disagree about fake-IP handling without testing. One provider may classify a domain by its original name, while another expects a real destination address. A profile can appear syntactically valid and still route the same connection differently after a provider update. Export the active configuration, record provider versions, and make one rule change at a time so that a successful or failed pull remains attributable.
Diagnose Timeouts in a Repeatable Order
When a pull fails, begin with the daemon rather than the browser. On Linux, inspect journalctl -u docker; with Docker Desktop, open the diagnostic or daemon logs supplied by the application. The error wording helps narrow the layer: DNS failures mention name resolution, TCP failures often mention connection refusal or timeout, and TLS failures point toward handshake, certificate, or interception problems. A generic timeout is not enough to justify changing five proxy settings at once.
- Validate DNS: resolve
registry-1.docker.iofrom the host and from a temporary container, then compare the resolver and returned address behaviour. - Validate the Clash listener: use
curl -v -x http://127.0.0.1:7890 https://registry-1.docker.io/v2/and confirm that an expected authentication response arrives through the explicit proxy. - Validate transparent capture: repeat the pull with the terminal proxy disabled and watch Mihomo’s live connections for the daemon’s registry sockets.
- Validate redirects: look for new CDN destinations when the first layer begins downloading; add a narrow rule only if the destination bypasses the intended group.
- Validate sustained transfer: pull a medium-sized uncached image and watch for node switching, throughput collapse, TCP resets, or a timeout at a consistent byte count.
- Validate MTU and fragmentation: if handshakes work but large transfers stall, test a lower TUN MTU and check whether the path includes a VPN, PPPoE link, or nested virtual network.
Use curl results carefully. A successful request to /v2/ confirms reachability and often returns a 401 response by design; it does not confirm that the daemon can download every redirected blob. Likewise, docker login may succeed while a pull fails later because login tests the authentication lane but not the CDN lane.
MTU problems are especially easy to misread. Small DNS and TLS packets can pass while larger encrypted segments are fragmented or dropped. Docker then appears to hang randomly, although the failure occurs at a repeatable packet size. Reduce the TUN MTU gradually, restart the affected client, and retest. Do not permanently lower it without measuring the trade-off, because an unnecessarily small MTU reduces efficiency for every captured application.
Finally, test without aggressive failover. Select one stable proxy node, disable automatic switching temporarily, and pull the same image twice. If the first attempt succeeds and the second fails only after the group changes nodes, the routing rules may be correct and the problem may be node quality or session interruption. If both attempts fail at the same hostname, continue investigating DNS, capture, or CDN classification instead of repeatedly rotating nodes.
Production Hardening for Docker Hosts and Build Pipelines
Once the basic pull works, make the configuration predictable. Pin a known-good Mihomo profile version, document the selected core, and keep a backup of the active YAML before editing rules. Treat subscription URLs and daemon proxy credentials as secrets. They may grant access to private infrastructure or reveal provider account information, so do not paste them into public issue reports or include them in image layers.
Separate interactive development from automated builds. A developer laptop can use a manually selected Docker proxy group, while a CI runner should use a stable egress policy with health checks that resemble real registry downloads. Configure timeouts for the environment rather than masking every failure with a huge value. A 30-minute timeout can hide a broken route and leave build workers occupied; a reasonable timeout combined with retry logic gives the pipeline a chance to recover without concealing systemic errors.
For private registries, prefer a direct internal route or a trusted enterprise proxy. Public Docker Hub rules should not accidentally capture internal domains merely because both use registry-like names. If your organization mirrors common images, configure Docker to use the mirror and route only the mirror’s hostname through the appropriate path. This reduces external dependency, makes audit logs clearer, and avoids repeated downloads across build agents.
Monitor three separate signals: Docker daemon errors, Mihomo connection decisions, and actual transfer throughput. A green client dashboard does not mean the daemon is healthy, and a successful health check does not measure a multi-layer pull. Keep a small diagnostic image or known test tag for scheduled checks, but avoid pulling large images unnecessarily. Record the timestamp, selected group, rule hit, resolved hostname, and failure phase when an incident occurs; these details make later profile changes much safer.
Production rule of thumb: keep the Docker route narrow, stable, and observable. Transparent mode should remove application-specific proxy guessing, not remove the need for clear ownership of DNS, routes, and daemon boundaries.
Compared with generic desktop proxy tools, many Docker-specific workarounds are either too narrow—setting a browser proxy and hoping the daemon follows—or too broad, forcing every cloud destination through one unstable route with little visibility into redirects. Clash Official Site is more useful in this scenario because its Mihomo-oriented guidance connects the daemon, TUN capture, DNS behaviour, rule order, and live connection logs into one testable workflow instead of treating “Docker Hub timeout” as a single switch to flip. If you want a client and configuration environment where these checks are easier to apply and maintain, you can download Clash Official Site and use the steps above as your starting checklist.