Choose the Network Topology First: Main Router or Bypass Router
Running a Clash-family core on OpenWrt usually means deploying mihomo. mihomo retains Clash Meta’s configuration format and rule capabilities, reads YAML configuration directly, and provides mixed-port, DNS, TUN, rule providers, and an external control API. A router deployment differs from a desktop client: a desktop client handles only local traffic, while a router must also identify forwarded traffic from LAN devices and manage DNS, policy routing, and IPv4/IPv6 return paths.
Before deploying, decide what role the device will play in the network. In a main-router setup, OpenWrt handles the WAN connection, DHCP, DNS, and default gateway, making the traffic path clear and transparent proxy rules easier to manage centrally. In a bypass-router setup, the existing main router remains in place while OpenWrt acts as a second Layer 3 device on the same LAN, taking over only selected clients or traffic assigned through DHCP. A bypass router requires fewer changes, but the gateway, DNS, and return path must be checked individually.
Main-Router Topology
- With the ONT in bridge mode, the OpenWrt WAN port handles the PPPoE connection; in router mode, the OpenWrt WAN port receives an address from the upstream network.
- Set OpenWrt as the default gateway for LAN clients, for example
192.168.10.1. - OpenWrt provides both DHCP and DNS, so clients do not need a separate proxy address.
- TUN or TProxy rules can cover the entire LAN, or bypass selected sources by IP address, fixed DHCP lease mapped to a MAC address, or destination subnet.
Bypass-Router Topology
- The main router can use
192.168.1.1, while the bypass router uses a fixed address on the same subnet,192.168.1.2. - Set the bypass router’s default gateway and upstream DNS to the main router first, so it retains Internet access while updating subscriptions.
- Point the gateway and DNS of clients that need proxy access to
192.168.1.2; other clients can continue using the main router. - If the main router’s DHCP server can distribute a custom gateway, assign the bypass-router address centrally. Otherwise, configure clients manually or adjust the DHCP scope with care.
Hardware, Architecture, and Storage
Whether the core starts depends on matching the CPU architecture and runtime libraries; actual throughput also depends on single-core performance, cryptography, rule-set size, connection count, and network drivers. For two or three clients at around 100 Mbps, a dual-core ARM64 device with 256 MB of RAM can handle basic rule-based proxying. For targets above 500 Mbps with TUN, DNS fake-IP, and larger rule sets enabled, start with a quad-core ARM64 device and 512 MB of RAM. Gigabit connections also require checking NAT performance, NIC interrupts, and CPU softirq usage.
A configuration containing tens of thousands of rules and multiple rule providers commonly keeps mihomo’s resident memory around 80–180 MB; extensive domain rules, connection tracking, and dashboard queries can push usage higher. OpenWrt itself, along with dnsmasq, firewall4, and logging services, also needs memory, so 128 MB devices are generally suitable only for trimmed configurations. For flash storage, reserving at least 100 MB for the core, GeoIP, GeoSite, rule sets, and temporary downloads is a safer choice.
Confirm the CPU Architecture
SSH into OpenWrt and read the system report first. Do not choose a binary solely by the router’s product name.
uname -m
ubus call system board
cat /etc/openwrt_release
x86_64 corresponds to AMD64, and aarch64 corresponds to ARM64. Some older devices report armv7l, mips, or mipsel. MIPS also varies by endianness and floating-point implementation, so match each detail against the architecture label in the release files before downloading. With the wrong binary architecture, execution usually fails immediately with Exec format error.
Check TUN and Transparent Proxy Modules
ls -l /dev/net/tun
opkg list-installed | grep -E 'kmod-tun|firewall4|nftables'
nft list ruleset | head
TUN mode requires kernel TUN support; on OpenWrt, the usual package is kmod-tun. An nftables-based TProxy setup also needs the corresponding transparent-proxy module supplied by the current firmware, such as kmod-nft-tproxy. Package names vary by OpenWrt branch and target platform, so use the package feeds for the current device and do not mix kernel modules from repositories for other versions.
Place the mihomo Core and Create a Minimal Configuration
For a standalone deployment, place the executable at /usr/bin/mihomo and keep configuration and runtime data in /etc/mihomo. On devices with limited flash, rule sets can instead live on mounted USB storage, but the startup script must wait for the mount to be ready. The commands below assume the binary has already been uploaded via SCP to /tmp/mihomo.
mkdir -p /etc/mihomo
install -m 0755 /tmp/mihomo /usr/bin/mihomo
/usr/bin/mihomo -v
First create a bootable /etc/mihomo/config.yaml. Proxy nodes and proxy groups should come from your actual service configuration; the example below shows only the router-relevant listeners, control API, and DNS structure.
mixed-port: 7890
redir-port: 7892
tproxy-port: 7893
allow-lan: true
bind-address: "*"
mode: rule
log-level: info
ipv6: false
external-controller: 127.0.0.1:9090
secret: "change-this-controller-secret"
profile:
store-selected: true
store-fake-ip: true
dns:
enable: true
listen: 0.0.0.0:1053
ipv6: false
enhanced-mode: fake-ip
fake-ip-range: 198.18.0.1/16
fake-ip-filter:
- "*.lan"
- "localhost.ptlogin2.qq.com"
nameserver:
- https://1.1.1.1/dns-query
- https://8.8.8.8/dns-query
proxies: []
proxy-groups:
- name: PROXY
type: select
proxies:
- DIRECT
rules:
- GEOIP,CN,DIRECT
- MATCH,PROXY
allow-lan: true lets LAN clients access the proxy listeners, so the OpenWrt firewall should allow trusted LAN traffic to ports 7890, 7892, 7893, and the DNS listener only. The control API example binds only to 127.0.0.1:9090; if the dashboard runs on another device, bind it to a LAN address and set a strong random secret with explicit firewall source restrictions.
After saving, validate the syntax and then start mihomo in the foreground. -d sets the working directory; mihomo reads config.yaml and stores cached data there.
/usr/bin/mihomo -t -d /etc/mihomo
/usr/bin/mihomo -d /etc/mihomo
Once the configuration finishes loading, check the ports from another SSH session:
ss -lntup | grep -E '7890|7892|7893|9090|1053'
logread -f
Choosing Between TUN and TProxy
Both TUN and TProxy provide transparent access for clients, but they use different processing paths. TUN lets mihomo create a virtual network interface and pass IP traffic entering that interface to a user-space protocol stack. TProxy uses firewall marks, policy routing, and transparent sockets to send TCP or UDP traffic to a designated listener while preserving the original destination address. Neither is enabled by simply flipping one setting; the router must also match the forwarding and firewall structure of the current OpenWrt release.
TUN: A More Centralized Deployment Path
mihomo’s TUN configuration can be written directly in YAML. On OpenWrt, the system stack is commonly used with auto-route and automatic detection of the outbound interface:
tun:
enable: true
stack: system
device: mihomo
auto-route: true
auto-detect-interface: true
strict-route: true
dns-hijack:
- any:53
mtu: 1500
TUN keeps most rules in the core configuration and handles TCP and UDP consistently, making it friendlier to game consoles, TVs, and apps without manual proxy support. The trade-off is that traffic passes through a user-space virtual interface, so CPU usage may be higher on low-powered routers than with a carefully tuned TProxy setup. If some sites time out while small packets work, test by gradually lowering the MTU from 1500 to 1480, 1460, or 1400. Choose the final value based on PPPoE, tunnels, and the upstream network.
TProxy: Finer-Grained Control
TProxy is better suited to maintainers familiar with nftables, policy routing, and OpenWrt firewall4. A typical traffic path filters LAN traffic in the prerouting chain, skips LAN and reserved addresses, applies a firewall mark to target connections, uses ip rule to send marked traffic to a local routing table, and then redirects it to mihomo’s tproxy-port: 7893. UDP must be included separately as well.
OpenWrt 22.03 and later use firewall4 and nftables by default. iptables commands, chain names, and startup timing from older guides cannot be applied directly to an nftables environment. When writing TProxy rules manually, also avoid processing router-originated subscription downloads, NTP, package updates, and node connections twice, or you may create a proxy loop. In practice, use a management component that matches the current OpenWrt release to generate the rules, then inspect the result with nft list ruleset.
How to Choose
- Need broad coverage for TCP, UDP, and most LAN devices? Evaluate TUN first.
- Have a low-powered device but can manage nftables precisely? Consider TProxy.
- Only proxying a browser and a few computers? Start with
mixed-port: 7890instead of adding transparent-proxy complexity immediately. - Need per-client routing? Either approach can filter by source IP, provided DHCP assigns clients fixed addresses.
- Using IPv6? Design IPv6 DNS, routing, and firewall policies at the same time. Handling only IPv4 does not ensure every connection passes through the proxy.
Subscription Updates: Download, Validate, and Replace Atomically
On a router, subscription management does not end when a URL is entered in a GUI. At minimum, the update process should download to a temporary file, validate the YAML, replace the active configuration, and reload the service. Overwriting config.yaml directly means an interrupted download or an HTML error page from the server could leave the next restart unable to load the configuration.
The script below uses uclient-fetch to download the complete configuration. Store the subscription URL in a file readable only by root or directly in the script, and never print it to public logs. Save the script as /usr/bin/update-mihomo, then run chmod 700 /usr/bin/update-mihomo.
#!/bin/sh
set -eu
CONFIG_DIR="/etc/mihomo"
TEMP_FILE="/tmp/mihomo-config.yaml"
SUB_URL="https://subscription.example/path"
uclient-fetch -q -O "$TEMP_FILE" "$SUB_URL"
/usr/bin/mihomo -t -f "$TEMP_FILE"
cp "$CONFIG_DIR/config.yaml" "$CONFIG_DIR/config.yaml.bak"
mv "$TEMP_FILE" "$CONFIG_DIR/config.yaml"
/etc/init.d/mihomo reload || /etc/init.d/mihomo restart
mihomo -t -f parses the specified configuration file and can catch YAML indentation, field-structure, and some rule errors. It cannot confirm that every remote node is reachable, so check service logs and proxy-group status after updating. If the subscription requires a specific User-Agent or redirects, adjust the download command according to the provider’s requirements.
Once manual updates are stable, add a cron job. For example, update once daily at 04:17:
17 4 * * * /usr/bin/update-mihomo >> /tmp/mihomo-update.log 2>&1
OpenWrt’s /tmp is a tmpfs, so update logs are cleared on reboot; this helps avoid long-term flash writes. Limit log size during troubleshooting, and do not continuously write debug-level logs to flash in long-running deployments.
Use procd for Automatic Startup
OpenWrt services should be managed by procd rather than by appending background commands to /etc/rc.local. procd tracks the process, restarts it after abnormal exits, and provides consistent start, stop, and reload handling. Create /etc/init.d/mihomo:
#!/bin/sh /etc/rc.common
START=99
STOP=10
USE_PROCD=1
start_service() {
procd_open_instance
procd_set_param command /usr/bin/mihomo -d /etc/mihomo
procd_set_param respawn 3600 5 5
procd_set_param stdout 1
procd_set_param stderr 1
procd_set_param limits nofile="65535 65535"
procd_close_instance
}
reload_service() {
procd_send_signal mihomo HUP
}
service_triggers() {
procd_add_reload_trigger "network"
}
Grant execute permission and enable the service:
chmod 755 /etc/init.d/mihomo
/etc/init.d/mihomo enable
/etc/init.d/mihomo start
/etc/init.d/mihomo status
logread -e mihomo
respawn 3600 5 5 handles abnormal exits within the monitoring window and delays restarts, preventing a tight crash loop. If remote rule sets in the configuration require network access, the first start may occur before the WAN is ready. Preload essential rule files locally or run the update task separately after connectivity is established; do not depend on every startup immediately fetching all remote resources.
Routing, Security Boundaries, and Common Failures
Reserved Addresses Must Bypass the Proxy
Transparent proxy rules should skip the local loopback, LAN, multicast, and reserved ranges, such as 127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, and 224.0.0.0/4. If your home network uses multiple private subnets, define which ones can communicate through static routes. Blindly proxying NAS devices, printers, and admin pages can send traffic on an unnecessary detour or even make the router’s management interface unreachable.
Expose the Control API Only to the Management Network
The 9090 control API can switch policies, read connections, and change runtime state, so it should bind to loopback by default. If the dashboard must be accessible from the LAN, bind it to the router’s LAN address, such as 192.168.10.1:9090, set a secret, and restrict sources with the firewall. Proxy ports should likewise be open only in the LAN firewall zone and should not accept connections from the WAN.
Clients Still Cannot Access the Internet After the Core Starts
- Run
ip routeon the router and confirm that the default route points to the actual WAN. - Run
nslookup openwrt.org 127.0.0.1to confirm that basic DNS is working. - Connect a computer manually to the router’s port 7890 to rule out node and subscription problems.
- Run
nft list rulesetand confirm that the transparent proxy chain exists and its counters are increasing. - Check that the client’s gateway and DNS both point to the planned main-router or bypass-router address.
Domestic Sites Work, but Some Overseas Sites Time Out
First check whether the proxy group has selected a working node, then compare DNS results with rule matches. If only large pages, uploads, or video connections fail, test the MTU. PPPoE links are typically 8 bytes smaller than Ethernet, and additional tunnels reduce it further. Reduce the packet size gradually and observe the result, but do not attribute every failure to MTU.
The Router Can Connect, but LAN Clients Cannot
This usually means mihomo’s own outbound connection works, but the LAN forwarding path is not entering the transparent proxy. On a main router, check LAN-to-WAN forwarding and transparent proxy rules; on a bypass router, check net.ipv4.ip_forward, firewall zones, and the client gateway. If clients still use the main router as their gateway, their traffic will naturally bypass the bypass router.
The Service Exists After Reboot, but the Rules Are Inactive
Service startup and firewall loading can occur in different orders. If a standalone script generates TProxy rules, integrate it with firewall4’s include or hotplug mechanism and ensure repeated execution does not create duplicate chains. With TUN auto-route, check startup logs for interface-creation, routing-table, or permission errors, and confirm that /dev/net/tun is available during startup.
Deployment Acceptance Checklist
The goal of running a core directly on a router is not merely to show the process as “running,” but to make the entire traffic path predictable and recoverable. Before handing over the home network, verify each item below:
- The core architecture matches the device, and
mihomo -vreturns version information normally. mihomo -tpasses configuration validation, and a failed subscription update never overwrites the existing configuration.- The standard proxy port 7890 has been tested separately, and nodes and proxy groups can establish connections.
- The gateway, DHCP, and DNS responsibilities of the main or bypass router are clear, with no conflict between two DHCP services.
- Only one primary interception path, TUN or TProxy, is enabled to avoid redirecting the same connection twice.
- The LAN, router management addresses, multicast traffic, and required reserved subnets bypass the proxy.
- The control API has an access key, and the firewall restricts it to a trusted management network.
- After rebooting the device, the WAN, time, DNS, mihomo, and transparent proxy rules recover in the correct order.
- Test web browsing, video, UDP applications, LAN NAS access, and printer access separately.
- During throughput tests, record CPU, memory, softirq usage, and temperature instead of looking only at a single bandwidth result.
For a first deployment, start with a standard mixed port and one test client, then expand to TUN or TProxy. A main-router setup has a more direct path and suits centralized control; a bypass-router setup enables gradual migration and works well for covering a small number of devices first. Whichever topology you choose, validate configuration syntax, DNS paths, firewall rules, and reboot recovery independently.