Docker networking often fails at the boundary between the host and the container. The host can browse through Clash normally, while docker pull, a package manager, or a build step inside a container still times out. The reason is simple: containers do not automatically inherit the host application's proxy behavior. A browser may use an HTTP proxy or a TUN interface, but Docker has its own daemon, network namespace, DNS path, and outbound connection logic.

When several containers need the same network policy, adding proxy variables to every image is the wrong abstraction. It creates configuration drift, exposes proxy credentials to build logs and environment inspection, and misses traffic generated by tools that do not honor HTTP_PROXY. A better design is to make Clash the host-level policy engine and route selected Docker traffic through it transparently. This article explains how to build that setup with Docker bridge networks, Clash or Mihomo redirection, selective rules, secure controller access, and a repeatable troubleshooting workflow.

Understand the Docker-to-Clash Network Model

Before editing a Clash configuration, map the traffic path. In the common Linux bridge setup, a container receives an address from a private subnet such as 172.17.0.0/16. Its default gateway is the Docker bridge on the host, usually 172.17.0.1. When the container connects to registry-1.docker.io, the packet leaves the container namespace, crosses the bridge, and is routed by the host.

That path is important because the Docker daemon and ordinary container processes are not necessarily the same traffic source. A command such as docker pull is usually executed by the Docker daemon, which may run as a system service outside your shell environment. By contrast, curl inside a container is generated by a process inside the container namespace. A reliable design must account for both paths.

Traffic sourceTypical network pathConfiguration concern
Docker daemonHost process to the registryDaemon proxy settings or host-level transparent routing
Container processContainer namespace → Docker bridge → hostForwarding, NAT, and Clash interception
Build stepTemporary build container → bridgeMust work without modifying every image
Host browserHost process → Clash listener or TUNUseful for comparison, but not proof that Docker is configured
Docker DNS queryContainer resolver → Docker embedded DNS → upstreamDNS mode and domain-based rule matching

There are two broad implementation patterns. The first is an explicit proxy: configure Docker and applications to use Clash's HTTP or SOCKS listener. It is easy to understand, but every client must support and receive the proxy settings. The second is transparent routing: Clash intercepts traffic based on the host's routing and firewall path, so applications can connect normally. Transparent routing is usually the better fit for a development machine with many images and tools.

Choose the abstraction first: use an explicit proxy when only one daemon or a small number of applications need proxy access. Use transparent routing when you want package managers, Git, image builds, language tools, and short-lived containers to follow one host-level policy.

Prepare Clash for Transparent Routing

For new deployments, Mihomo is generally the most capable Clash-compatible core because it supports TUN mode, enhanced DNS behavior, rule providers, and additional routing controls. The exact field names can vary slightly between Clash derivatives and client interfaces, so validate the generated configuration in the client you actually run. Clash Verge Rev and Mihomo-based clients commonly expose these features directly, while older clients may require manual YAML editing.

The following baseline enables a TUN interface, automatic route installation, and system-wide DNS interception. It is intentionally conservative: private networks remain direct, and the proxy group name is an example that must match a group defined elsewhere in your configuration.

Mihomo TUN and DNS Baseline
mixed-port: 7890
allow-lan: false
mode: rule

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

dns:
  enable: true
  listen: 0.0.0.0:1053
  enhanced-mode: fake-ip
  fake-ip-range: 198.18.0.1/16
  nameserver:
    - https://dns.google/dns-query
    - https://cloudflare-dns.com/dns-query
  fake-ip-filter:
    - +.lan
    - +.local
    - localhost.ptlogin2.qq.com

rules:
  - DOMAIN-SUFFIX,lan,DIRECT
  - DOMAIN-SUFFIX,local,DIRECT
  - IP-CIDR,10.0.0.0/8,DIRECT,no-resolve
  - IP-CIDR,172.16.0.0/12,DIRECT,no-resolve
  - IP-CIDR,192.168.0.0/16,DIRECT,no-resolve
  - MATCH,Proxy

auto-route installs routes so traffic can enter the TUN interface without application-specific proxy variables. auto-detect-interface helps Mihomo select the active physical interface, which is useful on laptops that move between Wi-Fi, Ethernet, and VPN adapters. strict-route reduces accidental leaks by making route handling more deterministic, but it can expose mistakes in local-network rules. If a printer, development server, or corporate subnet stops working, inspect the routing rules before disabling strict behavior globally.

DNS deserves special attention. Domain rules are most reliable when Clash can see the original hostname instead of only a resolved address. Fake-IP mode assigns a synthetic address and keeps the domain mapping internally, allowing Clash to apply rules such as DOMAIN-SUFFIX,github.com,Proxy. Docker's embedded DNS service may still answer container queries through 127.0.0.11, so a working host DNS configuration does not automatically prove that container DNS is being intercepted correctly.

