How this page relates to the tutorial:the Usage Guide is a follow-along path to a working setup; this page is a field-by-field reference for editing configs, writing rules, and chasing down errors. See the download page for client installers, and the subscription import article for how to obtain and format subscription links.
Config File Structure Overview
Everything a Clash-family client does is governed by a single YAML config file. After you import a subscription link, what the client fetches from the subscription URL is essentially this file; switching nodes, changing modes, or adding rules in the UI all end up written back to the corresponding fields. Once you can read this file, every UI operation has a clear meaning.
The file is usually named config.yaml and lives in the client's config directory. GUI clients such as Clash Plus, Clash Verge Rev, and FlClash manage reads and writes for you: they generate it when a subscription is imported and rewrite it when options change. Before editing by hand, quit the client first or check its override policy, otherwise your edits may be overwritten wholesale by a UI operation. See Chapter 8 for details on override mechanisms.
Top-Level Fields at a Glance
The top level of a complete config is a set of sibling fields with no ordering dependency; write them in any order. Common fields and their roles:
| Field | Type | Role |
|---|---|---|
| port / socks-port / mixed-port | Integer | Local listening ports for HTTP, SOCKS5, and mixed proxy entry points |
| mode | String | Proxy mode: rule routes by rules, global proxies everything, direct connects directly |
| log-level | String | Log verbosity; set to debug temporarily when troubleshooting |
| dns | Mapping | Switch, upstreams, and resolution mode of the built-in DNS |
| proxies | List | Proxy node definitions, one item per node |
| proxy-groups | List | Proxy groups that organize nodes into selectable sets |
| rules | List | Routing rules, matched top to bottom |
| proxy-providers | Mapping | Subscription sources: external providers of node lists |
| rule-providers | Mapping | Rule-set sources: external providers of rules |
| external-controller | String | Listen address of the external controller, used by dashboards and APIs |
A minimal working config needs only three things: a listening port, at least one node, and at least one rule. All other fields have defaults, and the kernel runs on default behavior when they are absent. Configs generated by subscription converters tend to be fully populated; when trimming by hand, keep the backbone and skip the rest.
Four Hard Rules of YAML Syntax
YAML parsing is strict, and most "config won't start" failures come from formatting, not content. Hold these four lines:
- Indent with spaces only, never tabs; keep the indent width consistent at each level — two spaces is the common convention.
- Separate key and value with a half-width colon plus one space, as in
key: value; a missing space after the colon is a frequent error. - List items start with a hyphen plus a space; the hyphen may be flush or indented, but keep one style per file.
- When a value contains special characters such as colons, hash signs, or braces, wrap the whole value in double quotes; quote password and token fields as a rule.
config.yaml · Minimal skeleton
mixed-port: 7890
mode: rule
log-level: info
proxies:
- name: "Node A"
type: ss
server: ss.example.com
port: 8388
cipher: aes-128-gcm
password: "your-password"
rules:
- MATCH,Node A
This nine-line skeleton is already a bootable config: local port 7890 accepts proxy requests and all traffic is forwarded through the single node. Real subscription configs expand on it with more nodes, groups, and rules, but the skeleton stays the same. The following chapters detail each part field by field.
General Fields: Ports, Modes, and Switches
Top-level general fields control the kernel's overall behavior: which ports accept requests, how traffic is routed, how much is logged, and whether LAN sharing is allowed. GUI clients mostly map these fields to toggles on their settings pages; use this section as the reference when writing by hand.
Listening Ports
port is the HTTP proxy port, socks-port is the SOCKS5 port, and mixed-port is a mixed port that accepts both HTTP and SOCKS5 connections on one entry point. Current clients generally expose only the mixed port, and both the system proxy and browser extensions point to it. The three fields can coexist as long as the ports don't conflict; omit any entry you don't need and the kernel won't listen on it.
redir-port and tproxy-port serve Linux transparent proxying with iptables redirection. Desktop users rarely need them; they are enabled in router and gateway scenarios. See the Linux CLI deployment article for the setup process.
Running Mode
mode takes one of three values: rule routes traffic by the rule list and is the everyday mode; global hands all traffic to a built-in group named GLOBAL — the "Global Mode" in the UI; direct connects everything directly, effectively pausing the proxy. Switching modes in the UI just rewrites this field and hot-reloads it; the initial value in the config file sets the default mode at every startup.
Logging and External Control
log-level runs from quiet to verbose: silent, error, warning, info, debug. Use warning or info day to day; when tracing rule hits, temporarily switch to debug to see the matching process and result of every connection.
external-controller sets the listen address of the external control API; GUI clients route their connection panels, latency tests, and config hot-reloads through it. external-ui points to a set of static dashboard files — open the address in a browser to manage the kernel directly; secret is the API access key, and an empty value means no authentication.
LAN Sharing and Other Switches
With allow-lan set to true, devices on the same LAN can use this machine as a proxy gateway; bind-address restricts which interface to listen on, and the default * means all interfaces. Other common switches: ipv6 controls whether IPv6 is resolved and forwarded; unified-delay makes latency tests start timing at handshake completion so results are comparable across protocols; tcp-concurrent dials candidate nodes concurrently and picks the fastest; profile.store-selected remembers manual selections in each group so they don't revert to the default node after a restart.
| Field | Typical Value | Notes |
|---|---|---|
| mixed-port | 7890 | Mixed proxy entry; the desktop client convention |
| mode | rule | One of rule / global / direct |
| log-level | warning | Switch to debug temporarily when troubleshooting |
| allow-lan | false | Set true to share with LAN devices |
| external-controller | 127.0.0.1:9090 | Localhost only; switching to 0.0.0.0 requires a secret |
| unified-delay | true | Unifies latency test timing |
| tcp-concurrent | true | Dials candidate nodes concurrently |
| profile.store-selected | true | Remembers manual group selections |
config.yaml · General fields example
mixed-port: 7890
allow-lan: false
bind-address: "*"
mode: rule
log-level: warning
ipv6: false
external-controller: 127.0.0.1:9090
secret: ""
unified-delay: true
tcp-concurrent: true
profile:
store-selected: true
store-fake-ip: false
Boundaries of the control API:bound to 127.0.0.1, external-controller accepts only local connections; once changed to 0.0.0.0, any machine on the LAN can read configs and switch nodes. Always set a secret in that case, and only do so on trusted networks.
DNS Fields: Resolution Behavior and Upstreams
Routing rules depend on domain-to-IP mapping, and the DNS config decides when and by which upstream a domain is resolved — a prerequisite for rules to hit correctly. Typical symptoms of misconfiguration: sites that should be proxied are treated as direct, or pages stall noticeably before loading.
Basic Switches
dns.enable is the master switch for the whole section and defaults to false, in which case the kernel leaves resolution to the system and every other field here is inert. listen sets the built-in DNS server's address, used with TUN mode or when pointing the system DNS at this machine; ipv6 controls whether AAAA queries are answered.
Resolution Modes: fake-ip vs redir-host
enhanced-mode is either fake-ip or redir-host. In fake-ip mode, the kernel answers domain queries with a virtual address from fake-ip-range (default 198.18.0.1/16); the application connects to that virtual address, and when the connection arrives the kernel looks up the original domain, routes it by rules, and resolves the real IP. This skips application-side resolution waiting and keeps poisoned system DNS results from derailing routing. redir-host is the traditional mode: resolve the real IP first, then hand it to the application — best compatibility, slightly slower.
fake-ip-filter lists domains that should never get virtual addresses; matches go through real resolution. LAN domains, time servers, and protocols that need real IPs for service discovery belong here — otherwise you get oddities like missing LAN devices or a system clock that won't sync.
Upstream Servers
default-nameserver resolves "the DNS servers' own domains" — when an upstream is written as a domain like dns.alidns.com, a pure-IP upstream must resolve it first, so this layer must be IPs. nameserver is the main upstream list and supports UDP, TLS, and HTTPS forms. fallback is the backup upstream, used when resolution is judged to need the proxy. nameserver-policy assigns dedicated upstreams by domain or GEOSITE category — for example, pinning mainland China domains to the ISP's DNS and specific services to encrypted upstreams — with finer granularity than fallback.
config.yaml · DNS section example
dns:
enable: true
listen: 0.0.0.0:53
ipv6: false
enhanced-mode: fake-ip
fake-ip-range: 198.18.0.1/16
fake-ip-filter:
- "*.lan"
- "*.local"
- "time.*.com"
- "ntp.*.com"
default-nameserver:
- 223.5.5.5
- 119.29.29.29
nameserver:
- https://doh.pub/dns-query
- https://dns.alidns.com/dns-query
fallback:
- https://1.1.1.1/dns-query
- tls://8.8.4.4
GUI client defaults:Clash Plus, Clash Verge Rev, and FlClash all ship a built-in DNS config that needs no changes for daily use. When writing a full config by hand, enable must be explicitly set to true; listing upstreams without the switch is the most common hand-written-config omission.
Proxy Node Fields
proxies is the node list; each item describes one outbound node. Four fields are the floor for any protocol: name (node name), type (protocol), server (server address), and port (server port). The rest vary by protocol. Subscription-imported nodes are generated by the provider; hand-editing is mostly about adding backup nodes and tweaking individual fields.
Common Fields
name must be unique across the whole config — groups reference nodes by name, and duplicates make references unpredictable. udp controls whether the node forwards UDP traffic, which voice calls, some games, and QUIC rely on. sni sets the domain announced in the TLS handshake and usually must match the node's domain; skip-cert-verify set to true skips certificate verification and belongs only in test environments with self-signed certs. alpn and client-fingerprint fine-tune the TLS fingerprint; dialer-proxy names a front node, chaining this node behind another for relayed transit.
Per-Protocol Examples
config.yaml · Nodes in four protocols
proxies:
- name: "SS Node"
type: ss
server: ss.example.com
port: 8388
cipher: aes-128-gcm
password: "your-password"
udp: true
- name: "VMess Node"
type: vmess
server: vm.example.com
port: 443
uuid: 00000000-0000-0000-0000-000000000000
alterId: 0
cipher: auto
tls: true
servername: vm.example.com
network: ws
ws-opts:
path: /ray
headers:
Host: vm.example.com
- name: "Trojan Node"
type: trojan
server: tj.example.com
port: 443
password: "your-password"
sni: tj.example.com
skip-cert-verify: false
- name: "Hysteria2 Node"
type: hysteria2
server: hy2.example.com
port: 443
password: "your-password"
sni: hy2.example.com
skip-cert-verify: false
Required and extended fields differ considerably by protocol; the table below is a checklist for common ones. A misspelled field name makes the kernel report the exact line during startup validation — see Chapter 9 for locating errors.
| Protocol type | Required fields | Common extended fields |
|---|---|---|
| ss | server, port, cipher, password | plugin, plugin-opts, udp |
| ssr | server, port, cipher, password, protocol, obfs | protocol-param, obfs-param |
| vmess | server, port, uuid, alterId, cipher | tls, network, ws-opts, servername |
| vless | server, port, uuid | flow, tls, reality-opts, network |
| trojan | server, port, password | sni, alpn, network, grpc-opts |
| hysteria2 | server, port, password | sni, obfs, up, down |
| tuic | server, port, uuid, password | congestion-controller, alpn |
To verify node fields after a subscription import, check the parsed result in the client's config preview: whether the transport (network), TLS switch, and SNI are complete decides whether a node passes startup validation. When adding a node by hand, write only the required fields first, get the connection working, then add extended fields one by one — a much smaller search space when something breaks.
Protocol support depends on the kernel:newer protocols like vless, hysteria2, and tuic come from the mihomo kernel and are unsupported by the discontinued original Clash kernel. See the kernel comparison article for the lineage and differences; every maintained client on our download page ships with mihomo.
Proxy Group Fields
proxy-groups organize nodes into selectable sets. Rule targets are usually group names rather than node names: rules decide "which group gets the traffic," and the group decides "which node is currently in use." With the two layers decoupled, switching nodes never touches rules, and editing rules never touches nodes.
Group Types and Selection
| Type | How it selects | Use case |
|---|---|---|
| select | Manual selection, a dropdown in the UI | Main group; exits that need human judgment |
| url-test | Periodic speed tests; auto-picks the lowest latency | Auto-picking among multiple nodes in one region |
| fallback | First available node in list order | Primary/backup failover; stability first |
| load-balance | Spreads connections across members | Splitting heavy traffic across lines |
| relay | Chains nodes in listed order | Fixed multi-hop relaying |
Health-Check Fields
The url-test, fallback, and load-balance types rely on health checks. url is the test target, defaulting to http://www.gstatic.com/generate_204 — a 204 response means usable; interval is the test interval in seconds, and too-frequent testing wastes node traffic; tolerance is the latency tolerance in milliseconds — no switch happens when the gap between old and new best is below it, preventing flip-flopping; lazy set to true pauses testing while the group has no connections, handy for configs with many standby groups.
Where Group Members Come From
The proxies field lists member names directly — node names or other group names; nesting is allowed, and a "Node Select" group wrapping an "Auto Select" group is a common UI pattern. The use field references subscriptions declared in proxy-providers, pulling all their nodes into the group; pair it with a filter regex to keep only matching names, or exclude-filter to drop them.
config.yaml · Proxy groups example
proxy-groups:
- name: "PROXY"
type: select
proxies:
- "Auto Select"
- "Manual Node A"
- "Manual Node B"
- DIRECT
- name: "Auto Select"
type: url-test
use:
- mysub
filter: "Hong Kong|Taiwan"
url: http://www.gstatic.com/generate_204
interval: 300
tolerance: 50
lazy: true
- name: "Ad Block"
type: select
proxies:
- REJECT
- DIRECT
DIRECT and REJECT are two built-in outbounds that need no definition in proxies: DIRECT connects directly, and REJECT refuses the connection outright — commonly used to block ad and tracking domains.
With nested groups, the UI shows the outermost group, and the inner group's current selection appears as one of its members. Once "Auto Select" is nested inside PROXY, you just keep it selected in PROXY day to day while the inner group handles testing and switching; when you need a specific node, change the outer selection temporarily — the rule list never changes.
Rule Syntax and Matching Order
rules is the core of routing. When a connection is made, the kernel compares it against the list from the first entry down; on a hit, traffic exits via that entry's group and matching stops; if nothing hits, the final MATCH catches it. Order is priority — half the work of writing rules is ordering them.
The Three-Part Rule Structure
A rule is comma-separated into three parts: type, match content, and target group — e.g. DOMAIN-SUFFIX,example.com,PROXY. IP-type rules accept a fourth part, no-resolve: by default, matching a domain connection against an IP rule triggers a resolution first; an IP rule with no-resolve only matches connections that are already IPs and never resolves domains for it, saving DNS requests and keeping resolution results from skewing routing. GEOIP and IP-CIDR rules carry this suffix by convention.
Rule Type Cheat Sheet
| Type | Matches | Example |
|---|---|---|
| DOMAIN | Full domain, exact match | DOMAIN,api.example.com,PROXY |
| DOMAIN-SUFFIX | Domain suffix, subdomains included | DOMAIN-SUFFIX,google.com,PROXY |
| DOMAIN-KEYWORD | Any fragment in the domain | DOMAIN-KEYWORD,telegram,PROXY |
| GEOSITE | Domain category database | GEOSITE,cn,DIRECT |
| IP-CIDR / IP-CIDR6 | IPv4 / IPv6 ranges | IP-CIDR,192.168.0.0/16,DIRECT,no-resolve |
| GEOIP | IP geolocation | GEOIP,CN,DIRECT,no-resolve |
| SRC-IP-CIDR | Source IP range of the connection | SRC-IP-CIDR,192.168.1.0/24,DIRECT |
| DST-PORT / SRC-PORT | Destination / source port | DST-PORT,22,DIRECT |
| PROCESS-NAME | Name of the process making the connection | PROCESS-NAME,chrome.exe,PROXY |
| RULE-SET | External rule set | RULE-SET,ads,REJECT |
| MATCH | Catch-all, matches everything | MATCH,PROXY |
Ordering in Practice
Write exceptions before broad rules: to force a site direct, its DOMAIN-SUFFIX must sit above broad entries like GEOSITE and GEOIP, or earlier entries will capture the traffic first. The narrower the rule, the earlier it goes; the broader, the later — MATCH always last. Process rules depend on OS process information: they work on Windows desktop, but mobile kernels usually can't read process names, so such rules never hit there.
config.yaml · Rule list example
rules:
- DOMAIN-SUFFIX,internal.example.com,DIRECT
- RULE-SET,ads,REJECT
- GEOSITE,private,DIRECT
- GEOSITE,google,PROXY
- GEOSITE,cn,DIRECT
- GEOIP,private,DIRECT,no-resolve
- GEOIP,CN,DIRECT,no-resolve
- MATCH,PROXY
Rule count vs. cost:sequential matching means more rules cost more per connection. Keep everyday configs within a few hundred entries; for tens of thousands of routing needs, use the rule sets from Chapter 7 — the kernel indexes them, so matching cost is essentially independent of rule count.
Providers: Subscriptions and Rule Sets
Both nodes and rules can be moved out of the main config and handed to "providers": the main config only declares sources and update policy, and the client fetches content on a schedule. Importing a subscription link generates exactly a proxy-providers entry; rule sets compress tens of thousands of routing rules into a single reference.
proxy-providers: Subscription Sources
Each subscription is a named entry. type is http (remote fetch) or file (local file); url is the subscription address; path is the local cache path, used to start from cache when offline; interval is the auto-update interval in seconds. The health-check sub-section enables uniform testing for all nodes in the subscription, with the same fields as group health checks. The override sub-section rewrites node fields across the subscription — for example, forcing UDP on.
rule-providers: Rule-Set Sources
The key field of a rule set is behavior: domain matches entries by domain suffix, ipcidr by IP range, and classical means entries are complete rules (with type prefixes). For the first two the kernel builds dedicated indexes and matching is extremely fast, but the file may contain only bare domain or range lists. format supports yaml and text files.
config.yaml · Providers example
proxy-providers:
mysub:
type: http
url: "https://example.com/sub?token=xxxx"
path: ./providers/mysub.yaml
interval: 86400
health-check:
enable: true
url: http://www.gstatic.com/generate_204
interval: 300
rule-providers:
ads:
type: http
behavior: domain
format: yaml
url: "https://example.com/rules/ads.yaml"
path: ./providers/ads.yaml
interval: 86400
Declarations take effect only when referenced: use use: [mysub] in a group to pull in subscription nodes, and RULE-SET,ads,REJECT in the rule list to attach a rule set. For obtaining subscription links and converting formats, see the subscription import article.
Inline and provider styles can be mixed: keep one or two hand-maintained backup nodes in proxies, let proxy-providers manage the bulk subscription nodes, and list both in the same group. Subscription updates touch only the provider entries and leave hand-written nodes alone — a sturdier organization than writing everything into proxies.
Override and Merge
A subscription update is a wholesale replacement: the client fetches the new config and the old file is overwritten along with your hand edits. To keep your changes long-term, there are two paths — use the client's override mechanism to turn changes into "patches," or use YAML anchor syntax to cut repetition and maintenance cost.
Client Override Mechanisms
Mainstream GUI clients all store "subscription original" and "user changes" in separate layers. Clash Verge Rev offers two override entries — a global Merge config and scripts; the former merges by field, the latter rewrites freely in JavaScript. FlClash edits any field on its override page; Clash Plus stores UI settings apart from the subscription config, so settings survive subscription updates automatically. The common ground: the subscription file stays untouched, user changes stack on as patches, and they are re-applied after every update.
Note that merge semantics differ by client: scalar fields (ports, mode) always follow the patch; array fields (rules, proxies) are merged per item by some clients, replaced wholesale by others, and some support prepending and appending. After a change takes effect, the surest check is exporting the client's final runtime config and verifying the target fields — not just reading the patch text on the override page.
YAML Anchors and References
In hand-written configs, anchors eliminate repetition: write &name before a value to define an anchor, then reference it verbatim with *name; mappings can also merge an anchor's keys with <<: *name. When several groups share one node list or one set of test parameters, anchors keep a single source of truth — edit once, apply everywhere.
config.yaml · Anchor reuse example
proxy-groups:
- name: "Auto Select"
type: url-test
url: &test-url http://www.gstatic.com/generate_204
interval: &test-interval 300
proxies: &all-nodes
- "Node A"
- "Node B"
- "Node C"
- name: "Backup Chain"
type: fallback
url: *test-url
interval: *test-interval
proxies: *all-nodes
Expanded anchors are normal:some clients parse configs into internal structures and re-serialize on import, expanding anchors into repeated content on disk. Runtime behavior is identical — the file just gets longer, and nothing needs fixing.
Validation and Troubleshooting
Config problems cluster in three kinds: YAML format errors, bad field references, and rule logic that defies expectations. Troubleshoot in this section's order and most issues are located within minutes.
Pre-Start Validation
The mihomo kernel ships a config checker that validates without starting:
Terminal · Config validation command
mihomo -t -d /path/to/config-dir
An output of configuration ok means syntax and field checks passed; on failure the error carries a line number and field name — follow it back to the relevant chapter. GUI clients run the same validation on import or save, and their error popups match the command-line text.
Frequent Errors at a Glance
| Error snippet | Cause | Fix |
|---|---|---|
| mapping values are not allowed | A second colon in the value is misread as a new key | Wrap the whole value in double quotes |
| found character '\t' | A tab slipped into the indentation | Replace all indentation with spaces |
| proxy not found | A group or rule references a nonexistent name | Check name spelling in proxies and groups |
| rules[N] error | Rule N has the wrong segment count or a mistyped type | Check segment by segment against the Chapter 6 cheat sheet |
| field not found | Field name misspelled or placed at the wrong level | Compare against the Chapter 1 top-level field table |
Applying Changes Without a Restart
Saving a config in a GUI client triggers a hot reload — no kernel restart needed. From the command line, call the external controller: a PUT to /configs?force=true with the new config path reloads everything; for resolution residue from the fake-ip cache, flush the corresponding cache endpoint and retry. When rule hits defy expectations, temporarily set log-level to debug — the log prints the rule index hit by each connection, and tracing the index back to the rules list shows which entry captured the traffic.
Problems beyond the config — failed subscription imports, system proxy not working, auto-start on boot, and more — are answered one by one in the Beginner FAQ; see the Usage Guide for the main path and the download page for client installers.