Running Clash inside Docker is useful when proxying development tools, CI jobs, containers, and selected services without installing a desktop client on every machine. However, a Docker-based transparent proxy is not simply a matter of starting a container and exposing a SOCKS port. Traffic must be intercepted at the correct network layer, DNS requests must follow the same policy as application connections, and the container must have enough privileges to create a TUN device or modify iptables rules. This guide presents a practical Clash Docker transparent proxy design using Mihomo-compatible YAML, Docker networking, TUN mode, DNS handling, iptables, and application-specific routing.

The examples are intentionally modular. You can use TUN mode for a host-wide setup, redirect selected traffic with iptables, or place only particular development containers behind Clash. Before applying the configuration to a production host, replace sample proxy credentials, review the private subnets used by your environment, and test direct and proxied destinations separately.

Architecture and Prerequisites

A transparent proxy has three independent parts:

  • Clash or Mihomo: receives intercepted connections, resolves domains, evaluates rules, and forwards traffic through a proxy group or directly.
  • The interception layer: TUN mode, iptables REDIRECT, or a combination of Docker routing and host firewall rules sends packets to Clash without requiring applications to understand proxy protocols.
  • DNS control: DNS queries must be captured or explicitly forwarded to Clash. If an application resolves domains through an unrelated resolver, domain rules may be bypassed and DNS leaks can occur.

For this article, assume the host runs Linux, Docker Engine, and a recent Mihomo-compatible build. The host interface is represented by eth0, the Docker bridge is docker0, and the Clash container is named clash. Names and interface addresses vary between distributions, so inspect them before copying firewall commands:

Inspect Host Interfaces and Docker Networks
# Find the default interface and route
ip route
ip addr

# Inspect Docker's bridge subnet
docker network inspect bridge

# Check whether the kernel exposes TUN support
ls -l /dev/net/tun

The container generally needs NET_ADMIN to create routes and firewall rules, and access to /dev/net/tun for TUN mode. Some environments require NET_RAW for diagnostic tools or packet handling. Do not add privileged: true by default; it grants far more access than necessary.

Security boundary: a transparent proxy container with network administration capabilities can alter traffic handling on the host. Bind the external controller to localhost or a protected management network, set a strong secret, and never expose the controller port directly to the public Internet.

Docker Network and Container Layout

There are two common deployment patterns. In the first, Clash uses host networking. This is the easiest design for host-wide TUN or REDIRECT interception because the process sees the host's interfaces directly. In the second, Clash uses a normal bridge network and selected application containers send traffic through it. The bridge approach provides better isolation, but routing and DNS become more involved.

A host-network deployment can be started with a Compose file like this:

docker-compose.yml
services:
  clash:
    image: metacubex/mihomo:latest
    container_name: clash
    network_mode: host
    cap_add:
      - NET_ADMIN
      - NET_RAW
    devices:
      - /dev/net/tun:/dev/net/tun
    volumes:
      - ./config:/root/.config/mihomo
    restart: unless-stopped

With network_mode: host, Docker does not perform port mapping. The ports in the YAML are bound directly on the host, and Clash can see host routing more naturally. The trade-off is reduced network isolation and a higher risk of accidentally exposing a mixed-port or controller port on every host interface.

If you want only one or two containers to use the proxy, create a dedicated network instead. A normal bridge allows application containers to remain separated from the host, but it does not automatically make their traffic transparent. You must either configure those applications to use a proxy endpoint, place them in a network namespace that shares the Clash container, or route their packets through host firewall rules.

Core YAML for TUN and DNS

TUN mode is usually the cleanest option for modern Mihomo deployments. It creates a virtual network interface and captures TCP, UDP, and optionally DNS traffic before applications need to know that a proxy exists. The exact supported fields can differ between Clash forks, so confirm the version's documentation when a field is rejected.

config.yaml: TUN, DNS, and Controller
mixed-port: 7890
allow-lan: false
mode: rule
log-level: info

external-controller: 127.0.0.1:9090
secret: replace-with-a-long-secret

tun:
  enable: true
  stack: mixed
  dns-hijack:
    - any:53
    - tcp://any:53
  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://dns.google/dns-query
  fallback:
    - https://9.9.9.9/dns-query
  fake-ip-filter:
    - +.lan
    - +.local
    - localhost.ptlogin2.qq.com