Do not proxy private address ranges by accident. Docker bridge subnets, host services, databases, and local development domains commonly use RFC1918 addresses. Put direct rules for your actual private networks before broad IP or catch-all rules, and verify that container-to-host connections still work.

Route Docker Traffic Through the Host

On Linux, transparent interception depends on the relationship between Docker's bridge, the host routing table, and the TUN implementation. The simplest path is to let Mihomo install system routes and then verify that the Docker bridge traffic is visible to the core. Start with a normal bridge network rather than host networking; host mode removes useful boundaries and makes it harder to tell which component is responsible for a failure.

Create a small test network and run a container with basic diagnostic tools:

Create a Docker Test Network
docker network create \
  --driver bridge \
  --subnet 172.30.0.0/24 \
  clash-dev-net

docker run --rm -it \
  --network clash-dev-net \
  --name clash-net-test \
  alpine:3.20 sh

Inside the container, test the layers separately. First inspect the default route and resolver:

Inspect Routes and DNS Inside the Container
ip route
cat /etc/resolv.conf
cat /etc/hosts

Then test a direct IP, a normal domain, and a domain that you intentionally route through the proxy. Alpine images may not include every utility, so install a diagnostic package temporarily if necessary:

Layered Connectivity Tests
apk add --no-cache curl bind-tools

nslookup github.com
curl -I --connect-timeout 10 https://github.com
curl -I --connect-timeout 10 https://registry-1.docker.io

Do not use a successful ping as the main test. Many services block ICMP, and a ping only proves that one protocol reached one address. HTTPS tests exercise DNS, TCP, TLS, routing, and the remote service in a way that better matches package installation and image pulls.

If the container cannot reach anything, check forwarding and firewall policy first. Docker normally installs its own NAT and forwarding rules, but a host firewall, VPN client, or hardened Linux profile may set the forwarding policy to DROP. Confirm the bridge address and interface names with ip addr and , then inspect firewall counters while making a request. A packet that never reaches the host routing path cannot be repaired by a Clash rule.

If direct traffic works but proxy-selected domains fail, inspect Clash's connections view or API dashboard while running curl. You should see the destination domain, the selected rule, and the proxy group. If no connection appears, the TUN route is not receiving Docker traffic, or the traffic is bypassing the expected interface. If the connection appears as an IP-only request, DNS interception may be incomplete and domain rules will not behave as expected.

Treat the Docker Daemon Separately

A frequent mistake is to test curl inside a container and assume that docker pull uses the same path. The Docker daemon is an independent process. Depending on the platform, it may be managed by systemd, Docker Desktop, or a virtualized Linux environment. A host-level TUN route can cover the daemon, but daemon-specific proxy configuration may still be necessary, especially when the daemon runs outside the namespace that Clash is intercepting.

For a Linux systemd installation, an explicit daemon proxy can be used as a fallback or as a deliberate design. Store credentials in a protected environment file rather than placing them in a public image or shell history:

Optional Docker Daemon Proxy Override
sudo mkdir -p /etc/systemd/system/docker.service.d

sudo tee /etc/systemd/system/docker.service.d/http-proxy.conf > /dev/null <<'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,.local,172.16.0.0/12,192.168.0.0/16"
EOF

sudo systemctl daemon-reload
sudo systemctl restart docker
sudo systemctl show --property=Environment docker

This method is explicit rather than transparent, so it should not be treated as a universal substitute for TUN routing. It also requires the listener to be reachable from the daemon's network context. On Docker Desktop, 127.0.0.1 may refer to a VM or desktop backend rather than the host process running Clash. In that environment, use the proxy settings exposed by Docker Desktop or a host address reachable from its Linux VM.

Design Selective Rules for Development

Transparent proxying does not mean sending every packet through a remote node. Developers usually need a split policy: public registries, Git hosting, and selected package mirrors use the proxy, while local services, private repositories, corporate networks, and nearby mirrors remain direct. Narrow rules make the setup faster and easier to audit.

DestinationRecommended routeReason
Docker Hub and public registriesProxy when requiredImage manifests and layers may be slow or unavailable on the local route
GitHub, GitLab, package indexesProxy selectivelyImproves access without affecting internal Git services
Docker bridge subnetsDIRECTRequired for container-to-container communication
Host gateway and LAN servicesDIRECTPrevents development databases and dashboards from leaving the LAN
Unknown destinationsPolicy dependentUse a deliberate MATCH rule instead of accidental behavior

