Docker networking becomes surprisingly difficult when applications need to use Clash or Mihomo. Passing HTTP_PROXY and HTTPS_PROXY into every container works for some command-line tools, but it is fragile: image builds may ignore the variables, UDP traffic may bypass them, applications may open direct connections, and third-party services may use DNS in ways that the proxy environment cannot control. A transparent proxy solves this by intercepting traffic at the network layer. Containers continue connecting to normal destinations, while the Linux host redirects selected TCP and UDP flows to Clash or Mihomo.

This guide presents a practical, maintainable design for Docker transparent proxying in 2026. It focuses on Linux hosts running Docker Engine or Docker Compose, with Mihomo used as the primary example because it provides modern TUN, redirection, DNS, and rule capabilities. The same architecture can be adapted to compatible Clash Meta-based clients. You will learn how Docker bridge traffic moves through the host, how to choose between redir, tproxy, and TUN mode, how to avoid DNS loops, and how to troubleshoot the setup without guessing.

Important scope: transparent interception is a host-level firewall operation. It is not enough to enable a proxy port inside Clash. You must also route container packets through the correct interception chain, exclude Clash itself and private networks where appropriate, and verify that return traffic follows the expected path.

How Docker Transparent Proxying Works

A normal Docker container usually receives an address from a bridge network such as 172.17.0.0/16 or a Compose-created subnet. When the container connects to github.com:443, the packet is sent to the container's default gateway, which is the Docker bridge on the host. Docker then applies its own NAT rules and sends the connection toward the host's external interface.

Transparent proxying inserts Clash or Mihomo into this path. The host identifies packets whose source belongs to a Docker subnet, redirects them to a local proxy listener, and lets the proxy apply domain rules, proxy groups, and DNS policy. The application inside the container does not need to know that a proxy exists. This is especially useful for package managers, Git, language tooling, containerized browsers, CI jobs, and AI SDKs that do not consistently honor proxy environment variables.

There are three common interception models:

  • Redir mode: TCP connections are redirected to a local redirection port. This is simple and widely compatible, but it cannot transparently handle every UDP workload.
  • TProxy mode: TCP and UDP packets are intercepted while preserving the original destination information. It provides better protocol coverage, but requires policy routing and a correctly configured firewall chain.
  • TUN mode: Mihomo creates a virtual network interface and captures traffic through the kernel routing table. TUN is often the cleanest choice for a general host, but Docker forwarding, route exclusions, and container DNS still require careful design.

For a dedicated Linux gateway, TProxy or TUN is usually the most complete solution. For a single development machine where the main requirement is reliable TCP access from containers, redirection mode is easier to operate. Do not choose a mode merely because it appears in a sample configuration. Select it according to the traffic you need to support and the amount of control you have over the host firewall.

Choosing the Right Interception Mode

ModeTraffic coverageOperational complexityRecommended use
redirPrimarily TCPLowDeveloper tools, Git, package managers, HTTPS APIs
tproxyTCP and UDPHighDNS, QUIC, real-time apps, gateway deployments
tunTCP, UDP, and routed trafficMedium to highWhole-host or whole-network interception with Mihomo

A useful production principle is to start with TCP redirection and a small Docker test network. Once domain routing, DNS behavior, and firewall exclusions are verified, extend the design to UDP or TUN if a real application requires it. This staged approach makes failures easier to isolate than enabling every advanced feature at once.

Mihomo and Docker Base Configuration

First decide where Mihomo runs. Running it directly on the host gives it straightforward access to loopback listeners and the host network namespace. Running Mihomo in a container is also possible, but that container needs elevated network capabilities, access to the host namespace or carefully designed routing, and persistent configuration. For a first transparent-proxy deployment, host installation is generally less error-prone.

The following example shows a minimal Mihomo configuration for Docker-oriented transparent interception. The exact field names can vary between Clash cores and Mihomo releases, so validate the configuration with the version used on your host.

Mihomo Docker-Oriented Core Settings
mixed-port: 7890
allow-lan: false
mode: rule
log-level: info

