On this page

mihomo Operations Manual

mihomo (clash meta) is a proxy rule engine. Core concepts: proxy (proxy node), proxy-group (node group, containing strategies like url-test/fallback), rule (traffic splitting rule), sniffer (protocol sniffing), fake-ip (fake DNS mapping). The typical configuration path used below is /opt/mihomo/mihomo_config/config.yaml.

Configuration Management

# True source location (the only manually edited configuration)
vi /opt/mihomo/mihomo_config/config.yaml

# Validate configuration syntax (must run before restart; syntax errors prevent service startup)
mihomo -t -f /opt/mihomo/mihomo_config/config.yaml
# OK: "configuration file is valid"
# NG: Specific error line number

# API hot reload (does not interrupt connections)
curl -X PUT 127.0.0.1:9999/configs?force=true \
  -H "Authorization: Bearer <secret>" \
  -d '{"path":""}'
# force=true: Attempt even if there are errors (not recommended, validate with -t first)

# Remote side router (e.g., OpenWrt Nikki):
# ssh <router> /etc/init.d/nikki reload
# Or: curl -X PUT <router_ip>:9090/configs?force=true

Logs and Monitoring

# Logs (default level: info)
tail -f /var/log/mihomo.log
journalctl -u mihomo -f

# API: Basic status
curl 127.0.0.1:9999 -H "Authorization: Bearer <secret>" | jq
# Returns: {"hello":"clash"}

# API: All proxy nodes + latency
curl 127.0.0.1:9999/proxies -H "Authorization: Bearer <secret>" | jq '.proxies[] | {name, type, history}'
# history: [{time, delay}] — recent latency samples

# API: Active connection count
curl 127.0.0.1:9999/connections -H "Authorization: Bearer <secret>" | jq '{upload,download,connections: [.connections[] | {src,dst,rule,chain}]}'

# API: Rule list
curl 127.0.0.1:9999/rules -H "Authorization: Bearer <secret>" | jq

Rule Debugging

# Enable debug logs (resets after restart)
# config.yaml: log-level: debug

# Key information in logs:
# [TCP] 192.168.1.100:52341 --> google.com:443 match MATCH using PROXY[Auto] --> HK-Hy2
# ↑ Source IP          ↑ Target Domain    ↑ Matched Rule   ↑ Group Used ↑ Final Node

# Common troubleshooting steps:
# 1. Rule not matching → Check if it's using Matcher (domain) or GEOIP (IP geolocation)
# 2. Using PROXY but node is unreachable → Check proxy history latency
# 3. Using DIRECT but expecting PROXY → Check if the domain is in the direct connection rules

Side Router Transparent Proxy Operations

# OpenWrt/Nikki system: Check nftables transparent proxy rules
nft list table inet nikki
# → Should include: dstnat redirect TCP → :7891, mangle markers → TUN

# Check failopen daemon (if deployed)
/etc/init.d/nikki-failopen status
# Function: Detect if mihomo is alive → Continuous failure → Remove transparent proxy rules → Whole house direct connection

# Check DNS chain
docker exec adguardhome nslookup google.com   # If DNS is in a container

Common Issues

# All nodes timeout
# → Check local outbound network: curl --proxy 127.0.0.1:7890 -I https://www.google.com
# → Check each node: curl 127.0.0.1:9999/proxies | jq '.proxies[] | select(.history|length>0) | {name, delay: .history[-1].delay}'

# Domain rules not taking effect
# → Is sniffer enabled? config.yaml: sniffer: enable: true
# → fake-ip mode: DNS returns fake IP (198.18.x.x) → Table lookup during connection → Match domain rules
# → redir-host mode: No fake-ip → Must sniff during connection — if not sniffing, domain rules fail

# DNS resolution anomalies
# → fake-ip range conflicts with hosts? Check if /etc/hosts contains 198.18.x.x
# → Upstream DNS unreachable? Check nameserver-policy and fallback DNS