Put specific rules before broad rules. A common mistake is placing MATCH,Proxy above private-network exceptions. Once a connection matches, later rules are not considered. Also remember that rule providers are easier to maintain than hundreds of manually edited entries. Keep the stable local exceptions in the main configuration and place frequently changing service lists in providers.

Developer-Oriented Routing Rules
rules:
  # Container and local traffic stays on the local network
  - IP-CIDR,172.17.0.0/16,DIRECT,no-resolve
  - IP-CIDR,172.30.0.0/24,DIRECT,no-resolve
  - IP-CIDR,10.0.0.0/8,DIRECT,no-resolve
  - IP-CIDR,172.16.0.0/12,DIRECT,no-resolve
  - IP-CIDR,192.168.0.0/16,DIRECT,no-resolve
  - DOMAIN-SUFFIX,corp.example,DIRECT
  - DOMAIN-SUFFIX,github.com,Proxy
  - DOMAIN-SUFFIX,githubusercontent.com,Proxy
  - DOMAIN-SUFFIX,docker.io,Proxy
  - DOMAIN-SUFFIX,ghcr.io,Proxy
  - DOMAIN-SUFFIX,pypi.org,Proxy
  - DOMAIN-SUFFIX,npmjs.org,Proxy
  - MATCH,Proxy

Registry traffic can involve several hostnames. Docker Hub may contact authentication, API, registry, CDN, and object-storage domains during one pull. If only docker.io is proxied, the initial login may succeed while a layer download still times out. Use the Clash connection log to discover the actual destinations, then add precise rules rather than blindly proxying all IP addresses.

For reproducible builds, keep the routing policy in version control but do not commit secrets, private node URLs, or authentication tokens. Document the expected proxy group name, the Docker subnets, and the test commands. A new developer should be able to clone the configuration, start Clash, create the test network, and determine whether the host policy is working in a few minutes.

Secure the Setup and Troubleshoot by Layer

Transparent routing changes packet flow, but it does not remove the need for access control. Keep Clash's external controller bound to 127.0.0.1 unless remote administration is genuinely required. Never expose the controller without a strong secret. The controller can switch nodes, inspect connections, and sometimes alter configuration; treating it as a harmless monitoring port is a serious security mistake.

Secure Local Controller
external-controller: 127.0.0.1:9090
secret: replace-with-a-long-random-secret

When a request fails, test from the bottom of the stack upward. This avoids changing five settings at once and losing the cause:

  1. Container interface: confirm the container has an address, a default route, and a valid gateway.
  2. Host forwarding: verify that the host accepts traffic from the Docker bridge and that firewall rules are not dropping forwarded packets.
  3. DNS resolution: compare nslookup results inside the container with the host. A DNS failure is not the same as a proxy failure.
  4. Direct HTTPS: test a known reachable domain and an IP endpoint. This separates general connectivity from rule matching.
  5. Clash visibility: watch the connection log or dashboard while making the request. Check the matched rule and selected group.
  6. TLS and registry behavior: if Clash sees the request but it fails after connection, inspect certificate errors, SNI, authentication, and remote node health.
  7. Daemon context: test docker pull independently from container curl. Confirm the Docker daemon environment or Desktop proxy configuration.

Common symptoms provide useful clues. If every container request fails, suspect forwarding, TUN routes, or a firewall. If direct domains work but GitHub fails, inspect domain rules and DNS mode. If DNS resolves but HTTPS hangs, check whether the resolved address is a fake IP that the traffic path does not return to Clash. If docker pull fails while an interactive container works, focus on the daemon or Docker Desktop backend. If only large layers fail, investigate MTU, HTTP/2 behavior, the selected node, and the registry CDN rather than immediately rewriting rules.

Capture evidence before changing the configuration: record docker info, docker network inspect, the container's route table, DNS output, the exact failing hostname, and the Clash connection entry. These details make intermittent problems reproducible and reveal whether the failure is in Docker, DNS, routing, or the remote proxy.

A host-level Clash policy gives development environments one consistent place to manage routing. It avoids rebuilding images merely to add proxy variables, covers temporary build containers, and lets you keep private services direct while sending only selected public traffic through a proxy. Start with a small bridge network, validate DNS and HTTPS independently, confirm that Clash can see the connection, and only then expand the policy to production-like Compose projects. Once the network path is understood, Docker pulls, package installs, Git operations, and automated builds become predictable rather than a collection of per-container workarounds.

Get Started

Take Full Control of Your Traffic with Clash

Available on Windows, macOS, Linux, Android, and iOS. Flexible rules, simple setup, ready to use.

Download Free View Setup Guide →