Environment and Installation
System Environment
Linux distribution: Ubuntu 24.04.4 LTS
Install Mihomo
The official Mihomo project is:
MetaCubeX/mihomoIt is itself a binary program compiled in Go, so it does not require Python or pip.
My server is linux amd64, and the installation is:
Mihomo Meta v1.19.30Download via GitHub CLI:
cd /tmp
gh release download v1.19.30 -R MetaCubeX/mihomo -p 'mihomo-linux-amd64-v1-v1.19.30.gz'After the download is complete:
gunzip mihomo-linux-amd64-v1-v1.19.30.gzInstall:
install -m 755 mihomo-linux-amd64-v1-v1.19.30 /usr/local/bin/mihomoCheck:
mihomo -vMy output:
Mihomo Meta v1.19.30 linux amd64 with go1.26.6 Sun Aug 16 10:02:19 UTC 2026Use tags: with_gvisorConfiguration Directory
Store all Mihomo data in one place:
/etc/mihomoCreate:
mkdir -p /etc/mihomoMain configuration:
/etc/mihomo/config.yamlMihomo is used at startup:
mihomo -d /etc/mihomoMihomo Configuration
Subscription Management
Nodes are not written directly into the main configuration, but are managed through proxy-providers:
proxy-providers: Airport Subscription: type: http url: "Your subscription URL" path: ./sublinks/airport.yaml interval: 86400
health-check: enable: true url: https://www.gstatic.com/generate_204 interval: 300This way, the main configuration handles rules, while the subscription handles nodes. After updating the subscription, there is no need to regenerate the entire configuration.
I use Sublink for subscription aggregation, so the directory is named
sublinks/.
Automatic Node Selection
- name: Automatic Selection type: url-test use: - Airport Subscription
exclude-filter: "(?i)套餐|到期|剩余|流量|重置|官网|文档|下载|通知"
url: https://www.gstatic.com/generate_204 interval: 300 tolerance: 50Manually select a region for overseas traffic:
- name: Overseas Websites type: select proxies: - Automatic Selection - Japan - Hong Kong - Singapore - United States - All Nodes - DIRECTAutomatically Filter Nodes by Region
Different countries and regions use url-test to select nodes.
For example, Japan:
- name: Japan type: url-test
use: - Airport Subscription
filter: "(?i)日本|东京|大阪|Japan|\\bJP\\b|🇯🇵"
exclude-filter: "(?i)套餐|到期|剩余|流量|重置|官网|文档|下载|香港|新加坡|台湾|美国"
url: https://www.gstatic.com/generate_204
interval: 300 tolerance: 20Other regions follow the same logic:
Hong KongJapanSingaporeUnited States...DNS and Fake-IP
Mihomo manages DNS itself:
dns: enable: true listen: 127.0.0.1:7874
ipv6: false prefer-h3: false
enhanced-mode: fake-ip fake-ip-range: 198.18.0.1/16
fake-ip-filter: - +.lan - +.local - time.*.com - ntp.*.comMainland China DNS:
nameserver: - https://120.53.53.53/dns-query - https://223.5.5.5/dns-queryAnd specify separately for Mainland China sites:
nameserver-policy: geosite:private: system
geosite:cn,steam@cn,category-games@cn,microsoft@cn,apple@cn: - 119.29.29.29 - 223.5.5.5 - 1.12.12.12 - 114.114.114.114On the server, limit DNS listening to:
listen: 127.0.0.1:7874Instead of:
listen: :7874This avoids accidentally exposing the DNS service.
Service Traffic Routing
In addition to Mainland China and overseas traffic, separate policy groups can be established for several commonly used services.
For example, GitHub:
- name: GitHub type: select proxies: - Overseas Websites - DIRECTGoogle:
- name: Google type: select proxies: - Blocked Websites - DIRECTOpenAI:
- name: openAi type: select proxies: - All Nodes - Blocked Websites - DIRECTYou can also route these services separately:
YouTubeNetflixTwitterTikTokFacebookDiscordOneDriveMicrosoftSteamCloudflare...Rule Provider
The rules themselves are not all written into config.yaml, but are instead managed through a remote Rule Provider.
For example:
GitHub-rule: type: http interval: 86400 behavior: classical format: yaml
url: https://cdn.jsdelivr.net/gh/blackmatrix7/ios_rule_script@master/rule/Clash/GitHub/GitHub.yaml
path: ./ruleset/blackmatrix7/GitHub.yamlThen:
rules: - RULE-SET,GitHub-rule,GitHubAd Blocking:
- RULE-SET,Ad Blocking-rule,Ad BlockingThe final fallback logic:
- RULE-SET,cncidr,DIRECT,no-resolve- RULE-SET,direct,DIRECT
- GEOSITE,gfw,Blocked Websites- GEOIP,CN,Domestic Websites
- MATCH,Blocked WebsitesThe overall logic is roughly:
Advertisements ↓REJECT
Domestic websites / CN IP ↓DIRECT
Overseas services ↓Proxy
Unrecognized traffic ↓ProxyFinally, use:
MATCH,Blocked WebsitesThere is a practical benefit: even if a foreign website is not in the GFWList, it will not therefore accidentally use a direct connection.
TUN Transparent Proxy
TUN Configuration
What truly makes Mihomo the core of the Linux Server system proxy is TUN.
My configuration:
tun: enable: true
stack: mixed
auto-route: true auto-redirect: false auto-detect-interface: true
dns-hijack: - any:53 - tcp://any:53
mtu: 1500 device: mihomo
strict-route: falseAmong these:
stack: mixedis relatively universal on current Mihomo.
After auto-route is enabled, system traffic will automatically enter the Mihomo TUN.
Excluding Docker, Tailscale, and the Internal Network
route-exclude-address: - 10.0.0.0/8 - 100.64.0.0/10 - 169.254.0.0/16 - 172.16.0.0/12 - 192.168.0.0/16Among these:
10.0.0.0/8172.16.0.0/12192.168.0.0/16is a common private network.
Docker is also typically in:
172.16.0.0/12Within the scope.
Tailscale uses:
100.64.0.0/10Therefore, after excluding these addresses:ablytyped
SSHDockerLocal Area NetworkTailscaleWill not have its routes rewritten by Mihomo TUN.
The overall structure becomes:
flowchart TD A[Linux Server] --> B[LAN / Docker / Tailscale] B --> C[DIRECT] A --> D[Internet] D --> E[Mihomo TUN] E --> F[Rule Matching] F --> G[DIRECT] F --> H[Proxy]
Proxy Ports on the Server
I keep a Mixed Port:
mixed-port: 9870Because even with TUN already enabled, an explicit proxy port is still convenient for testing, Docker, or certain programs.
For example:
curl -x http://127.0.0.1:9870 https://github.comIf Docker or the local network needs access, then:
allow-lan: truebind-address: "*"But do not directly use:
lan-allowed-ips: - 0.0.0.0/0Otherwise, a public server can easily become an open proxy by accident.
You can restrict it to:
lan-allowed-ips: - 127.0.0.0/8 - 10.0.0.0/8 - 172.16.0.0/12 - 192.168.0.0/16If only the local machine uses it, it is even simpler:
allow-lan: falsebind-address: 127.0.0.1Service Management
Persistent systemd Service
Create:
/etc/systemd/system/mihomo.serviceContent:
[Unit]Description=Mihomo Proxy ServiceAfter=network-online.targetWants=network-online.target
[Service]Type=simpleUser=root
ExecStart=/usr/local/bin/mihomo -d /etc/mihomo
Restart=on-failureRestartSec=3
LimitNOFILE=1048576
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICECapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE
NoNewPrivileges=true
[Install]WantedBy=multi-user.targetLoading:
systemctl daemon-reloadStarting:
systemctl enable --now mihomoView status:
systemctl status mihomo --no-pagerView real-time logs:
journalctl -u mihomo -fRestart:
systemctl restart mihomoConfiguration Check and Restart
mihomo -t -d /etc/mihomoAfter it passes:
systemctl restart mihomoMetaCubeXD WebUI
Mihomo itself provides a Controller API, but does not include a traditional management page.
Here we use MetaCubeXD.
Configuration:
external-controller: 0.0.0.0:9090
external-ui: /etc/mihomo/ui
secret: "Set your own password"Mihomo can also download it automatically:
external-ui: ui
external-ui-url: "https://github.com/MetaCubeX/metacubexd/archive/refs/heads/gh-pages.zip"Then:
mihomo -t -d /etc/mihomosystemctl restart mihomoTest the API:
curl http://127.0.0.1:9090/versionNormally, it will return something like:
{ "meta": true, "version": "1.19.30"}Test the WebUI:
curl -I http://127.0.0.1:9090/ui/It should return:
HTTP/1.1 200 OKIf you wish to use it via Tailscale, you can:
external-controller: 0.0.0.0:9090Final Network Structure
flowchart TD A[Ubuntu Server] A --> B[Private Network] B --> C[Docker / LAN] B --> D[Tailscale / SSH] C --> E[Does not enter TUN] D --> E A --> F[Internet] F --> G[Mihomo TUN] G --> H[Rule Engine] H --> I[DIRECT] H --> J[Proxy] J --> K[Automatically select node]
The management plane is:
flowchart TD A[Mac] -->|Tailscale| B[Ubuntu Server] B --> C[SSH] B --> D[9090/ui/] D --> E[MetaCubeXD]
Reference Materials
- mihomo
- sublink pro
- Clash Verge’s Extension Script (The yaml configuration is rewritten through this script; I can’t quite find the specific source of the configuration for the secondary development.)