基于 Mihomo + Docker 实现旁路由
背景
之前折腾过 OpenWrt + OpenClash,用起来不顺手——OpenWrt 太重,OpenClash 配置繁琐,隔三差五出点小问题。Sing-box 看过一眼,功能确实新,但迭代太快,还没到能放心用的程度。最后决定直接拿 OpenClash 的内核 Mihomo 自己搭,省去中间那层。
触发这次折腾的直接原因是 Vibe Coding。AI 自动跑任务时,npm 装包、Docker 拉镜像、调 LLM API,哪个卡住都得手动去处理,烦死了。手动写代码偶尔挂个代理还好说,AI 自动跑的时候网络一抖就全乱了,得有个稳的底座。
Docker + Macvlan + Mihomo + TUN 这套组合,比在 OpenWrt 上再加一层要干净得多:不需要完整的 OpenWrt 系统,容器启停很快,Mihomo 本身也够成熟。
准备工作
一台能跑 Docker 的 Linux 主机(物理机、虚拟机都行)
Linux 内核 3.9+(基本都支持 macvlan)
准备好
config.yaml(下面有模板)
创建 Macvlan 网络
先开 IP 转发,这个不开旁路由就是摆设:
# 临时开启(重启后失效)
sysctl -w net.ipv4.ip_forward=1
# 永久开启
echo "net.ipv4.ip_forward=1" > /etc/sysctl.d/99-ipforward.conf
sysctl -p /etc/sysctl.d/99-ipforward.conf
然后创建 macvlan 网络,按实际情况改 --subnet、--gateway 和 -o parent:
docker network create -d macvlan \
--subnet=192.168.1.0/24 \
--gateway=192.168.1.1 \
-o parent=eth0 \
macnet
有一个坑要注意:macvlan 模式下宿主机和容器默认不能互通。如果宿主机本身也要走代理,得加一个 shim 接口:
ip link add macvlan-shim link eth0 type macvlan mode bridge
ip addr add 192.168.1.10/24 dev macvlan-shim
ip link set macvlan-shim up
ip route add 192.168.1.11/32 dev macvlan-shim
这样宿主机通过 192.168.1.10 就能访问容器的 192.168.1.11。宿主机不需要代理就跳过。
准备配置文件
在 /root/mihomo/ 下建 config.yaml,下面是一份可以直接用的最小配置,代理信息替换成自己的:
# Mihomo Minimal Transparent Proxy Config
# Suitable for Docker macvlan + TUN model
# -----------------------------
# 代理配置(示例)
# -----------------------------
proxies:
- name: "SS1"
type: ss
server: aaa.mangege.com # 替换为你的代理服务器地址
port: 443 # 替换为你的代理端口
cipher: chacha20-ietf-poly1305
password: "aaa" # 替换为你的代理密码
udp: true
udp-over-tcp: true
udp-over-tcp-version: 2
ip-version: ipv4
# -----------------------------
# 代理提供者(示例)
# -----------------------------
proxy-providers:
my-provider:
type: http
path: ./providers/my-provider.yaml
url: https://example.com/provider.yaml # 替换为你的订阅链接
interval: 3600
health-check:
enable: true
url: http://www.gstatic.com/generate_204
interval: 300
# -----------------------------
# 基础配置
# -----------------------------
mixed-port: 7890
redir-port: 7892 # TCP 透明代理
tproxy-port: 7893 # UDP 透明代理
ipv6: true
allow-lan: true
unified-delay: false
tcp-concurrent: true
# -----------------------------
# 外部控制
# -----------------------------
external-controller: 127.0.0.1:9090
external-ui: ui
external-ui-url: "https://github.com/MetaCubeX/metacubexd/archive/refs/heads/gh-pages.zip"
# -----------------------------
# Geo 数据
# -----------------------------
geodata-mode: true
geox-url:
geoip: "https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip-lite.dat"
geosite: "https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geosite.dat"
mmdb: "https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/country-lite.mmdb"
asn: "https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/GeoLite2-ASN.mmdb"
# -----------------------------
# 其他设置
# -----------------------------
client-fingerprint: chrome
profile:
store-selected: true
store-fake-ip: true
sniffer:
enable: true
sniff:
HTTP:
ports: [80, 8080-8880]
override-destination: true
TLS:
ports: [443, 8443]
QUIC:
ports: [443, 8443]
skip-domain:
- "Mijia Cloud"
- "+.push.apple.com"
# -----------------------------
# TUN 设置(关键)
# -----------------------------
tun:
enable: true
stack: gvisor
mtu: 1500
dns-hijack:
- "any:53"
- "tcp://any:53"
auto-route: true
auto-redirect: true
auto-detect-interface: true
fake-ip-range: 198.18.0.1/16
# -----------------------------
# DNS 设置
# -----------------------------
dns:
enable: true
ipv6: true
enhanced-mode: fake-ip
fake-ip-filter:
- "*"
- "+.lan"
- "+.local"
- "+.market.xiaomi.com"
default-nameserver:
- tls://223.5.5.5
- tls://223.6.6.6
fallback:
- tls://1.1.1.1
- tls://8.8.8.8
nameserver:
- https://doh.pub/dns-query
- https://dns.alidns.com/dns-query
# -----------------------------
# 代理组
# -----------------------------
proxy-groups:
- name: "DEFAULT"
type: select
proxies: [SS1]
# -----------------------------
# 规则
# -----------------------------
rules:
- GEOIP,lan,DIRECT,no-resolve
- GEOSITE,CN,DIRECT
- GEOIP,CN,DIRECT
- DOMAIN-SUFFIX,ts.net,DIRECT
- DOMAIN-SUFFIX,tailscale.io,DIRECT
- DOMAIN-SUFFIX,tailscale.com,DIRECT
- MATCH,DEFAULT
server、port、password 换成自己的,用 proxy-providers 的话把 url 改成订阅链接,其余按需调整。更多选项看 Mihomo 官方文档。
启动容器
docker run -d \
--name mihomo \
--restart always \
--network macnet \
--ip 192.168.1.11 \
-v /root/mihomo/config.yaml:/root/.config/mihomo/config.yaml \
metacubex/mihomo:latest
--ip 指定容器在局域网里的地址,确认没被占用。-v 挂载配置文件,路径对就行。
配置说明
Macvlan 让容器拿到一个独立的局域网 IP,对局域网里其他设备来说它就是一台普通机器。TUN 模式在容器内建虚拟网卡,流量进来就被接管,应用层完全无感知。DNS 劫持把所有 53 端口的查询都拦下来,防止 DNS 泄露。
分流规则很直接:局域网直连,国内域名和 IP 直连,Tailscale 相关域名直连,剩下的全走 DEFAULT 代理组。
客户端这边需要手动把网关和 DNS 都改成 192.168.1.11,流量才能经过旁路由:
Windows:网络设置 → 更改适配器选项 → 右键以太网/WiFi → 属性 → IPv4 → 手动填 IP 和 DNS
macOS:系统偏好设置 → 网络 → 高级 → TCP/IP 手动配置,DNS 填
192.168.1.11Linux:用
nmcli或直接在/etc/resolv.conf加nameserver 192.168.1.11
安全方面几个要注意的:配置文件权限改成 chmod 600,Mihomo 的端口(7890、7892、7893、9090)不要暴露到公网,镜像记得定期更新。
性能方面,MTU 默认 1500,如果走的是 PPPoE 或者有封装开销,试试改到 1400。store-selected 和 store-fake-ip 已经开了,对性能有帮助。
验证
容器起来之后,先跑几个基础检查:
# 容器状态
docker ps | grep mihomo
# DNS 解析
nslookup google.com 192.168.1.11
# 连通性
ping 192.168.1.11
curl -x socks5://192.168.1.11:7890 https://httpbin.org/ip
再测分流是否正常:
curl -I https://www.baidu.com # 应该直连
curl -I https://www.google.com # 应该走代理
# 看日志确认
docker logs mihomo -f | grep -E "DIRECT|REJECT"
故障排查
容器起不来,先看日志:docker logs mihomo,多半是 config.yaml 语法错了。确认 macvlan 网络存在:docker network ls。容器 IP 冲突的话 ping 192.168.1.11 会有响应但容器没跑起来。
客户端上不了网,先确认网关和 DNS 都改对了,然后 nslookup google.com 192.168.1.11 测一下 DNS 通不通。
速度慢,先排查代理节点本身的问题,再试着调 MTU,用 docker stats mihomo 看看容器资源有没有跑满。
维护
# 重启
docker restart mihomo
# 改配置后重启
vim /root/mihomo/config.yaml
docker restart mihomo
# 通过 API 热重载(不停服)
curl -X PUT http://127.0.0.1:9090/configs -d '{"path":"/root/.config/mihomo/config.yaml"}'
# 更新镜像
docker pull metacubex/mihomo:latest
docker stop mihomo && docker rm mihomo
docker run -d --name mihomo --restart always --network macnet --ip 192.168.1.11 \
-v /root/mihomo/config.yaml:/root/.config/mihomo/config.yaml \
metacubex/mihomo:latest
# 备份配置
cp /root/mihomo/config.yaml /root/mihomo/config.yaml.backup
日志查看:
docker logs mihomo -f
docker logs mihomo --tail 100
docker logs mihomo --since 2026-03-25T21:00:00
整体跑下来,这套方案比 OpenWrt 那一套轻多了,折腾成本也低。把客户端网关和 DNS 指向容器 IP,剩下的 Mihomo 自己处理。