Why Docker Hub pulls need a different Clash strategy

Docker image pulls often fail in a way that looks random: a small image downloads normally, a larger layer stalls at 30%, authentication opens in a browser but the Docker Engine reports a timeout, or docker pull works on a laptop while the same command fails on a development server. On a restricted network, these symptoms are usually caused by a routing mismatch between the application that launched Docker, the Docker daemon, DNS resolution, and the network path selected for Docker Hub.

This guide explains how to use Clash TUN mode as a transparent proxy layer for Docker-related traffic. The goal is not to paste one universal YAML file and hope that every environment behaves identically. Instead, you will build a predictable policy: Docker Hub and its authentication endpoints use a proxy group, local registries remain DIRECT, DNS answers follow the same routing design, and the Docker daemon is tested independently from your interactive shell.

The examples are suitable for a Linux development host running Mihomo or a Clash-compatible client, with Docker Engine installed locally. The exact menu names differ between Clash Verge Rev, Mihomo Party, OpenClash, and other clients, but the concepts remain the same. You need administrative access to the host, a legitimate Clash-compatible profile, and permission to change local firewall, DNS, or daemon settings. Do not publish subscription URLs, access tokens, private registry credentials, or complete configuration files containing secrets.

Understand the Docker traffic path before changing YAML

Docker does not behave like an ordinary browser. A browser normally sends requests through the desktop system proxy when that proxy is enabled. Docker has several separate traffic paths, and each one may make its own connection:

  • Docker CLI: the command-line client contacts the Docker daemon through a local Unix socket or a TCP endpoint. The CLI process itself is not necessarily the process downloading image layers.
  • Docker daemon: dockerd resolves registry names, authenticates against registry services, downloads manifests, and retrieves image layers. Proxy variables configured only in your shell may never reach it.
  • Container processes: applications running inside containers have their own network namespace and DNS configuration. A successful host-side image pull does not prove that an application inside a container can reach its upstream API.
  • BuildKit: modern builds may use BuildKit workers, which can create separate sessions for fetching base images, Git sources, package indexes, and build secrets.

Clash TUN can capture traffic at the host network layer, which is useful when a daemon ignores system proxy variables. However, TUN mode does not eliminate the need for correct DNS behavior, policy rules, or daemon-specific configuration. It also does not automatically make a proxy reachable from every container network. Think of TUN as a transparent interception mechanism, not as a substitute for understanding the path.

A useful first test is to separate three questions. Can the host resolve registry-1.docker.io? Can the host establish HTTPS to it? Can the Docker daemon complete an authenticated layer download? Each answer may be different. Testing only curl https://registry-1.docker.io/v2/ is helpful, but it is not a complete Docker test because authentication and layer hosts may be involved later.

Build a safe TUN and YAML policy design

Start from the profile that already contains your proxy providers and proxy groups. Avoid replacing the entire provider section with a short example from a forum. Provider formats, health checks, DNS settings, and rule providers can be version-sensitive. Add or adjust the TUN, DNS, and rule sections while preserving the parts that your client requires.

A typical Mihomo-oriented foundation looks like this:

tun:
  enable: true
  stack: mixed
  auto-route: true
  auto-detect-interface: true
  strict-route: true

dns:
  enable: true
  enhanced-mode: fake-ip
  fake-ip-range: 198.18.0.1/16
  nameserver:
    - https://1.1.1.1/dns-query
    - https://dns.google/dns-query
  fallback:
    - tls://1.1.1.1
    - tls://8.8.8.8
  fallback-filter:
    geoip: true
    geoip-code: CN

proxy-groups:
  - name: DOCKER
    type: select
    proxies:
      - AUTO
      - DIRECT

rules:
  - DOMAIN-SUFFIX,docker.io,DOCKER
  - DOMAIN-SUFFIX,docker.com,DOCKER
  - DOMAIN,registry-1.docker.io,DOCKER
  - DOMAIN,auth.docker.io,DOCKER
  - DOMAIN-SUFFIX,local,DIRECT
  - IP-CIDR,127.0.0.0/8,DIRECT,no-resolve
  - MATCH,DIRECT

This is a structural example rather than a drop-in production profile. Some clients use mixed, system, or gvisor stacks differently; some require an explicit TUN device name; and some profiles already define a rule provider with a final catch-all policy. Confirm the keys supported by the Mihomo version bundled with your client before enabling strict routing.

The most important detail is policy ordering. A broad rule such as GEOIP,CN,DIRECT placed above Docker-specific domain rules can send a Docker endpoint directly if its address is classified as local or if the result is affected by fake-IP handling. Put explicit Docker rules before broad geographic, regional, or final-match rules. If your provider has a dedicated streaming or international group, do not assume it is suitable for Docker. Long image downloads need stable TCP behavior, consistent idle timeouts, and enough bandwidth, not merely a node that opens a web page quickly.

Which Docker domains should be considered

