Docker containers do not automatically inherit the proxy settings of the Linux host. A process inside a bridge network can send traffic directly through the host’s default route even while Clash or Mihomo is running perfectly. To route that traffic transparently, the host must recognize packets arriving from the Docker bridge, send selected packets to the proxy core, and avoid capturing traffic that should stay local. This guide builds that path step by step, explains the role of firewall forwarding and policy routing, and shows how to diagnose DNS problems, redirect loops, and failed container connections.

Choose a Traffic-Routing Method

There are two practical ways to send container traffic through Clash. The simpler method is to configure applications to use an HTTP or SOCKS proxy. The transparent method intercepts packets at the Docker host, so applications can connect normally without knowing about the proxy. These approaches solve different problems and can coexist, but transparent interception requires more careful firewall and routing configuration.

  • Explicit proxy settings: Set an application’s proxy environment variables or proxy options to the host’s reachable proxy address. This is easy to test and usually the best choice for a small number of containers. It only affects applications that honor those settings, and UDP traffic typically needs separate handling.
  • Transparent proxying: Use a Clash/Mihomo transparent listener and Linux firewall rules to intercept selected bridge traffic. Applications need no proxy configuration, but the host must correctly handle packet marks, policy routing, exclusions, and DNS.

This article focuses on transparent proxying on a Linux Docker host. The example assumes a bridge named br-apps, a container subnet of 172.20.0.0/16, and a host-side bridge address of 172.20.0.1. Replace these values with the actual interface and subnet on your machine. Find them with docker network inspect and ip addr; do not assume Docker uses the same subnet on every host.

Start with a narrow test: Use one test container and one dedicated Docker network before applying rules to every bridge. Confirm the proxy works from the host, then add container forwarding. This makes it much easier to separate a Clash configuration problem from a Docker or firewall problem.

Prepare Clash or Mihomo

The proxy core needs a transparent-proxy listener that the host firewall can deliver intercepted packets to. On Linux, Mihomo commonly uses a tproxy-port listener for transparent TCP and UDP handling. A redir-port listener is an alternative for TCP redirection, but it is not a drop-in replacement when you need transparent UDP forwarding. Check the capabilities of the core and client version you are running before choosing a listener type.

A minimal starting point for a Mihomo-style configuration looks like this:

Mihomo Transparent Listener Example
allow-lan: true
bind-address: 0.0.0.0
mode: rule
log-level: info

tproxy-port: 7893

# If DNS is enabled, configure it deliberately rather than
# assuming transparent TCP/UDP interception will handle DNS.
dns:
  enable: true
  listen: 0.0.0.0:1053
  enhanced-mode: fake-ip
  nameserver:
    - https://1.1.1.1/dns-query

The listener must be reachable from the Linux host’s packet-processing path. Binding to 0.0.0.0 can make it reachable on every host interface, so do not treat that setting as a firewall. Restrict access to the listener with host firewall rules, and avoid exposing the Clash controller or proxy ports to untrusted networks. If your core runs in another container, account for that container’s network namespace and port publishing; a listener bound only to that container’s loopback address is not reachable through the Docker host’s loopback.

Transparent interception is not the same as sending traffic to a normal HTTP proxy. Do not set container environment variables to the TProxy port. Applications using an explicit proxy need an HTTP or SOCKS listener; firewall-intercepted packets need the transparent listener and the matching Linux routing setup. Also verify that Clash’s rules and proxy groups have a suitable final rule. A working listener can still send traffic directly if the selected rule or policy group says DIRECT.

Enable Policy Routing and Interception

A transparent proxy cannot receive an intercepted packet merely because a firewall rule names its port. Linux must also route marked packets to the local machine instead of forwarding them toward the normal gateway. Policy routing provides that path: the firewall assigns a mark to selected packets, an ip rule matches the mark, and a route in a dedicated table treats the destination as local.

The following commands use mark 0x1 and routing table 100. Check for existing mark and table conventions on your host before adopting them, particularly if you use a VPN, another policy-routing service, or a container networking plugin.

Policy Routing Setup
sudo ip rule add fwmark 0x1/0x1 lookup 100
sudo ip route add local 0.0.0.0/0 dev lo table 100

# Verify the rule and local route
ip rule show
ip route show table 100

With nftables, create a dedicated table and a prerouting chain that runs before normal route selection. The example below intercepts TCP and UDP packets arriving from the chosen Docker bridge, excludes private and local destinations, then assigns the mark and sends eligible traffic to the transparent listener. The exact syntax and hook ordering can vary with your nftables version and firewall manager; inspect the active ruleset before installing it.

Example nftables Rules for One Docker Bridge
sudo nft add table inet clash_docker
sudo nft 'add chain inet clash_docker prerouting { type filter hook prerouting priority mangle; policy accept; }'