auto-route installs routes for captured traffic, while auto-detect-interface helps Mihomo select the physical interface used for direct connections. strict-route is useful when you want to reduce accidental bypasses, but it may interfere with unusual VPNs, policy routing, or multiple uplinks. Enable it only after normal direct traffic works.

fake-ip is generally a good companion to TUN mode because Clash can preserve the original domain and apply domain rules before connecting to the destination. Private names and service discovery domains often need exceptions. If a local application fails when it receives a fake address, add its domain to fake-ip-filter rather than disabling fake IP globally.

The DNS listener above uses port 1053 to avoid competing with a host resolver already bound to port 53. TUN DNS hijacking can still redirect application DNS queries to Clash. For bridge-only deployments, however, Docker containers may continue using Docker's embedded resolver at 127.0.0.11; those containers need an explicit DNS setting or a separate routing design.

Proxies, Groups, and Application Routing

The inbound and DNS settings do not decide where traffic goes. That work is performed by proxy definitions, groups, and rules. Keep the policy readable: private networks should normally remain direct, development services can have their own group, and a final catch-all rule can use the general proxy group.

Proxy Groups and Routing Rules
proxies:
  - name: dev-exit
    type: socks5
    server: proxy.example.net
    port: 443
    username: proxy-user
    password: replace-this-password
    tls: true

  - name: direct-exit
    type: direct

proxy-groups:
  - name: Development
    type: select
    proxies:
      - dev-exit
      - DIRECT

  - name: General
    type: url-test
    proxies:
      - dev-exit
      - DIRECT
    url: https://www.gstatic.com/generate_204
    interval: 300
    tolerance: 50

rules:
  - DOMAIN-SUFFIX,corp.example,DIRECT
  - DOMAIN-SUFFIX,internal, 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
  - DOMAIN-SUFFIX,github.com,Development
  - DOMAIN-SUFFIX,pypi.org,Development
  - DOMAIN-SUFFIX,npmjs.org,Development
  - MATCH,General

Check the syntax carefully: rule entries are comma-separated strings, and whitespace inside a rule can make it invalid or produce an unexpected match. The order is also significant. A private-network rule placed after MATCH never gets a chance to run.

Application-specific routing is especially useful for development workloads. You may want package managers, container registries, and source-control hosts to use a proxy while keeping databases, Kubernetes API endpoints, and corporate services direct. Use precise suffix rules instead of broad keyword rules whenever possible. A broad DOMAIN-KEYWORD,dev rule can unexpectedly capture unrelated public domains.

For IP-only applications that do not preserve the original hostname, domain rules may not work. In that case, use an IP-CIDR rule, a maintained rule provider, or enable a DNS strategy that lets Mihomo recover the domain from the connection. Avoid adding no-resolve casually: it prevents Clash from resolving the IP for rule evaluation, which is correct for private CIDRs but often defeats domain-based policy for public addresses.

iptables and Selective Transparent Routing

TUN mode can handle most interception by itself, but iptables remains valuable when you need to redirect traffic from a selected user, Docker bridge, or subnet. The safest pattern is to exclude Clash's own process and private destinations before applying a redirect. Otherwise, Clash may capture its own outbound connection and create a loop.

The following example illustrates the structure of a dedicated chain. It assumes a REDIRECT-capable inbound port is configured in the Clash variant you use. Verify the supported transparent proxy fields before enabling these commands.

iptables Redirect Chain Example
# Create a chain for TCP interception
iptables -t nat -N CLASH

# Never redirect local and private destinations
iptables -t nat -A CLASH -d 127.0.0.0/8 -j RETURN
iptables -t nat -A CLASH -d 10.0.0.0/8 -j RETURN
iptables -t nat -A CLASH -d 172.16.0.0/12 -j RETURN
iptables -t nat -A CLASH -d 192.168.0.0/16 -j RETURN

# Avoid loops from the Clash service account
iptables -t nat -A CLASH -m owner --uid-owner clash -j RETURN

# Redirect remaining TCP traffic to the transparent port
iptables -t nat -A CLASH -p tcp -j REDIRECT --to-ports 7892

# Apply to traffic arriving from the Docker bridge
iptables -t nat -A PREROUTING -i docker0 -p tcp -j CLASH

Do not assume the service account exists inside the host namespace just because the container has a user with that name. In a containerized deployment, owner matching may not identify the packets as expected. A dedicated source subnet, mark-based policy routing, or TUN-only design can be more predictable. Also remember that traditional REDIRECT handles TCP; UDP and ICMP require separate mechanisms such as TPROXY, TUN capture, or application-level support.