dns:
  enable: true
  listen: 127.0.0.1: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:
    - tls://8.8.8.8:853
  fake-ip-filter:
    - '*.lan'
    - '*.local'
    - 'localhost'
    - '+.docker.internal'

redir-port: 7892
tproxy-port: 7893

rules:
  - DOMAIN-SUFFIX,local,DIRECT
  - IP-CIDR,127.0.0.0/8,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
  - MATCH,Proxy

The mixed-port is useful for applications that explicitly support an HTTP or SOCKS proxy, but transparent interception uses redir-port or tproxy-port instead. Keeping these roles separate makes testing easier. You can first verify the proxy itself with curl --proxy http://127.0.0.1:7890, then test redirected traffic independently.

DNS deserves special attention. A container normally receives Docker's embedded resolver at 127.0.0.11. That address is inside the container namespace, not the host, so a host firewall rule that redirects packets destined for port 53 may not behave as expected. With fake-IP mode, you should either explicitly route container DNS queries to a reachable Mihomo DNS listener or ensure that Docker's DNS forwarding path is compatible with your design. Avoid redirecting Mihomo's own DNS requests back into its interception chain, because that creates a loop.

Use domain-based rules whenever possible: fake-IP preserves the original domain inside Mihomo, allowing rules such as DOMAIN-SUFFIX,github.com or DOMAIN-KEYWORD,openai to work even when the application only sees a synthetic address. Keep private Docker and LAN ranges as DIRECT unless you intentionally need to proxy internal services.

Hands-On Setup: Compose, Capabilities, and Firewall Rules

The following procedure uses a dedicated Compose network and a redirection-based TCP setup. It is deliberately conservative: only traffic originating from the selected Docker subnet is intercepted, and private destinations are excluded. Replace the subnet and interface names with values from your host.

  1. Create a predictable Docker network. A fixed subnet makes firewall rules readable and prevents them from silently targeting the wrong range after a network recreation.
  2. Confirm that IP forwarding is enabled on the host. Docker normally manages much of its forwarding behavior, but transparent routing still depends on the kernel forwarding setting.
  3. Start a test container and verify its default route, DNS server, and assigned address before adding interception rules.
  4. Insert a Docker-specific NAT rule that redirects TCP traffic to Mihomo's redirection port. Exclude private destinations and the proxy process to prevent loops.
  5. Test direct IP access, domain access, HTTPS, and a known proxy-only domain separately. Then inspect Mihomo's connection log.
docker-compose.yml
services:
  toolbox:
    image: curlimages/curl:latest
    container_name: proxy-toolbox
    networks:
      proxy_net:
        ipv4_address: 172.30.0.10
    command: ["sleep", "infinity"]
    restart: unless-stopped

networks:
  proxy_net:
    driver: bridge
    ipam:
      config:
        - subnet: 172.30.0.0/24
          gateway: 172.30.0.1

Enable forwarding temporarily for the first test:

Enable IPv4 Forwarding
sudo sysctl -w net.ipv4.ip_forward=1
docker compose up -d
docker exec -it proxy-toolbox sh

For a host using iptables, a basic redirection pattern can look like this. The owner exclusion is only effective for locally generated packets and may not identify every proxy process in every deployment. The subnet exclusion is the more important safeguard for container-originated traffic.

Example iptables Redirection Rules
sudo iptables -t nat -N CLASH_DOCKER 2>/dev/null || true
sudo iptables -t nat -F CLASH_DOCKER

sudo iptables -t nat -A CLASH_DOCKER -d 127.0.0.0/8 -j RETURN
sudo iptables -t nat -A CLASH_DOCKER -d 10.0.0.0/8 -j RETURN
sudo iptables -t nat -A CLASH_DOCKER -d 172.16.0.0/12 -j RETURN
sudo iptables -t nat -A CLASH_DOCKER -d 192.168.0.0/16 -j RETURN
sudo iptables -t nat -A CLASH_DOCKER -p tcp -j REDIRECT --to-ports 7892

