Deep Docs · Config Reference

Clash Config YAML Manual

This page explains config.yaml field by field: top-level structure, general fields, DNS, proxies, proxy groups, rule syntax, providers, and override merging, with ready-to-use YAML examples throughout. The tutorial solves "installed and running"; this page solves "what does every line of this config mean."

Format: YAML Kernel: mihomo Works with Clash Plus / Verge Rev / FlClash About a 25-minute read

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:

FieldTypeRole
port / socks-port / mixed-portIntegerLocal listening ports for HTTP, SOCKS5, and mixed proxy entry points
modeStringProxy mode: rule routes by rules, global proxies everything, direct connects directly
log-levelStringLog verbosity; set to debug temporarily when troubleshooting
dnsMappingSwitch, upstreams, and resolution mode of the built-in DNS
proxiesListProxy node definitions, one item per node
proxy-groupsListProxy groups that organize nodes into selectable sets
rulesListRouting rules, matched top to bottom
proxy-providersMappingSubscription sources: external providers of node lists
rule-providersMappingRule-set sources: external providers of rules
external-controllerStringListen 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.

FieldTypical ValueNotes
mixed-port7890Mixed proxy entry; the desktop client convention
moderuleOne of rule / global / direct
log-levelwarningSwitch to debug temporarily when troubleshooting
allow-lanfalseSet true to share with LAN devices
external-controller127.0.0.1:9090Localhost only; switching to 0.0.0.0 requires a secret
unified-delaytrueUnifies latency test timing
tcp-concurrenttrueDials candidate nodes concurrently
profile.store-selectedtrueRemembers 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 typeRequired fieldsCommon extended fields
ssserver, port, cipher, passwordplugin, plugin-opts, udp
ssrserver, port, cipher, password, protocol, obfsprotocol-param, obfs-param
vmessserver, port, uuid, alterId, ciphertls, network, ws-opts, servername
vlessserver, port, uuidflow, tls, reality-opts, network
trojanserver, port, passwordsni, alpn, network, grpc-opts
hysteria2server, port, passwordsni, obfs, up, down
tuicserver, port, uuid, passwordcongestion-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

TypeHow it selectsUse case
selectManual selection, a dropdown in the UIMain group; exits that need human judgment
url-testPeriodic speed tests; auto-picks the lowest latencyAuto-picking among multiple nodes in one region
fallbackFirst available node in list orderPrimary/backup failover; stability first
load-balanceSpreads connections across membersSplitting heavy traffic across lines
relayChains nodes in listed orderFixed 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

TypeMatchesExample
DOMAINFull domain, exact matchDOMAIN,api.example.com,PROXY
DOMAIN-SUFFIXDomain suffix, subdomains includedDOMAIN-SUFFIX,google.com,PROXY
DOMAIN-KEYWORDAny fragment in the domainDOMAIN-KEYWORD,telegram,PROXY
GEOSITEDomain category databaseGEOSITE,cn,DIRECT
IP-CIDR / IP-CIDR6IPv4 / IPv6 rangesIP-CIDR,192.168.0.0/16,DIRECT,no-resolve
GEOIPIP geolocationGEOIP,CN,DIRECT,no-resolve
SRC-IP-CIDRSource IP range of the connectionSRC-IP-CIDR,192.168.1.0/24,DIRECT
DST-PORT / SRC-PORTDestination / source portDST-PORT,22,DIRECT
PROCESS-NAMEName of the process making the connectionPROCESS-NAME,chrome.exe,PROXY
RULE-SETExternal rule setRULE-SET,ads,REJECT
MATCHCatch-all, matches everythingMATCH,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 snippetCauseFix
mapping values are not allowedA second colon in the value is misread as a new keyWrap the whole value in double quotes
found character '\t'A tab slipped into the indentationReplace all indentation with spaces
proxy not foundA group or rule references a nonexistent nameCheck name spelling in proxies and groups
rules[N] errorRule N has the wrong segment count or a mistyped typeCheck segment by segment against the Chapter 6 cheat sheet
field not foundField name misspelled or placed at the wrong levelCompare 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.