Start narrow: apply interception to one test container or one Docker subnet first. Confirm that direct access to local services still works, then expand the rule scope. A small test surface makes routing loops and DNS failures much easier to identify.

Testing and Diagnosing Failed Connections

Transparent proxy failures are easiest to diagnose when you test each layer independently. Do not begin by changing five YAML fields at once. First confirm that the container is running, then check DNS, then check the route, and finally inspect Clash's decision and outbound connection.

  1. Validate the configuration: start the container in the foreground and inspect startup logs for unknown fields, invalid proxy definitions, or a failure to open /dev/net/tun.
  2. Check the controller: query the local API with the configured secret and confirm that the active configuration, proxy groups, and connections are visible.
  3. Test DNS from the application environment: compare getent hosts, nslookup, or dig inside the affected container and on the host. A successful host lookup does not prove that the application container uses Clash.
  4. Test the route: use curl -v to a known HTTP endpoint and inspect whether Clash logs a connection. For TLS failures, use curl -vk only as a diagnostic tool; do not disable certificate verification permanently.
  5. Inspect packets: capture traffic on docker0, the TUN interface, and the physical interface. If packets appear on the bridge but never on TUN, interception is wrong. If they appear on TUN but not on the physical interface, the outbound proxy or route is failing.
Useful Runtime Diagnostics
# Follow Clash logs
docker logs -f clash

# Inspect container DNS and routes
docker exec -it app cat /etc/resolv.conf
docker exec -it app ip route

# Test name resolution and HTTPS from the app container
docker exec -it app getent hosts github.com
docker exec -it app curl -v https://github.com/

# Observe DNS and TCP packets on the Docker bridge
sudo tcpdump -ni docker0 port 53 or port 443

# Observe traffic on a TUN interface when available
sudo tcpdump -ni Meta port 53

If a connection works from the host but fails inside a container, compare the container's default route and resolver first. Docker often supplies its own DNS proxy, and an application may use a hard-coded resolver, DNS-over-HTTPS endpoint, or IPv6 address that bypasses your intended policy. Disable IPv6 only when it is not part of your network design; otherwise configure IPv6 capture and rules consistently.

DNS leaks usually indicate one of four conditions: DNS hijacking is not enabled, the application uses an encrypted resolver directly, the container has a static DNS server, or the fake-IP range overlaps with an internal network. Search logs for the actual query destination, then inspect /etc/resolv.conf and application-specific settings. A browser, package manager, and container runtime may each use different DNS behavior.

When containers bypass the proxy entirely, verify that the iptables rule is attached to the correct interface and protocol. A rule on docker0 does not automatically cover a custom bridge. Run docker network inspect, identify the bridge interface on the host, and apply the policy there. For containers using network_mode: host, their traffic will not enter a normal Docker bridge and must be captured through host-level TUN or firewall rules.

Finally, check MTU when handshakes start successfully but downloads stall or large responses time out. TUN encapsulation and an upstream VPN can reduce the effective packet size. Lower the TUN or Docker interface MTU gradually, then retest with a large response. Avoid treating MTU changes as a substitute for fixing routing loops, incorrect DNS, or an unavailable proxy node.

Production Hardening and Maintenance

A reliable Docker transparent proxy should be maintained like a network service, not treated as a disposable test container. Pin a tested image tag instead of relying on latest, back up the YAML and rule-provider files, and review changes in a staging environment before upgrading Mihomo. A new version may add fields, remove deprecated behavior, or change how TUN and DNS interact.

  • Bind the external controller to 127.0.0.1 or a management-only address.
  • Use a long secret and keep proxy passwords outside publicly shared configuration files when possible.
  • Exclude loopback, Docker subnets, VPN subnets, and corporate private ranges deliberately.
  • Monitor container restarts and verify that routes are restored after a host reboot.
  • Keep a direct-access recovery path, such as console access or a tested command that removes temporary iptables chains.
  • Document every custom interface, subnet, DNS exception, and rule-provider source.

The most dependable rollout sequence is to begin with a normal Clash proxy port, verify the nodes and rules, enable DNS processing, activate TUN for one test workload, and only then add Docker-wide interception or iptables policies. This staged approach separates proxy credential problems from kernel permissions, DNS leaks, and firewall mistakes. Once packet captures, logs, and application tests all agree, the Docker-based transparent proxy becomes predictable enough for daily development and automation workloads.

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 →