Start with the config path and how its sections connect
The core relationships are simple: rules select a policy group, policy groups select a proxy, and DNS resolves domain names. Organizing the fields around these relationships makes problems easier to pinpoint than editing a subscription file line by line. This guide covers mihomo, the successor project commonly referred to as the Clash Meta core. Older Clash cores and different GUI clients may not support the same fields or config-merging behavior.
When running the core directly, use ~/.config/mihomo as the working directory and save the config as config.yaml there. In a client, first check which config file it ultimately passes to the core. The original subscription, override file, and runtime config may all be separate files. Editing the runtime file directly may be undone the next time the subscription updates.
mkdir -p ~/.config/mihomo
mihomo -v
mihomo -t -d ~/.config/mihomo
-v prints the version of the running core; -t checks the config in the specified directory. If the config hasn't been created yet, it's normal for the last command to report that the file can't be found. You can combine the YAML snippets in the first six sections into a starter config. The local SOCKS proxy in the example requires an existing service; it is not a ready-to-use remote proxy.
1. mixed-port: Set up a local proxy endpoint
mixed-port: 7890
allow-lan: false
bind-address: "127.0.0.1"
ipv6: false
mixed-port accepts both HTTP proxy and SOCKS5 connections, so browsers and command-line tools can share 127.0.0.1:7890. This is neither the dashboard port nor a remote port on a proxy server. If only local apps need access, leave allow-lan: false. Don't expose the listener on every network interface just to troubleshoot a website.
| Field | Purpose | Value used here |
|---|---|---|
mixed-port | HTTP / SOCKS endpoint for apps | 7890 |
port | Separate HTTP proxy endpoint | Not enabled separately |
socks-port | Separate SOCKS proxy endpoint | Not enabled separately |
dns.listen | Address the core's DNS server listens on | 127.0.0.1:1053 |
external-controller | Management API; does not accept regular web proxy requests | Omitted from the starter config |
IPv6 is disabled here as a variable to simplify initial setup; that doesn't mean it should stay disabled on every network. If you enable it later, check AAAA DNS responses, IPv6 connectivity to your proxies, and system routing together. To share the proxy with devices on your LAN, plan the listener address, authentication, and firewall rules instead of changing a single boolean.
ss -lntp 'sport = :7890'
curl --proxy http://127.0.0.1:7890 --connect-timeout 10 -I https://example.com/
The first command checks whether the port is listening; the second explicitly uses the proxy, bypassing desktop system proxy settings. These checks are useful only after the core is running. If the connection is refused, check the process and port first—you don't need to change DNS straight away.
2. mode and logging: Set the routing mode
mode: rule
log-level: info
mode: rule selects an outbound route using the rules list. direct mode favors direct connections, while global mode uses a global policy. When troubleshooting rules, first make sure the client hasn't switched to another mode. The management API can also change the mode, so a config file set to rule doesn't guarantee that's the mode currently in use.
log-level: info is useful for general connection and startup errors. To trace specific rule or DNS behavior, switch briefly to debug, reproduce the issue, then switch back. Logs may contain visited domains, proxy addresses, and connection details; remove sensitive information before sharing them. Don't change the mode, proxy, and DNS all at once. Change one set of fields at a time so you can link each result to a specific change.
3. DNS: Understand upstreams, listener ports, and fake-ip
dns:
enable: true
listen: "127.0.0.1:1053"
ipv6: false
enhanced-mode: fake-ip
fake-ip-range: 198.18.0.1/16
fake-ip-filter:
- "*.lan"
- "localhost"
default-nameserver:
- 223.5.5.5
- 119.29.29.29
nameserver:
- https://dns.alidns.com/dns-query
- https://doh.pub/dns-query
What each of the three fields does
listensets where the local DNS server accepts queries. This example uses the unprivileged port1053, avoiding the system's commonly used port53during initial setup.default-nameserverresolves the domain names of DNS upstream servers, so mihomo can look them up before sending queries to them.nameserverspecifies the default DNS upstreams. This example uses two DoH addresses; your network must be able to reach them. Using HTTPS doesn't guarantee they're reachable.
fake-ip returns a virtual address for eligible domains. The core then uses its mapping to recover the domain and handle the connection. This is not the actual IP of a remote proxy, nor does it mean the destination website is in 198.18.0.0/16. For LAN services that rely on real DNS answers, add their domains to fake-ip-filter so they don't receive fake-IP responses.
dig @127.0.0.1 -p 1053 example.com A
dig @127.0.0.1 -p 1053 localhost A
With this example config, the first query will usually return a fake IP; filtered domains behave according to their actual DNS resolution. If dig isn't installed, install the DNS tools package provided by your distribution. Querying the core's listener directly helps distinguish an upstream resolution problem from a system that isn't using mihomo DNS.
4. proxies: Define proxies; don't use the local listener as an upstream
proxies:
- name: "Local SOCKS"
type: socks5
server: 127.0.0.1
port: 1080
udp: false
This example points to an existing local SOCKS5 service at 127.0.0.1:1080, with no authentication. It's useful for checking config references, but it won't create the service. If you don't have this upstream, selecting DIRECT in the policy group later still lets you test the local listener and rule loading, but not proxy connectivity.
name is the internal name used to reference this entry, and the policy group later must match it exactly. server and port specify the proxy endpoint. If you already have a subscription, use the proxy parameters provided by the service; don't mechanically convert this SOCKS5 example to another protocol. Shadowsocks, VMess, VLESS, and other types each have their own authentication and transport fields—type isn't the only difference.
5. proxy-groups: Give rules a stable outbound name to use
proxy-groups:
- name: "Proxy"
type: select
proxies:
- DIRECT
- "Local SOCKS"
select creates a manually selectable group. Rules only need to reference “Proxy,” so you can replace a proxy without editing each rule. DIRECT is a built-in direct-connection policy and doesn't need to be declared again under proxies. “Local SOCKS” must match the proxy name from the previous section. On a first run with no saved selection, this example puts DIRECT first so you can verify the basic connection path.
GUI clients usually save policy group selections. After reordering the list, a previous selection may still be restored, so check which entry is currently selected before testing. When running the core alone without the management API, you can change the first entry and restart during initial tests, then connect a local dashboard if needed.
Add url-test only when you need automatic latency checks
url-test periodically probes a URL and compares latency; it doesn't prove that every website is reachable. For example, interval: 300 runs a check every 300 seconds, not on every request. If you have only a few proxies and are just starting to troubleshoot, use select to confirm a working route first. Consider automatic selection later, so health-check failures don't get confused with proxy authentication failures.
6. rules: Match specific conditions first, then add a fallback
rules:
- DOMAIN-SUFFIX,lan,DIRECT
- DOMAIN,localhost,DIRECT
- 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
- DOMAIN-SUFFIX,github.com,Proxy
- MATCH,Proxy
Rules are checked from top to bottom, and matching usually stops at the first hit. DOMAIN matches an exact domain; DOMAIN-SUFFIX matches the domain and its subdomains. For example, both github.com and api.github.com match the suffix rule here, but that doesn't mean every domain used by GitHub services is covered.
IP-CIDR matches a destination IPv4 address range. no-resolve means not to resolve a domain just to evaluate this IP rule if its destination IP is not already known; a known destination IP can still match. For LAN services, maintain any necessary domain rules too—don't assume every local hostname can be inferred from private IP ranges.
MATCH is the final fallback and belongs at the end. If it comes earlier, later rules won't get a chance to match. This example explicitly lists private IP ranges, so it doesn't require a GeoIP database during initial setup; it also isn't a complete list of special-use addresses. IPv6 was disabled in the starter config above; if you enable it, add the appropriate IPv6 routing rules too.
The snippets in the first six sections now make up a basic config. They demonstrate how the fields relate; they aren't a complete region-based routing setup. Once the baseline works, add more detailed rule sets, DNS routing, and process rules one at a time. See the configuration reference for more fields.
7. proxy-providers: Split proxies into a separate file as your list grows
As your proxy list grows, you can move it into a Provider file. The option below replaces sections 4 and 5; don't paste it alongside policy groups with the same names from earlier. The main config loads proxies from the file, and the policy group references the Provider name with use—not by adding the Provider name to the proxy list.
proxy-providers:
local-pool:
type: file
path: ./providers/local.yaml
health-check:
enable: true
url: https://www.gstatic.com/generate_204
interval: 600
proxy-groups:
- name: "Proxy"
type: select
proxies:
- DIRECT
use:
- local-pool
Add the following to ~/.config/mihomo/providers/local.yaml, creating the providers directory first. Check relative paths against your actual working directory; the launch command in this guide sets it explicitly with -d.
proxies:
- name: "Local SOCKS"
type: socks5
server: 127.0.0.1
port: 1080
udp: false
Provider files must use the proxy-list format. Renaming a full client config containing fields such as dns and rules won't make it a valid Provider file. For type: http, add the actual subscription URL and set the refresh interval with the Provider-level interval field. Subscription URLs often contain credentials, so keep them out of public logs and screenshots.
Note the difference between the two intervals: the Provider's interval controls remote content refreshes; health-check.interval controls proxy checks. This is a local-file Provider, so a health check every 600 seconds won't download new proxies from a remote server. If the probe URL itself is unreachable, the entire group may fail its checks; verify this against actual requests.
8. TUN is an optional traffic interception layer
Get explicit proxying working before enabling TUN. TUN uses a virtual network device and routing to capture traffic; a regular mixed-port doesn't automatically proxy every app. The snippet below is for Linux testing with the required permissions and routing setup in place. It isn't part of the basic config in the first six sections.
tun:
enable: true
stack: mixed
auto-route: true
auto-detect-interface: true
dns-hijack:
- any:53
auto-route asks the system to set up the relevant routes, while auto-detect-interface identifies the outbound interface. Creating a TUN device and changing routes require sufficient system privileges, usually provided by a client's service mode or an administrator-configured service. A regular user process may not have the required permissions. dns-hijack captures traditional port 53 DNS requests that enter this processing path; it doesn't decrypt DoH traffic from the browser itself.
9. Validate syntax, listeners, DNS, and outbound connectivity
- Back up the current config. Before making changes, keep a copy of a known-working config, especially if it contains subscription overrides or custom rules. Make sure you don't mistake the backup for the active config.
- Check syntax and references. Run
mihomo -t -d ~/.config/mihomo. Resolve missing proxy names, invalid policy group references, and YAML indentation errors before restarting. - Run only one test instance. If a GUI client is already using
7890, stop its core first or choose another port for the test config. A port conflict doesn't mean the proxy is broken. - Run in the foreground and watch the logs. Start mihomo with the command below, then test the port, DNS, and proxy requests from another terminal. Press Ctrl+C to stop the foreground test.
mihomo -d ~/.config/mihomo
# Run this in another terminal
ss -lntp 'sport = :7890'
dig @127.0.0.1 -p 1053 example.com A
curl --proxy http://127.0.0.1:7890 --connect-timeout 10 -I https://example.com/
curl --proxy socks5h://127.0.0.1:7890 --connect-timeout 10 -I https://example.com/
The h in socks5h means the proxy resolves the destination hostname. This differs from resolving the domain locally before connecting, and helps determine whether the issue lies in the app's own DNS path. An HTTP error status also needs context: distinguish a response from the destination website from a failed proxy connection. Don't judge the connection solely by whether it returns 200.
Passing the config test only confirms that the config can be parsed and loaded. It doesn't guarantee that the subscription URL is reachable, proxy credentials are correct, DNS upstreams are accessible, or TUN routes are valid. Once the baseline checks pass, restore the systemd service or client management and make sure it uses the same binary, working directory, and config file as your test. For deployment steps, continue with Getting Started; for connection issues, see Troubleshooting.
Keep the dependency chain clear: apps connect to mixed-port, rules select “Proxy,” the policy group chooses a working proxy or DIRECT, and DNS provides the relevant resolution. When adding subscription updates, rule sets, or TUN, expand one layer at a time and repeat the same validation commands.