mihomo 配置文件参考
YAML 结构、端口、DNS、节点与分流规则。先确认字段所在层级,再检查引用和运行结果。
本页面向已经能启动客户端、需要理解配置行为的用户。首次安装、导入订阅与连接验证,请先阅读快速上手;遇到具体字段、覆盖顺序或服务运行差异,再回到本页按章节查阅。示例以 Linux 上直接运行 mihomo 为基准,图形客户端可能额外生成端口、权限和覆写设置。
YAML 结构与配置目录
映射、列表与缩进
config.yaml 是一份有层级的配置,不是按行执行的脚本。顶层的 dns、tun 是映射,下面继续放字段;proxies、proxy-groups、rules 通常是列表,使用短横线表示条目。缩进决定归属,建议统一使用两个空格,不能混入制表符。字段看起来拼写正确,却被缩进到错误的父项下面时,可能无法通过解析,也可能只表现为预期设置没有生效。
冒号后留一个空格,布尔值直接写 true 或 false,端口和间隔使用数字。密码、含特殊符号的名称、看起来像日期的文本建议加引号。井号在普通标量中可能开始注释,因此带井号的密码必须作为字符串书写。双引号字符串会处理反斜线转义,单引号更适合需要保留反斜线的文本;不要为所有内容机械套上双引号,再把路径中的反斜线意外解释成转义字符。
同一映射中不要重复声明相同键。文件上方写一段 dns,下方再写另一段,并不表示两段会自动合并。不同解析器可能拒绝重复键,也可能保留其中一项,不能依赖这种行为。追加规则时应找到原来的 rules 列表继续添加,而不是新建第二个同名列表。YAML 锚点可以减少重复文本,但不等于客户端的覆写机制,也不能替代对最终配置的检查。
先建立能验证的最小文件
下面是一份只做直连的最小配置。它能验证文件路径、解析器和监听端口,不依赖订阅或代理服务器。这里关闭 TUN,明确把控制接口和混合端口绑定到回环地址。测试成功只说明本地入口可用,不代表已经建立代理出口。后续添加节点时,保留这份最小配置作为排查基线;如果复杂配置启动失败,可以用它区分二进制、系统环境与配置内容三类问题。
mixed-port: 7890
bind-address: 127.0.0.1
allow-lan: false
mode: rule
log-level: info
external-controller: 127.0.0.1:9090
tun:
enable: false
rules:
- MATCH,DIRECT
直接运行内核时,建议显式指定工作目录和配置文件,避免从不同终端启动后使用了不同文件。下列命令中的 -d 指定数据目录,-f 指定配置路径,-t 执行配置测试。目录内还可能存在缓存、Provider 下载内容和地理数据,不能把它们全部当作配置正文。相对路径通常按内核工作目录解析,而不是按终端当前所在目录猜测;迁移服务时应重新确认这一点。
mkdir -p "$HOME/.config/mihomo"
mihomo -t -d "$HOME/.config/mihomo" \
-f "$HOME/.config/mihomo/config.yaml"
mihomo -d "$HOME/.config/mihomo" \
-f "$HOME/.config/mihomo/config.yaml"
区分源文件与运行文件
图形客户端可能维护订阅原文、用户覆写和生成后的运行文件。编辑缓存目录里的生成文件,常会在下次切换配置或更新订阅时被重建。先从客户端日志确认内核实际加载的路径,再决定修改来源。不要同时启动图形客户端内置内核和独立服务去监听同一端口;即使两个程序读取同名文件,它们也可能位于不同目录,拥有不同的工作目录、运行用户和环境变量。
配置报错时先读第一条错误和附近行号,不要从最后一行开始猜。上一个列表项缺少缩进,可能让解析器在后面的合法字段才停止。复制网页示例后,检查是否混入全角冒号、智能引号或不可见空格。建议每次只增加一个完整模块,再运行测试并保留可回退副本。需要按文件自然顺序复习,可结合阅读config.yaml 逐段拆解,但最终仍以本机内核的解析结果为准。
监听端口、访问范围与模式
端口是应用接入内核的入口
port 提供 HTTP 代理入口,socks-port 提供 SOCKS 入口,mixed-port 在一个端口同时接受两种协议。普通桌面部署选择混合入口即可,不必为了字段齐全把三类入口全部开启。程序连接代理时必须选择正确的协议和地址;把 HTTP 代理设置填成控制接口的端口,或者让程序使用已经关闭的 SOCKS 端口,都不会因为内核运行正常而自动修复。
allow-lan 决定是否允许局域网接入,bind-address 约束监听地址。仅供本机使用时优先保留回环绑定;给其他设备使用时,才结合实际网卡地址、防火墙和访问控制开放入口。绑定地址不是对所有端口的统一安全策略,尤其不能假定控制接口与 DNS 监听也会跟随变化。修改后用系统工具查看实际监听结果,比只看界面开关更可靠。
| 字段 | 用途 | 本机示例 |
|---|---|---|
mixed-port | HTTP 与 SOCKS 共同入口 | 7890 |
allow-lan | 是否接受局域网连接 | false |
bind-address | 代理入口绑定地址 | 127.0.0.1 |
external-controller | 管理接口监听地址 | 127.0.0.1:9090 |
log-level | 日志详细程度 | info |
规则、全局与直连模式的边界
mode: rule 按规则选择出口,是长期使用时较容易解释的状态。global 将一般流量交给全局策略,direct 主要用于直连验证。模式控制的是内核如何处理已经进入内核的流量,不负责让浏览器、命令行和后台进程自动接入。因此切到全局模式后,完全没有使用系统代理的程序仍可能直连;这类现象首先检查接管路径,而不是继续更换规则。
图形客户端提供的模式切换可能通过管理接口立即改变运行状态,但未必同步改写订阅源文件。重启后恢复了旧模式,应检查客户端的持久化设置和配置生成逻辑,而不是在多个文件里重复添加 mode。同样,策略组的当前选择可能来自缓存,不一定等于列表的第一项。排查时记录文件值、界面值和实际连接记录,避免把三个层面的状态混为一谈。
mixed-port: 7890
allow-lan: false
bind-address: 127.0.0.1
mode: rule
log-level: info
external-controller: 127.0.0.1:9090
secret: "your-controller-secret"
管理接口用于读取状态、选择策略和加载配置,不是浏览器代理。需要控制功能时应设置自己的访问密钥,并把真实值保留在本机。不要把管理接口直接暴露到公网,也不要把密钥放入截图、公开配置或问题反馈。若确实需要远程管理,先采用受控的访问通道,并分别审查接口绑定、认证和防火墙;仅更换一个不常见端口,不能代替访问控制。
用明确的测试排除环境差异
先检查监听,再让单个命令显式使用代理。HTTP 入口和 SOCKS 入口应分别验证,后者使用 socks5h 时会把目标域名交给代理侧处理,便于与本地解析路径区分。测试目标、网络和出口不同,结果可能不同,因此保留错误原文,例如拒绝连接、握手失败或域名解析失败。不要只记录“打不开”,否则很难判断问题发生在接入之前还是出口之后。
ss -lntp | grep -E ':(7890|9090)\b'
curl --proxy http://127.0.0.1:7890 \
--connect-timeout 10 --head https://example.com
curl --proxy socks5h://127.0.0.1:7890 \
--connect-timeout 10 --head https://example.com
env | grep -iE '^(http|https|all|no)_proxy='
环境变量只对继承它们的进程生效,桌面设置也不保证被所有命令行工具采用。大写、小写变量及 NO_PROXY 可能共同影响结果;需要隔离测试时,清理当前命令的变量或使用明确参数,而不是永久修改整个系统。临时开启详细日志后应及时恢复,避免输出过多目标信息。端口已监听却仍失败时,可转到疑难解答,按代理入口、DNS、规则和节点的顺序继续定位。
DNS 上游、fake-ip 与解析路径
先确认查询有没有进入内核
dns.enable 开启内置解析器,但不会单独改变系统的 DNS 指向。桌面解析服务、路由器下发的地址和浏览器加密 DNS 都可能继续处理查询。只有系统显式使用内核监听地址,或者 TUN 等接管机制把查询转入内核,相关 DNS 配置才有机会生效。排查时先画出应用、系统解析器、内核和上游之间的路径,再讨论上游是否合适;跳过这一层容易反复修改没有被使用的字段。
listen 是 DNS 服务监听地址,与混合代理入口不同。示例使用回环地址上的高位端口,便于普通用户进行隔离测试,不要求直接接管系统的标准 DNS 端口。系统网络设置通常只接受服务器地址,不支持填写任意端口,因此这段示例不能直接抄进桌面 DNS 地址栏。实际接管需采用本地转发、TUN 劫持或其他明确方案,并检查系统解析服务是否已经占用目标端口。
dns:
enable: true
listen: 127.0.0.1:1053
enhanced-mode: fake-ip
fake-ip-range: 198.18.0.1/16
fake-ip-filter:
- "*.lan"
- "*.local"
default-nameserver:
- 223.5.5.5
nameserver:
- https://dns.alidns.com/dns-query
proxy-server-nameserver:
- 223.5.5.5
理解三类解析任务
nameserver 处理普通目标域名的解析;default-nameserver 主要用于解析 DNS 上游自身的域名,让加密解析服务能够先找到地址;proxy-server-nameserver 用于代理服务器域名。分开考虑这些任务,能避免代理连接依赖 DNS,而 DNS 又依赖尚未建立的代理的循环。示例中的公共解析服务只是可测试的配置选择,部署时仍应考虑所在网络的可达性和自己的解析策略。
fake-ip 返回用于映射域名的合成地址,连接进入内核后再恢复域名并分流。这些地址不是目标网站的真实服务器地址,看到它们本身不表示解析污染。映射所用网段也不应与本地网络规划冲突。若应用拿到合成地址却绕过内核发起连接,可能无法到达目标;因此 fake-ip 的有效性依赖 DNS 路径与连接路径一致,不能只验证一次查询就认为整体链路正确。
fake-ip-filter 让指定域名返回真实解析结果,常用于局域网发现或对地址行为敏感的应用。它不决定流量直连,也不是规则列表的替代品。域名进入过滤列表后,后续连接仍需按分流规则选择出口。排查局域网设备时,应同时检查名称解析、真实地址、局域网路由和规则,而不是把所有失败域名都加入过滤列表;过大的例外范围会使配置行为更难解释。
策略解析与缓存的边界
需要让部分域名使用指定上游时,可考虑 nameserver-policy;需要上游连接遵循分流规则时,再研究 respect-rules。后者会改变 DNS 连接本身的路由依赖,应同时明确代理服务器域名如何解析,避免自我依赖。不要把 fallback 简化理解为主服务器失败才启动的备用地址,它与过滤判断一起决定结果。初次部署优先保留清晰、可验证的单一路径,再逐项增加复杂策略。
dig @127.0.0.1 -p 1053 example.com A
dig @127.0.0.1 -p 1053 example.com AAAA
resolvectl status
resolvectl query example.com
直接查询内核与查询系统解析器,是两项不同测试。前者正常、后者异常,优先检查系统指向与转发;两者正常但浏览器异常,还需检查浏览器自己的加密 DNS、缓存和代理设置。修改配置后,应用缓存、系统缓存和内核映射可能仍保留旧结果,必须明确清理的是哪一层。长连接也可能继续使用原来的地址,重新打开页面不一定意味着建立了全新连接。
dns.ipv6 与顶层 ipv6 作用范围不同,不能靠只改其中一项来保证所有应用都不使用 IPv6。遇到只在双栈环境出现的故障,分别测试 A、AAAA 结果和实际连接地址,确认出口是否具备对应能力。更多逐项验证可参阅已连接却无法访问网页的排查清单。DNS 调整应有明确目标,不建议同时更换上游、增强模式和路由设置后再凭网页是否打开判断原因。
代理节点字段与订阅 Provider
节点定义的是出口连接参数
proxies 保存单个节点定义,每个条目至少需要一个可引用的 name 和协议类型 type,其余字段按协议补齐。名称不仅用于界面展示,还会被策略组和规则引用,应该稳定、唯一并便于识别。名称相似不代表节点相同;服务器地址、端口和认证参数才决定实际连接。更改名称后,应同步检查所有使用该名称的列表,不能只确认节点条目本身通过了解析。
以下使用 Shadowsocks 说明最基本的字段关系。服务器使用保留示例域名,密码也是教学文本,必须替换成服务提供方给出的真实参数才能连接。cipher 必须与服务器一致,不能因为某个算法看起来更新就自行替换。udp 表示相关能力开关,但不能让不支持 UDP 的服务端或网络自动获得该能力;应用协议、内核实现、服务器与途中网络都要满足条件。
proxies:
- name: SS-Example
type: ss
server: proxy.example.com
port: 443
cipher: aes-128-gcm
password: "your-password"
udp: true
不同协议可能使用密码、UUID、TLS 名称、传输路径或公钥等字段,不能把一种协议的参数原封不动套到另一种协议。连接地址与 TLS 验证名称也可能不同,填写 IP 地址并不意味着可以省略证书名称。遇到证书错误,先检查系统时间、服务器证书和名称是否匹配,不要把跳过验证当作通用修复。公开求助时应删除认证内容和订阅地址中的敏感参数,只保留必要的协议结构与错误信息。
Provider 把节点更新与主配置分开
proxy-providers 适合让主配置保留策略和规则,把易变的节点列表交给外部文件或订阅更新。Provider 名称与节点名称属于不同层级:策略组通过 use 引用 Provider,通过 proxies 引用具体节点或其他策略组。只声明 Provider 不会自动让所有规则使用它,仍需建立策略组并把流量导向该组。这种分离便于更新,但也增加了引用检查的必要性。
proxy-providers:
local-nodes:
type: file
path: ./providers/local-nodes.yaml
health-check:
enable: true
url: https://www.gstatic.com/generate_204
interval: 600
proxy-groups:
- name: PROXY
type: select
use:
- local-nodes
本地 Provider 文件需要符合节点集合的格式,例如在文件顶层放置 proxies 列表。主配置中的相对路径按工作目录解释,目录必须存在且服务运行用户能够读取。不要把包含端口、DNS 和规则的完整客户端配置随意当作节点文件使用;即使某些输入格式被兼容,也不应依赖未确认的转换行为。遇到列表为空时先打开实际文件,再看日志中的读取路径、格式错误和节点过滤条件。
远程更新、健康检查与错误分类
HTTP 类型的 Provider 还需要 url、缓存 path 和更新 interval。更新间隔与健康检查间隔是两个独立任务:前者重新获取节点定义,后者检测已有节点访问测试地址的结果。缩短其中一个,不会自动加快另一个。测试地址本身不可达时,健康检查可能集中失败,但不代表所有节点认证参数都失效;应使用实际业务目标进一步验证,避免仅凭一个探测结果批量删节点。
订阅更新超时、HTTP 状态异常、内容不是 YAML、文件可写权限不足,属于不同故障分支。先确定请求有没有得到响应,再检查返回内容与落盘结果。订阅地址可能具有账户访问能力,备份和日志应限制权限,不应上传到公开仓库。若更新需要已有代理,必须安排可用的启动路径,避免初次安装时因为没有节点而无法拉取节点;本地缓存可以帮助恢复,但不能代替稳定的获取方案。
更新成功也可能改变节点名称、地区标签和可用协议,进而影响策略组过滤与手动选择。维护时应比较节点集合的变化,不要把文件下载成功等同于策略链完整。订阅提供方与本站客户端文档属于不同职责,客户端不会凭空生成可用服务。需要安装程序时从客户端页面按平台选择;Windows、macOS、Android 与 iOS 可先查看 Clash Plus,Linux 图形部署可查看 Clash Verge Rev。更新错误的详细分支见订阅失败与定时刷新设置。
策略组类型、引用与选择状态
策略组连接规则和实际出口
proxy-groups 定义出口决策。规则可以指向一个组,再由组选择节点、另一个组或内置出口。这样规则只需引用稳定的业务名称,不必随着订阅节点更名逐条修改。策略组不是节点的简单文件夹,它的类型决定选择行为;把所有节点放进一个组,并不意味着内核会同时使用它们。设计前先回答是人工选择、按探测选择、顺序容错还是分配连接,再选择相应类型。
select 适合明确控制出口,由用户或控制接口选择列表中的一项。url-test 根据测试结果选择候选,fallback 主要按候选顺序寻找可用出口,load-balance 按策略分配连接。自动选择依据的是指定测试地址,不是所有业务的综合质量;某个节点探测较快,仍可能无法访问目标服务。阅读自动测试结果时,应把测试目标、测试方法与实际业务分开。
proxy-groups:
- name: AUTO
type: url-test
use:
- local-nodes
url: https://www.gstatic.com/generate_204
interval: 600
tolerance: 50
- name: PROXY
type: select
proxies:
- AUTO
- DIRECT
use:
- local-nodes
上面的配置依赖前一章的 local-nodes Provider。AUTO 从 Provider 获取候选,PROXY 既可选择自动组,也可手动选择节点或直连。组内保留 DIRECT 是显式的人工选择,并不表示代理节点失败后一定直连。若业务要求失败时停止而不是改变出口,应移除不符合要求的候选,并确认容错组的完整链路,不能靠名称里的“安全”或“固定”推断实际行为。
名称引用构成有向关系
proxies 中列出的名称必须真实存在,大小写和空格都需要一致。一个组可以引用另一个组,但不能形成循环,例如入口组引用自动组、自动组又引用入口组。循环会让选择无法落到实际节点,可能在测试时被拒绝,也可能在运行时造成异常。可把配置画成从规则到组、再到节点的箭头图;每条路径最终都应结束于具体节点或明确的内置出口。
组名与节点名不要相同,也不要把业务组命名成内置策略名。复杂名称可以加引号以明确字符串边界,但引号不会修复名称不一致。Provider 更新后,如果某个节点消失,手动选择可能失效或退回其他候选,应在客户端查看当前实际选项。保存选择状态的缓存有助于重启恢复,也会造成“调整列表顺序却没有切换出口”的误解;列表第一项和运行时选中项不是同一个概念。
| 类型 | 选择依据 | 需要注意 |
|---|---|---|
select | 手动或管理接口指定 | 检查当前保存的选项 |
url-test | 候选对测试地址的探测结果 | 测试结果不代表全部业务 |
fallback | 候选顺序与可用性 | 顺序会影响出口优先级 |
load-balance | 配置的连接分配策略 | 不是单连接带宽叠加 |
控制自动切换的影响范围
interval 控制周期探测,tolerance 可减少轻微差异造成的频繁切换,但不是连接质量保证。配置过于频繁的探测会增加网络请求,节点多时尤为明显。已有长连接通常不会仅因组选择变化就无缝迁移到另一个出口;测试切换结果时应建立新连接,并确认应用没有复用原来的会话。涉及登录态、地区判断或长时间任务时,应先明确能否接受出口变化。
负载均衡通常按连接或目标关系分配,不会把同一个下载连接拆分到多个节点来叠加带宽。不同连接如果从不同出口离开,可能触发目标服务的会话限制,因此应按业务拆组,而不是全站共用一个随机出口池。需要固定出口的服务可以引用单独的选择组,并在规则里提前匹配;一般浏览流量再使用自动组。组越少越容易维护,但过度合并会让一次切换影响不相关业务。
若组为空,按顺序检查 Provider 是否加载、过滤表达式是否筛掉所有节点、引用是否指向正确 Provider,以及节点是否被协议支持限制排除。若组有候选但业务失败,先手动选定一个节点并建立新连接,观察规则最终指向与出口错误。不要同时修改组类型、探测地址和节点过滤条件,否则无法判断哪个变化带来了结果。稳定的策略组应有清楚的用途、可解释的选择依据,以及明确的失败处理方式。
规则顺序、匹配语法与规则集
从上到下,先命中先处理
rules 是有顺序的列表,通常从上到下判断,命中后使用该条指定的策略。规则写在文件的哪个位置直接影响结果,不是把关键词堆齐就会自动选出最精确的一条。常见组织方式是把明确的例外放前面,业务域名和规则集放中间,范围较大的地址判断放后面,最后用 MATCH 收口。若兜底规则出现在前面,后面的条目一般没有机会参与匹配。
DOMAIN 精确匹配一个域名,DOMAIN-SUFFIX 匹配域名本身及其子域,DOMAIN-KEYWORD 按字符串关键词匹配。关键词规则范围容易超出预期,应该谨慎使用,不能把短词当作业务边界。普通域名规则不包含网页路径;同一域名下不同目录,不能仅靠域名规则区分。加密连接中可用的目标信息也受应用行为、DNS 路径与嗅探设置影响,不能假设每个连接都带有完整 URL。
rules:
- DOMAIN,example.com,DIRECT
- DOMAIN-SUFFIX,example.org,PROXY
- IP-CIDR,127.0.0.0/8,DIRECT,no-resolve
- IP-CIDR,10.0.0.0/8,DIRECT,no-resolve
- IP-CIDR,172.16.0.0/12,DIRECT,no-resolve
- IP-CIDR,192.168.0.0/16,DIRECT,no-resolve
- IP-CIDR6,::1/128,DIRECT,no-resolve
- IP-CIDR6,fc00::/7,DIRECT,no-resolve
- MATCH,PROXY
这段示例依赖已有的 PROXY 策略组,目标域名只用于展示匹配范围。私有地址直连规则适合常见本地网络,但是否覆盖隧道网络、容器网络和企业网段,应按现场路由判断。no-resolve 表示不为了这条地址规则主动解析目标域名,不是关闭整个 DNS 模块;如果连接已经有目标 IP,仍可以按地址匹配。把它删掉可能增加解析依赖,因此应理解需求后再调整。
规则集的格式必须与内容一致
rule-providers 将较长的规则集合独立存放,主规则通过 RULE-SET 引用。Provider 的 behavior 说明条目属于域名、地址还是传统规则语法,format 说明文件编码形式,两者不是同一个字段。声明为域名集合却填入完整的规则行,或者声明文本格式却提供带顶层映射的 YAML,都可能读取失败或无法按预期匹配。应同时检查声明和实际下载内容。
rule-providers:
business-domains:
type: file
behavior: classical
format: yaml
path: ./rules/business.yaml
rules:
- RULE-SET,business-domains,PROXY
- MATCH,DIRECT
对应的 business.yaml 文件如下。规则集中的条目只描述匹配条件,使用哪个出口由主文件中的 RULE-SET 行决定。这样同一份匹配集合可以在不同部署中采用不同策略,不必修改规则文件本身。维护时要给本地文件和远程文件使用不同路径,避免更新任务覆盖手写内容。文件存在但没有命中时,还应确认它在主规则中的位置,没有被前面的宽泛条目提前截走。
payload:
- DOMAIN-SUFFIX,example.org
- DOMAIN,api.example.net
先验证命中,再调整集合
远程规则集的更新失败不一定立刻改变当前行为,内核可能仍在使用已有缓存。反过来,首次启动没有缓存时,某个集合失败可能影响配置加载或匹配结果。因此应查看 Provider 的加载状态和错误日志,不要只根据本地文件修改时间判断。使用地理数据类规则时,还需确认相关数据文件可用、格式受支持且更新机制明确;规则语法正确,并不保证外部数据已经准备完成。
进程规则在 Linux 上可能需要进程发现能力和相应权限,容器、转发流量或不同用户运行的程序也可能无法获得同样的进程信息。遇到进程规则不命中,应先确认内核是否看得到进程,而不是不断改变进程名称拼写。源地址规则还要区分本机应用和局域网转发来源。规则匹配依赖实际可见的信息,路由或 NAT 改变了来源后,旧规则的前提也可能不再成立。
排查误分流时记录目标域名、目标地址、命中的规则、最终策略组与实际出口。先在列表顶部加入一条范围明确的临时规则,验证后再放回正确位置;不要一次性删除大批规则来“恢复网络”。规则更新后使用新连接复测,必要时清理相关缓存。对于某个站点涉及多个域名的情况,应按连接记录逐一确认,避免只处理首页域名而遗漏接口、静态资源或认证域名。
TUN 接管、Linux 权限与常驻服务
透明接管需要内核之外的条件
tun.enable 开启虚拟网络设备接管能力,auto-route 请求设置相关路由,auto-detect-interface 尝试识别出口网卡。它们解决的是流量怎样进入内核,不替代节点、DNS 和规则配置。先确保显式代理测试可用,再启用 TUN;否则入口、路由和出口同时发生变化,故障范围会扩大。桌面系统已经运行其他 VPN 或透明代理时,应先明确哪个程序负责默认路由与 DNS。
tun:
enable: true
stack: mixed
auto-route: true
auto-detect-interface: true
dns-hijack:
- any:53
stack 决定 TUN 使用的协议栈方案,支持情况与平台、构建和内核实现有关。示例使用 mixed,但不应把它当作所有环境的固定最优选择。dns-hijack 中的常见写法针对普通 DNS 查询,不会自动解密浏览器或应用的 HTTPS DNS。开启后仍需确认查询路径,尤其是系统本地解析服务、容器和应用自行配置的上游,不能仅看界面中的 TUN 开关。
权限不足时可能无法创建设备或添加路由。先检查系统是否提供 /dev/net/tun,再检查运行用户及能力设置。容器还受设备映射、网络命名空间与宿主机授权约束;容器内使用管理员身份,不代表宿主机已经授予所需网络能力。远程服务器上修改路由可能中断管理连接,应准备控制台或回退通道,先确认管理网段与现有 VPN 路由不会被错误接管。
能力授予必须对应实际二进制
对直接部署的内核,可在评估权限边界后授予必要网络能力。下面以用户目录中的固定二进制为例,先确认路径确实是将要运行的文件。能力附着于该文件,更换或重新安装二进制后可能需要重新设置。不要对来源不明的程序执行授权,也不要把授予网络能力理解为完成了所有隔离;目录写入权限同样重要,否则其他进程可能替换被授权的程序。
test -c /dev/net/tun
install -d "$HOME/.local/bin"
sudo setcap cap_net_admin,cap_net_bind_service=+ep \
"$HOME/.local/bin/mihomo"
getcap "$HOME/.local/bin/mihomo"
cap_net_admin 涉及网络管理,cap_net_bind_service 用于低位端口绑定;是否需要后者取决于实际监听端口。某些文件系统或服务安全限制会使文件能力无法按预期生效,因此检查能力输出只是第一步,还应观察启动日志。图形客户端可能通过自己的服务组件处理权限,不应同时套用另一套授权方案。若只需显式 HTTP 代理,可以保持 TUN 关闭,以减少权限与路由变化。
用户服务保持路径和日志可追踪
下面的用户服务使用固定工作目录和绝对配置路径,启动前执行配置测试,适用于前文已安装到用户目录的内核。systemd 不会把 ExecStart 当作交互式 shell 运行,因此不能依赖终端里的别名、临时变量或波浪号展开。示例中的 %h 由 systemd 展开为用户主目录。若部署为系统服务,应另行设计运行用户、目录权限和能力边界,不能仅把文件移动到系统目录。
# 保存为 ~/.config/systemd/user/mihomo.service
[Unit]
Description=mihomo user proxy service
[Service]
Type=simple
WorkingDirectory=%h/.config/mihomo
ExecStartPre=%h/.local/bin/mihomo -t -d %h/.config/mihomo -f %h/.config/mihomo/config.yaml
ExecStart=%h/.local/bin/mihomo -d %h/.config/mihomo -f %h/.config/mihomo/config.yaml
Restart=on-failure
RestartSec=5
[Install]
WantedBy=default.target
先创建服务目录并保存文件,再重新加载单元。服务启动后查看日志中的真实路径、权限错误和路由错误,不要只看进程是否存在。用户服务通常跟随用户管理器生命周期;需要退出登录后继续运行时,再评估是否启用用户驻留。驻留会改变后台任务的生命周期,应结合机器用途决定,而不是作为所有桌面安装的默认步骤。
mkdir -p "$HOME/.config/systemd/user"
systemctl --user daemon-reload
systemctl --user enable --now mihomo.service
systemctl --user status mihomo.service
journalctl --user -u mihomo.service -n 80 --no-pager
ip route
ip rule
TUN 开启后局域网或另一条 VPN 失联,应先停服务确认路由是否恢复,再比较启用前后的路由表与策略路由。不要在不清楚来源时批量删除路由,避免破坏系统网络管理器的状态。服务退出后仍异常,需要确认是否还有另一个内核实例、残留解析设置或客户端后台服务。路由器部署还涉及转发、防火墙与网关关系,可参阅OpenWrt 旁路由部署说明,不要把本机 TUN 示例直接当作旁路由完整方案。
覆写合并、变更测试与回退
区分 YAML 语法与客户端生成逻辑
覆写是客户端或外部工具对配置进行加工的过程,不是所有 YAML 文件天然具备的能力。订阅原文、本地覆写和界面设置可能按不同顺序合成运行文件,各客户端的合并规则也可能不同。因此不要假定“后写的字段一定覆盖前面”,更不要把某个客户端使用的扩展键直接交给独立内核。首先确认处理者是谁、输入文件有哪些、最终输出在哪里,再讨论某一项为什么被覆盖。
标量、映射和列表需要分别检查。一个端口数字通常容易替换,DNS 映射可能递归合并,也可能整段替换;规则与策略组列表可能追加、前插或完全重建。若工具把覆写中的 rules 整体替换到主配置,只有一条局域网规则的覆写会把原来的代理规则全部移走。反过来,简单追加到兜底规则后面,也可能让新增条目永远不命中。合并成功不等于顺序符合目的。
维护订阅配置时,建议由订阅负责节点,由本地配置负责稳定的端口、安全边界和业务策略。能够使用 Provider 分离的场景,优先让节点更新独立发生,减少每次更新整份文件带来的冲突。但这不是所有客户端都支持的统一操作步骤;图形界面可能重新生成相关段落,应在其提供的覆写或扩展入口中设置,并通过最终运行文件确认,不能只看编辑器保存成功的提示。
按一次只改变一个行为组织测试
变更前保留可回退副本,并记录当前模式、策略组选择和接管方式。只备份 YAML 不一定能恢复运行状态,因为客户端缓存、Provider 文件和系统代理设置也可能参与。涉及 DNS 或 TUN 时,还应记录原来的解析指向和路由信息。备份包含订阅或认证参数时应保留在受限目录,不要放进网站、共享同步目录或公开问题附件;对外提交差异前先去掉敏感内容。
cd "$HOME/.config/mihomo"
cp -p config.yaml config.yaml.before-change
cp -p config.yaml config.candidate.yaml
# 编辑候选文件后,先检查候选文件本身。
mihomo -t -d "$HOME/.config/mihomo" \
-f "$HOME/.config/mihomo/config.candidate.yaml"
diff -u config.yaml.before-change config.candidate.yaml
候选文件应放在同一工作目录测试,以便复用预期的相对路径,但配置测试仍可能访问 Provider 或生成缓存,不能把它视为完全隔离的沙箱。配置测试主要发现语法、字段和引用问题,不能证明远程节点可用、DNS 不循环或路由不会冲突。通过后继续检查差异,确认没有意外删除策略组、扩大监听范围或把管理接口改为外部地址,再安排实际切换。
确认候选文件后再替换正式文件,并按当前部署方式重新加载。对本文的用户服务,可以重启服务使启动前测试重新执行;如果使用客户端热重载,应确认它载入的是目标配置,而不是另一个订阅。重载成功后需要建立新连接验证,旧连接可能仍沿用先前的出口和解析结果。需要主动断开连接时应先考虑正在进行的下载、远程会话和其他用户任务,避免为了测试造成额外中断。
cd "$HOME/.config/mihomo"
cp -p config.candidate.yaml config.yaml
systemctl --user restart mihomo.service
journalctl --user -u mihomo.service -n 80 --no-pager
curl --proxy http://127.0.0.1:7890 \
--connect-timeout 10 --head https://example.com
# 需要回退时执行:
cp -p config.yaml.before-change config.yaml
systemctl --user restart mihomo.service
把验证拆成可归因的层次
第一层检查配置解析与进程启动,第二层检查代理、DNS 和控制接口的实际监听,第三层检查节点握手与目标访问,第四层检查规则命中和透明接管。只有上一层结果明确,下一层测试才容易解释。若显式代理可用而 TUN 不可用,优先查接管与权限;若节点域名无法解析,先查引导解析;若访问成功但出口错误,则查规则顺序和组选择,而不是反复重装客户端。
回退也要验证结果。恢复文件后检查服务是否真的重新加载、Provider 是否仍是更新后的内容、客户端是否再次覆盖了端口和 DNS。若故障只在订阅更新后出现,应保存脱敏后的结构差异,检查节点名称、协议字段与组过滤条件,而不是把更新任务永久关闭。能定位到具体变化时,维护记录只需写清修改目的、涉及字段、测试结果和恢复方法,不需要保存包含账户信息的完整日志。
长期维护应围绕实际需求减少不必要配置:不使用的入口保持关闭,业务例外有明确范围,远程规则集有可追踪来源,启动服务只有一个责任主体。新增字段前先查当前内核是否支持,并确认客户端不会在生成阶段移除它。若仍无法确定行为,回到快速上手中的最短验证链,或按疑难解答整理错误信息。配置手册用于解释字段与依赖,最终判断依据始终是本机加载的文件、日志和新建连接的实际结果。