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.
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
| Mode | Traffic coverage | Operational complexity | Recommended use |
|---|---|---|---|
redir | Primarily TCP | Low | Developer tools, Git, package managers, HTTPS APIs |
tproxy | TCP and UDP | High | DNS, QUIC, real-time apps, gateway deployments |
tun | TCP, UDP, and routed traffic | Medium to high | Whole-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.
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.
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.
- 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.
- 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.
- Start a test container and verify its default route, DNS server, and assigned address before adding interception rules.
- 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.
- Test direct IP access, domain access, HTTPS, and a known proxy-only domain separately. Then inspect Mihomo's connection log.
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:
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.
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:
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.
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.
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:
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.comor an equivalent resolver command inside the container. - Inspect
/etc/resolv.confand identify whether the container uses Docker's127.0.0.11resolver, 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.
| Symptom | Likely cause | First check |
|---|---|---|
| No rule counters | Wrong subnet or firewall path | docker network inspect and firewall counters |
| Counters increase, no Mihomo entry | Listener mismatch or incorrect redirect port | ss -lntup and redir-port |
| Host works, container fails | Forwarding, Docker chain order, or DNS issue | ip_forward, route, and resolver configuration |
| Internal services fail | Private ranges are being intercepted | DIRECT exclusions and NO_PROXY |
| Only UDP applications fail | Redir mode does not capture UDP | Use TProxy or TUN and verify policy routing |
| Connections loop or consume CPU | Proxy upstream traffic is intercepted again | Process 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.
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.
Take Full Control of Your Traffic with Clash
Available on Windows, macOS, Linux, Android, and iOS. Flexible rules, simple setup, ready to use.