# Keep local, private, multicast, and reserved destinations out of the proxy path.
sudo nft add rule inet clash_docker prerouting iifname "br-apps" ip daddr 10.0.0.0/8 return
sudo nft add rule inet clash_docker prerouting iifname "br-apps" ip daddr 172.16.0.0/12 return
sudo nft add rule inet clash_docker prerouting iifname "br-apps" ip daddr 192.168.0.0/16 return
sudo nft add rule inet clash_docker prerouting iifname "br-apps" ip daddr 127.0.0.0/8 return
sudo nft add rule inet clash_docker prerouting iifname "br-apps" ip daddr 224.0.0.0/4 return
sudo nft add rule inet clash_docker prerouting iifname "br-apps" ip daddr 255.255.255.255 return

# Intercept the remaining TCP and UDP packets.
sudo nft add rule inet clash_docker prerouting iifname "br-apps" meta l4proto { tcp, udp } tproxy to :7893 meta mark set 0x1 accept

This is an example, not a universal firewall policy. Add exclusions for your actual LAN, Docker networks, VPN subnets, and any internal services that containers need to reach directly. If you use IPv6, write and test a corresponding IPv6 policy rather than assuming the IPv4 rules cover it. If the host uses UFW, firewalld, or a Docker-managed nftables backend, integrate the rules through that system’s supported mechanism. Manually adding rules to a live ruleset may work temporarily but disappear on reboot or conflict with rules recreated by Docker.

Forwarding and interception are separate concerns. Docker normally installs rules to permit traffic between its bridges and the outside network, but host firewall policies can still block forwarding. Check the active policy and counters rather than changing the default forwarding policy blindly. The goal is to allow the intended container traffic to reach the host’s interception path while preserving Docker’s isolation between unrelated networks.

Do not capture every packet from every interface: A broad rule can intercept host-originated traffic, proxy outbound connections, LAN access, container-to-container traffic, and management traffic. Match the Docker bridge interface and, where useful, the container source subnet. Keep the proxy’s own outbound connections outside the interception rule to prevent routing loops.

Handle DNS and Subnet Exclusions

DNS is often the reason a container appears to bypass the proxy even when its TCP connections are being intercepted. Docker commonly provides containers with an embedded resolver at 127.0.0.11. A container sends DNS queries to that resolver, and Docker forwards them upstream. That forwarding is not necessarily an ordinary packet arriving from the container bridge, so a prerouting rule that matches only iifname "br-apps" may never see the upstream DNS request.

First, inspect the resolver configuration inside the container with cat /etc/resolv.conf and verify which process or service is answering the query. Then choose a DNS design deliberately:

  • Keep Docker’s embedded DNS: Configure the daemon or the individual container’s upstream DNS settings, and understand that Docker may forward those requests from the host side. Ensure the selected resolver is reachable and that the intended DNS policy applies to it.
  • Use a reachable DNS service on the host: Run a DNS listener on a host address reachable from the bridge and configure the container to use it. If the DNS service listens on a nonstandard port, ordinary resolver settings cannot specify that port; use an explicit DNS forwarder or an appropriate firewall redirection.
  • Use application-level proxy resolution: For applications with HTTP or SOCKS proxy support, enable remote name resolution when available. This can avoid a separate local DNS lookup for proxied destinations, but behavior varies by application and proxy protocol.

Do not assume that enabling Mihomo’s DNS section automatically captures Docker DNS. Its DNS listener and the transparent traffic listener serve different roles. If you redirect DNS packets to a dedicated DNS port, make the rule specific to DNS traffic and verify both UDP and TCP port 53 behavior. Large DNS responses may use TCP, and a rule that handles only UDP can produce intermittent failures that are difficult to reproduce.

Private and local destination exclusions are equally important. Containers may need to access the Docker host, databases on a LAN, service-discovery addresses, or other bridge networks directly. Proxying those destinations can break internal applications or send private traffic to an unintended exit node. At minimum, review the host’s LAN CIDR, all Docker network CIDRs, loopback, link-local, multicast, and any organization-specific private ranges. Avoid hard-coding a sample subnet without checking for overlaps.

Validate Connectivity and Diagnose Failures