sudo iptables -t nat -D PREROUTING -s 172.30.0.0/24 -p tcp -j CLASH_DOCKER 2>/dev/null || true
sudo iptables -t nat -A PREROUTING -s 172.30.0.0/24 -p tcp -j CLASH_DOCKER

Modern distributions may use nftables underneath the iptables command, and Docker can rewrite chains when networks or containers start and stop. For a durable deployment, place the logic in the firewall system actually managed by your operating system, or use a boot-time service that verifies and restores the rule after Docker starts. Do not blindly append duplicate rules on every reboot. A repeated chain can cause confusing behavior, including multiple counters increasing for a single connection.

Now test from the container:

Container Verification Commands
ip route
cat /etc/resolv.conf
curl -I https://example.com
curl -I https://github.com
wget -qO- https://api.ipify.org
exit

sudo iptables -t nat -L CLASH_DOCKER -n -v
sudo ss -lntup | grep -E '7890|7892|7893'

The packet counters on CLASH_DOCKER should increase when the container makes TCP requests. If they remain at zero, the rule is attached to the wrong path, the source subnet is incorrect, or the host is using a firewall backend that does not process the command as expected. If counters increase but Mihomo shows no connection, confirm that redir-port is listening on the address expected by the REDIRECT target.

Compose Environment Variables Are Still Useful

Transparent interception removes the need to configure every application, but explicit proxy variables can remain valuable for build stages and tools that send proxy metadata or use special proxy authentication. Keep them optional rather than treating them as the only mechanism.

Optional Compose Proxy Variables
services:
  builder:
    build: .
    environment:
      HTTP_PROXY: http://host.docker.internal:7890
      HTTPS_PROXY: http://host.docker.internal:7890
      NO_PROXY: localhost,127.0.0.1,.local,172.16.0.0/12,172.30.0.0/24

On Linux, host.docker.internal may require an explicit host gateway mapping. More importantly, this option does not replace transparent routing: a process may ignore the variables, and the proxy address may be unavailable during an isolated build network. Use both approaches when you need predictable builds and transparent runtime behavior, but avoid setting a proxy variable that points back to a port being transparently redirected.

Advanced TProxy, TUN, and DNS Considerations

Redirection is an excellent first implementation, but it has boundaries. QUIC uses UDP, many DNS designs use UDP or encrypted DNS, and some applications rely on protocols that do not work through a basic TCP redirect. TProxy preserves the original destination and supports both TCP and UDP, but it requires a separate routing table and firewall marks. A simplified Linux policy-routing pattern usually includes a mark rule, a local route for marked packets, and a TProxy target in the mangle table.

Illustrative TProxy Policy Routing
sudo ip rule add fwmark 1 table 100
sudo ip route add local 0.0.0.0/0 dev lo table 100

sudo iptables -t mangle -N CLASH_TPROXY 2>/dev/null || true
sudo iptables -t mangle -F CLASH_TPROXY
sudo iptables -t mangle -A CLASH_TPROXY -d 127.0.0.0/8 -j RETURN
sudo iptables -t mangle -A CLASH_TPROXY -d 172.16.0.0/12 -j RETURN
sudo iptables -t mangle -A CLASH_TPROXY -p tcp -j TPROXY --on-port 7893 --tproxy-mark 1/1
sudo iptables -t mangle -A CLASH_TPROXY -p udp -j TPROXY --on-port 7893 --tproxy-mark 1/1

This is an architectural example, not a universal copy-and-paste firewall policy. TProxy rules must be ordered around Docker's own chains, must exclude the proxy's traffic, and must account for IPv6 if IPv6 is enabled. A mistake in policy routing can intercept the proxy's upstream connection and create a loop that consumes CPU while producing no useful traffic. Start with a disposable test host and save the working rules only after examining counters and connection logs.

TUN mode can reduce the amount of manual TProxy plumbing because Mihomo owns a virtual interface and performs routing through it. A typical Mihomo TUN section may look like this:

Example Mihomo TUN Section
tun:
  enable: true
  stack: system
  auto-route: true
  auto-detect-interface: true
  strict-route: true
  dns-hijack:
    - any:53
  route-exclude-address:
    - 10.0.0.0/8
    - 172.16.0.0/12
    - 192.168.0.0/16

With Docker, auto-route and strict-route should be tested carefully. The container bridge must remain reachable, and Docker's internal DNS address must not be trapped in a way that prevents service discovery. Some users prefer to exclude all Docker bridge ranges from the TUN route and use explicit PREROUTING rules for container traffic. Others route the bridge through TUN and use a DNS listener bound to the host bridge address. Both can work; consistency matters more than a particular style.

DNS failures often look like proxy failures. Check these layers independently:

  • Run getent hosts example.com or an equivalent resolver command inside the container.
  • Inspect /etc/resolv.conf and identify whether the container uses Docker's 127.0.0.11 resolver, a host address, or an external server.
  • Check whether Mihomo receives the DNS request and whether the returned address is a fake-IP address.
  • Verify that the connection's original domain is visible in the Mihomo connection panel or log.
  • Test an internal service by name to ensure that private DNS is not being sent through a public resolver.

Troubleshooting and Production Hardening

Use a layered troubleshooting sequence instead of changing several settings at once. First confirm that the proxy core is healthy by testing its normal mixed port from the host. Next confirm that the container can reach the host gateway. Then verify packet counters, followed by Mihomo logs, and finally the application itself. Each layer answers a different question.

SymptomLikely causeFirst check
No rule countersWrong subnet or firewall pathdocker network inspect and firewall counters
Counters increase, no Mihomo entryListener mismatch or incorrect redirect portss -lntup and redir-port
Host works, container failsForwarding, Docker chain order, or DNS issueip_forward, route, and resolver configuration
Internal services failPrivate ranges are being interceptedDIRECT exclusions and NO_PROXY
Only UDP applications failRedir mode does not capture UDPUse TProxy or TUN and verify policy routing
Connections loop or consume CPUProxy upstream traffic is intercepted againProcess exclusions, route exclusions, and packet trace

For deeper inspection, use tcpdump on the Docker bridge and external interface. A packet visible on the bridge but absent from the redirection chain indicates a firewall placement problem. A packet reaching the proxy listener but never leaving the host suggests a rule decision, DNS issue, or upstream connectivity failure. Capture only the necessary interfaces and avoid logging credentials or authorization headers in shared environments.

Production deployments should make the following choices explicit:

  • Persist firewall state: restore rules after reboot and after Docker starts, but make the operation idempotent so duplicate chains are not created.
  • Limit the interception scope: target known Docker subnets instead of all private traffic. This protects host services and avoids surprising routing changes.
  • Protect the control plane: keep Mihomo's external controller bound to localhost or protect it with a strong secret and a restrictive firewall rule.
  • Handle IPv6 deliberately: either configure IPv6 interception and DNS consistently or disable IPv6 for the relevant containers. A container that receives an IPv6 address can bypass an IPv4-only proxy design.
  • Document exclusions: list Docker DNS, container subnets, LAN services, metadata endpoints, and monitoring destinations that must remain direct.
  • Monitor after upgrades: Docker, nftables, kernel, and Mihomo updates can change chain behavior or supported options. Re-test packet counters and DNS after each major upgrade.
Recommended rollout: begin with one Compose network, TCP redirection, fake-IP DNS, and a small test container. Add explicit private-network exclusions, then validate package managers and AI APIs. Only after that should you introduce UDP interception, TProxy, or TUN-wide routing.

Docker transparent proxying is not a single switch; it is a chain of cooperating layers: container routing, Docker bridge behavior, kernel forwarding, firewall interception, Clash or Mihomo listeners, DNS resolution, and rule matching. When each layer is tested independently, the setup becomes repeatable rather than mysterious. Start with the simplest mode that satisfies your traffic requirements, keep Docker subnets and private destinations explicit, and preserve a clean path for the proxy's own upstream connections. With those principles in place, containerized developer tools and AI services can use the same reliable Clash routing policy as applications running directly on the host.

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 →