Docker Hub does not use one hostname for every operation. The exact host list can change, so the Connections panel during a real pull is more reliable than a static list. Commonly observed names include:

  • registry-1.docker.io for registry API requests and manifests.
  • auth.docker.io for authentication tokens.
  • index.docker.io in compatibility or older client flows.
  • Content delivery hostnames used for image layers, which may not end in docker.io.
  • GitHub, Quay, GHCR, or vendor-specific registry domains when an image is mirrored or hosted outside Docker Hub.

Do not create a rule that sends every unknown CDN domain through the same group without observing it first. A Docker Hub pull may reach a CDN whose hostname belongs to a broader cloud provider. Use Mihomo logs to identify the destination and then decide whether a narrow DOMAIN, DOMAIN-SUFFIX, or provider rule is appropriate. If the content host changes frequently, a carefully scoped registry policy or a trusted rule provider may be more maintainable than chasing individual CDN names.

Hands-on setup: enable TUN and verify a Docker pull

First, stop competing VPN or transparent proxy services on the host. Two services attempting to install routes, redirect DNS, or manage nftables can create intermittent behavior that looks like a bad proxy node. Record the current default route and DNS configuration so that you can restore them if necessary. On Linux, commands such as ip route, resolvectl status, and ss -lntup can show which interface and resolver are active.

  1. Import and validate the profile. Load the profile in your Clash client and use its built-in parser or configuration check. Confirm that the selected proxy group contains a working node and that the profile does not have duplicate TUN, DNS, or rule sections.
  2. Enable TUN with elevated permission. The client may need permission to create a TUN device, alter routes, or install firewall rules. Approve only the requested system access and confirm that the client reports the TUN interface as running.
  3. Choose the Docker policy group. Select a stable proxy manually for the first test instead of relying on automatic selection. Automatic health checks may measure a short URL and fail to detect poor performance on long-lived downloads.
  4. Watch live connections. Clear or filter the Mihomo Connections view by docker, registry, or the domain shown in the error. Start the pull while watching whether each connection is assigned to DOCKER rather than DIRECT.
  5. Test a small public image. Run docker pull hello-world or another small, trusted image. Then test a larger image with multiple layers. A small success confirms basic routing; it does not prove that long downloads, authentication, and CDN endpoints are healthy.
  6. Repeat after restarting Docker. Restart the Docker daemon and repeat the test. This catches cases where the first request was made by a shell tool while the daemon later used a different DNS path or inherited stale proxy settings.

During the test, record the selected rule, proxy group, resolved address, connection duration, and failure stage. “Timeout” is not precise enough for diagnosis. A timeout during token retrieval points toward authentication routing or DNS, while a timeout after the manifest succeeds often points toward a layer CDN, MTU issue, or unstable node.

If TUN captures the host request but Docker still fails, inspect whether Docker is using a custom daemon address, a rootless service, or a separate network namespace. Rootless Docker can run under a user service with a different environment and route configuration. Likewise, a remote Docker context means the command is talking to another daemon; changing Clash on your laptop will not change the network path used by that remote host.

Split DNS, daemon proxy settings, and container boundaries

DNS is often the hidden reason a transparent proxy appears inconsistent. With fake-IP mode, Clash returns synthetic addresses and associates them with hostnames internally. Some applications accept that model cleanly; others cache addresses, perform their own resolution, or compare the returned address with TLS expectations. If Docker logs show an address in the fake-IP range but the request is not matched correctly, review the client’s fake-IP filter and DNS hijack settings before changing every rule.

A split DNS design should answer three practical questions: which resolver handles local domains, which resolver handles proxy-bound domains, and whether the client can preserve the original hostname when routing through TUN. Local corporate registries, internal service discovery names, and VPN-only zones should normally use an internal resolver and remain outside public fallback DNS. Docker Hub domains can use the resolver path associated with the proxy policy, but avoid sending private registry names to public resolvers.

Some environments work more reliably when Docker’s daemon proxy is configured as an additional layer, especially when TUN is unavailable or when only registry traffic should be proxied. A systemd drop-in may look conceptually like this:

[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,registry.internal.example"

Use the actual Clash mixed or HTTP proxy port exposed by your client. Do not copy a port from another installation without checking the client’s current settings. After changing a systemd drop-in, reload the unit and restart Docker, then verify the daemon environment using the service manager rather than assuming that your shell’s env output applies to dockerd.

Do not blindly set NO_PROXY=*.docker.io. That would bypass the very traffic you intend to route, and wildcard syntax is interpreted differently by different clients. Keep private registries, loopback endpoints, and internal domains in NO_PROXY; leave Docker Hub domains governed by the Clash rule set unless your architecture specifically requires direct access.

Container traffic is a separate decision. If a pulled container must call an external API, configure its DNS and egress policy deliberately. TUN on the host may capture ordinary container traffic when routes and firewall rules permit it, but Docker bridge networks, IPv6, and custom interfaces can change the result. Test from inside a temporary diagnostic container and compare its route, DNS response, and HTTPS behavior with the host.

Troubleshoot timeouts, authentication failures, and partial downloads

Use the failure stage to choose the next experiment instead of changing five settings at once.

  • DNS failure: errors such as “no such host” or immediate resolver timeouts indicate a DNS path problem. Compare the result from the host, the Docker daemon, and a temporary container. Check fake-IP exclusions and whether systemd-resolved or Docker’s embedded DNS is bypassing Clash.
  • Authentication timeout: if the registry endpoint responds but token acquisition fails, inspect auth.docker.io and related login endpoints. A rule covering only registry-1.docker.io is incomplete.
  • Manifest succeeds, layers stall: watch for a new CDN hostname after the manifest request. Add a narrow rule only after confirming the destination. Also test another proxy node because large transfers expose packet loss and idle timeout problems.
  • TLS or certificate errors: confirm that you are not enabling an untrusted MITM certificate option. Docker’s trust store, the host trust store, and a browser trust store may differ. Do not disable certificate verification as a shortcut.
  • Only IPv6 fails: temporarily compare IPv4 and IPv6 behavior. An incomplete IPv6 route can bypass the intended TUN path or produce long connection delays. Fix the route or apply a documented address-family policy rather than repeatedly changing proxy groups.
  • HTTP 429 or 403: this is usually an account, rate-limit, repository permission, or policy response rather than a transparent proxy failure. Read the response and Docker Hub account status before modifying routing.

MTU problems deserve special attention on TUN setups. Encapsulation, an upstream VPN, and a physical interface with a lower path MTU can cause large TLS records or long transfers to fail while small requests pass. If a pull repeatedly stalls at similar points, compare behavior with a smaller MTU in a controlled test and inspect packet counters. Do not permanently lower MTU without measuring the path, because an unnecessarily small value reduces performance.

Also check clock accuracy. Token authentication and TLS validation can fail when the host clock is significantly wrong. Verify NTP status, especially on VMs, NAS devices, and machines that resume from sleep. Finally, make sure the selected proxy has enough concurrent connection capacity. Docker may download several layers simultaneously, so a node that handles one curl request can still collapse under parallel pulls.

Production practices for repeatable Docker proxying

For a development laptop, manually selecting a node and watching the Connections panel may be enough. A build server or production host needs stronger controls. Pin the configuration version, document which domains are proxied, and keep a rollback copy of the previous profile. Treat a subscription update as a change that can alter groups, DNS behavior, rule order, and even the available proxy ports.

Separate image acquisition from application runtime when possible. A controlled build host can pull images through the proxy, scan them, and publish approved artifacts to an internal registry. Production nodes then pull from that internal registry over a private route instead of depending on an external Docker Hub connection for every deployment. This reduces exposure to CDN changes and makes outages easier to contain.

Use health checks that resemble the real workload. A quick ICMP test or a tiny web request does not measure authenticated registry access or a 500 MB layer download. Monitor pull duration, failed layer count, DNS latency, and daemon logs. Keep credentials in Docker credential helpers or an approved secret manager; never place passwords or access tokens directly in a shared Clash YAML file.

Finally, define an explicit failure policy. If the Docker policy group has no healthy proxy, should pulls fail closed, fall back to DIRECT, or use an internal mirror? For restricted or production networks, silent fallback to DIRECT can create confusing outages or violate policy. A deliberate fail-closed choice is usually easier to audit, while a controlled internal mirror can provide availability without exposing every host to external routing changes.

Frequently asked questions

Is TUN mode required for Docker Hub pulls?

No. Docker can use an HTTP or HTTPS proxy configured specifically for the daemon, and an internal registry mirror may be a better production design. TUN is useful when several applications need transparent routing, when a daemon ignores shell proxy variables, or when you want one observable policy for host-level traffic. It introduces route and DNS complexity, so enable it because it solves a defined problem rather than because it is the most advanced switch.

Why does Docker still use DIRECT after TUN is enabled?

Check whether the connection is actually generated by the host you are monitoring, whether the Docker daemon runs in rootless or remote mode, and whether a higher-priority rule matches the destination first. Inspect the live connection entry and its rule label. Also check for a broad regional or MATCH,DIRECT rule placed above the Docker rules.

Why does login succeed but image pulling fail?

Login may contact an authentication endpoint successfully while the actual image layers come from a different registry or CDN hostname. Watch the complete pull, not only the login command. Add rules for observed destinations, verify DNS consistency, and test the same image after selecting a stable node with adequate long-download performance.

Should Docker Hub be added to NO_PROXY?

Usually not when Clash is responsible for routing Docker Hub traffic. NO_PROXY should normally contain loopback addresses, local services, and private registries that must remain direct. Adding Docker Hub there can bypass both the daemon proxy and your TUN policy, producing the exact timeout you are trying to remove.

Compared with desktop VPN clients that expose only a global on/off switch, or ad-hoc Docker proxy snippets that ignore daemon boundaries and CDN hostnames, a carefully designed Clash TUN setup gives you visible connections, ordered domain rules, selectable policy groups, and a DNS path you can test. That makes Docker Hub troubleshooting more repeatable: you can distinguish authentication, routing, resolver, MTU, and node-quality problems instead of restarting the daemon blindly. If you want one client to inspect and manage these routes across development workflows, download the appropriate Clash build for your platform.

Download Clash for free and browse freely →