Test from the host outward in layers. First confirm that the Clash/Mihomo process is running and that its listener exists. Next, confirm that the host can reach the listener and that the chosen proxy group can establish an outbound connection. Only then test from a container. This sequence prevents a failed node, invalid subscription, or incorrect rule from being mistaken for a Docker firewall issue.

  1. Check the Docker network: Run docker network inspect and record the bridge name, subnet, and gateway. Confirm that the container has the expected address and route with ip addr and ip route inside the container.
  2. Check the listener: Use ss -lntup on the host to confirm the transparent port is open on the expected address. A listener on 127.0.0.1 alone may not receive traffic redirected from a bridge interface.
  3. Check policy routing: Inspect ip rule show and ip route show table 100. The marked packet must match the intended rule and be routed locally. Confirm that the mark value in nftables is exactly the value used by ip rule.
  4. Check firewall counters: Run sudo nft list ruleset and inspect packet counters on the interception rules. If counters stay at zero, the interface name, hook, address family, or rule order is probably wrong.
  5. Test an external address and a domain: A connection to a known IP helps separate routing from DNS. A domain test adds resolver behavior. Use a destination you are authorized to access and compare the result with the host’s behavior.
  6. Check Clash logs and connections: Look for the container’s destination, the matched rule, and the selected proxy group. If the packet reaches the core but goes direct, investigate rules and group selection rather than adding more firewall rules.

A common redirect loop occurs when the proxy’s own outbound socket is captured by the same rule as container traffic. The proxy connects to a remote node, that connection is intercepted and sent back to the proxy, and the cycle repeats until connections time out or resources are exhausted. Matching only packets entering through the Docker bridge prevents many host-originated loops. If your design also intercepts host traffic, explicitly exclude the proxy process or its outbound mark and verify the behavior with connection logs.

If containers can reach IP addresses but domains fail, focus on DNS rather than the transparent TCP listener. If neither IPs nor domains work, check the bridge interface match, listener binding, routing table, firewall forwarding, and Clash rule result. If only LAN services fail, review subnet exclusions. If one protocol works and another does not, confirm that the selected listener and rules support that protocol; a TCP-only redirect setup will not transparently handle UDP in the same way as TProxy.

SymptomLikely AreaFirst Check
Container cannot reach any external IPForwarding, interception, or policy routingFirewall counters, ip rule, and the local route table
IP connections work, domain lookups failDocker DNS or upstream resolverContainer /etc/resolv.conf and the configured DNS path
External traffic works, LAN service failsMissing destination exclusionLAN and Docker subnet CIDRs in the bypass rules
Proxy connections repeatedly time outPossible interception loopWhether proxy-originated outbound traffic is captured
Only TCP worksListener or firewall protocol coverageWhether the configuration uses TProxy and handles UDP

Make the Setup Persistent and Safe

Commands entered with ip and nft usually change the running system; they do not automatically become a reliable startup configuration. Save the firewall rules through the host’s nftables service or firewall manager, and arrange for the policy-routing commands to run after the relevant network interfaces are available. Docker may recreate bridges during restart, so verify that the bridge name and address remain stable. A dedicated Docker network with a known subnet can make the rules easier to maintain.

Apply changes in stages and keep a way to recover remote access. A firewall error can lock you out of a server, especially when the same host provides SSH, routing, or container management. Before enabling the rules permanently, save the current firewall state, test from a separate session, and ensure the rollback procedure is understood. After a Docker or system update, recheck the firewall backend and hook ordering rather than assuming the old rules still apply.

For a dependable deployment, document the bridge name, CIDR, mark, routing table, listener port, DNS design, and excluded networks together. Monitor Clash logs and firewall counters after restarts. A transparent proxy is easiest to troubleshoot when each layer has one clear responsibility: Docker supplies the bridge, nftables selects eligible packets, policy routing delivers them locally, and Clash applies the proxy rules.

FAQ

Do containers need proxy environment variables?

No. Transparent interception is designed to route eligible packets without application proxy settings. Environment variables are useful for explicit HTTP or SOCKS proxying, but they are a separate method. Do not point an application at a TProxy listener as though it were a normal HTTP proxy.

Why does Docker DNS bypass the interception rule?

Docker commonly gives containers an embedded resolver at 127.0.0.11 and forwards queries upstream itself. The upstream request may not arrive through the bridge interface matched by your nftables rule. Inspect the container resolver configuration and configure Docker’s DNS path separately, or use a reachable DNS service designed for the bridge.

Can I proxy multiple Docker networks?

Yes. Add each intended bridge interface or define a carefully scoped match for the relevant Docker interfaces and source subnets. Keep private, inter-container, host, and LAN destinations excluded where appropriate. Test one network at a time, since broad interface matching can unintentionally capture management or service traffic.

How do I avoid a proxy loop?

Prefer rules that intercept only packets entering from the designated Docker bridge. If host-originated traffic is also intercepted, exclude the proxy’s own outbound connections or use a dedicated mark policy that distinguishes them. Confirm the result in Clash logs and firewall counters before making the configuration persistent.

Once the container route, DNS behavior, and local-network exclusions have all been verified, save the rules through your host’s firewall management system and repeat the tests after a restart. That final check turns a working experiment into a maintainable setup. If you also want to manage Clash profiles and proxy groups from a supported client, start with the platform download and setup resources below.

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 →