Clash Multi-Device Config Sync: Subscription Links, Hosted Configs, and Manual Export
How to keep configs consistent across phone, PC, and tablet: subscription links, self-hosted config hosting, and manual export/import compared by use case, upkeep, and conflict handling.
First, sort out what actually needs syncing
When configs don't match across devices, it's usually not the client's fault — it's that several kinds of data with different natures are being managed together. They live in different places, update in different ways, and may or may not sync automatically. Sort that out before picking a method.
| Data | Stored in | Auto-sync? |
|---|---|---|
| Subscription link | Each client's config store | No — entered once per device |
Config file config.yaml | Client config directory or hosted URL | Yes — replaced wholesale on subscription update |
| Node and ruleset providers | ./providers and ./ruleset cache | Yes — fetched on the interval schedule |
| Currently selected node, run mode | Client runtime state | No — independent per device |
What really needs to be unified is the config file and the providers cache: the former decides ports, DNS, TUN, and rules; the latter decides the node list. As for which node is selected or whether mode sits at rule or global, those should be set per device anyway — no need to force them to match.
A common misconception: the same subscription does not mean the same config
A subscription only determines where nodes come from. Fields like mixed-port, dns, tun, and rules are maintained locally by each client — switching subscriptions won't sync ports, and it won't sync local rules either. So entering the same link on three devices and having three devices behave identically are two different things; the latter depends on the methods below.
Method 1: Automatic sync with a subscription link
Enter the same subscription URL on all three devices and let the client fetch it on a fixed interval. Desktop, Android, and iOS all support this — it covers the most ground and costs the least to maintain.
Go to Subscriptions → New, paste the subscription URL, and save. Don't turn on the proxy yet.
Right-click the subscription card → Edit, and change the auto-update interval from the default 1440 minutes to 360–720 minutes.
Right-click the subscription card → Update and confirm you get a full node list, not just a config name.
Turn on Settings → System Proxy as needed; TUN mode is enabled separately under Settings → TUN Mode. On mobile, the client's VPN service takes over — no extra configuration required.
On Android, long-press a config in the Configs list to find the update entry; on iOS, pull to refresh on the config detail page.
rules, mixed-port, or dns sections, those fields are overwritten with the subscription's values on update. Writing your own rules straight into the subscription config is the most common reason they disappear every time you update.
Symptoms when the port is rewritten
After a subscription changes mixed-port from 7890 to 7891, anything hardcoded to 7890 — system proxy settings, browser extensions — still points at the old port. The symptom: the client shows connected, but no page loads. To check, open the external-controller panel (listening on 127.0.0.1:9090 by default) and look at the port actually in effect.
# Show the ports, mode, and DNS settings currently in effect
curl -s http://127.0.0.1:9090/configs
If the command line isn't convenient, just compare the port shown on the desktop client's Settings page with the value in your browser extension.
Where it fits
- Good fit: 1–3 devices, rules that follow the subscription entirely, no extra files to maintain.
- Poor fit: custom rules, per-device ports, or subscriptions whose structure changes often.
Method 2: Self-hosted config — one YAML for every device
Put the config file at an address that can be downloaded directly (private object storage, your own nginx, a raw link from a private repo — all fine), then import that address as a subscription on each device. From then on you edit rules in one file and every client picks it up on its next fetch.
Decouple node sources from rules
Don't write nodes directly into proxies; use proxy-providers and let the core fetch them. Ports, DNS, and rules — the parts that belong to you — all go in the base config.
mixed-port: 7890
allow-lan: false
mode: rule
log-level: warning
external-controller: 127.0.0.1:9090
unified-delay: true
tcp-concurrent: true
proxy-providers:
airport-a:
type: http
url: "https://sub.example.com/api/v1/client/subscribe?token=9f2c1a7b4e"
interval: 3600
path: ./providers/airport-a.yaml
health-check:
enable: true
url: https://www.gstatic.com/generate_204
interval: 300
timeout: 3000
rule-providers:
reject:
type: http
behavior: domain
format: mrs
url: "https://example.com/rules/reject.mrs"
path: ./ruleset/reject.mrs
interval: 86400
dns:
enable: true
enhanced-mode: fake-ip
fake-ip-range: 198.18.0.1/16
nameserver:
- https://223.5.5.5/dns-query
fallback:
- https://1.1.1.1/dns-query
Both interval values are in seconds: 3600 fetches nodes once an hour, 86400 fetches the ruleset once a day. If nodes change often, drop the first to 1800; there's no need to refresh the ruleset more frequently.
format: mrs requires mihomo 1.18.0 or later; on older versions use format: yaml with behavior: domain. Also, the hosted address must be directly downloadable — if it sits somewhere that needs a proxy to reach, the first launch can't fetch the config and you'll see an empty node list.
Run a local syntax check before publishing
# Config check only — do not start the proxy
mihomo -t -d /etc/mihomo -f config.yaml
Once the check passes, push it to the hosted address. After each device imports the hosted URL, nodes are fetched by the providers on schedule, and changing rules only requires each client to re-fetch the config — no editing device by device.
Fields and the kernel versions that support them
| Field | Available since | What it does |
|---|---|---|
format: mrs | mihomo 1.18.0 and later | Binary ruleset format — smaller and faster to load |
tcp-concurrent | mihomo 1.16 and later | Concurrent connection setup, less handshake waiting |
unified-delay | mihomo 1.16 and later | Unified latency measurement across protocols |
geodata-mode | mihomo 1.16 and later | Choose how built-in GEO data is used |
Method 3: Manual export and import
Worth using only for offline devices, routers, or one-off debugging. Export the config file and copy the providers/ and ruleset/ cache directories along with it — skip the cache and the trip is wasted.
On the desktop client, click Open Config Directory in Settings; the mihomo command-line version usually uses /etc/mihomo/config.yaml or ~/.config/mihomo/config.yaml.
If you copy only config.yaml, the first launch shows an empty node list because the providers cache is missing.
For example config-20260830.yaml, so the client doesn't end up with several same-named configs overwriting each other.
On iOS, save the YAML to the device with the Files app or cloud storage first, then choose Import Config in the client.
Check the node count on the Proxies page and the rule count on the Rules page — they should match the source device.
In a few cases, manual import is basically the only option:
- The target device can't reach the hosted address — an intranet or offline environment, for example.
- You just want to test a rule or a set of DNS settings temporarily without touching the hosted file.
- Embedded environments such as routers, where files can only be placed via USB drive or SCP.
Conflict handling: layer your changes
When the three methods are mixed, conflicts almost always come from one cause — the same field edited in two places. The fix is to layer changes: keep only shared settings in the subscription or hosted file, and put device differences in a merge layer.
Write only the differences in the merge layer
# Merge config: don't copy the whole config.yaml, just the parts you want to change
prepend-rules:
- DOMAIN-SUFFIX,intranet.example.com,DIRECT
prepend-proxy-groups:
- name: Manual Select
type: select
proxies: [Auto Select, DIRECT]
Rules go to the top with prepend-rules, so they match before the subscription's own rules; proxy groups go to the top of the list with prepend-proxy-groups, making them easy to pick in the UI. When the subscription updates, the merge layer isn't overwritten — that's what makes it more reliable than editing the subscription config directly.
Consistency checklist
| Item | Where | Expected result |
|---|---|---|
| Active port | Settings → Port, or GET /configs | 7890 on every device |
| Node count | Proxies page | Same node count from the same provider |
| Rule count | Rules page | Matches the number of rules in the hosted file |
| Provider update time | GET /providers/proxies | Within interval |
| Run mode | Tray menu or home screen | Keep rule day to day; switch to global only when troubleshooting |
# Show the provider's last fetch time and node count
curl -s http://127.0.0.1:9090/providers/proxies
A useful rule of thumb: let each field be defined in exactly one place. Ports, DNS, and TUN go in the base config; node sources go in proxy-providers; device differences go in the merge layer; the selected node and run mode stay with each device. Do that and the only difference left between three devices is which node is currently selected.
Two things to watch on a LAN
external-controllerlistens only on127.0.0.1by default. To reach the panel from another device, change it explicitly to0.0.0.0:9090and set asecret.- Several devices running
allow-lanat once won't conflict, but other devices must point at the right device IP; keep the port at 7890.
How to choose between the three methods
| Method | Best for | Upkeep | Conflict handling |
|---|---|---|---|
| Subscription link auto-sync | 1–3 devices, rules follow the subscription | Low | Each update overwrites; local edits are lost |
| Self-hosted config | 3+ devices, custom rules | Medium | Layered merge; conflicts are predictable |
| Manual export/import | Offline devices, routers, one-off debugging | High | Full overwrite; manual verification needed |
The most common setup: the main device uses a hosted URL plus a merge layer, phones and backup machines just take the subscription link, and devices that can't easily update over the network — routers, for instance — get files placed manually. With all three in play, as long as each field has exactly one definition, they won't fight each other.
FAQ
The phone and computer use the same subscription but show different node counts
&flag=clash when copying will change the result.Local rules vanish after a subscription update — how do I keep them?
prepend-rules; for clients that don't support merge chains, switch to a self-hosted URL and put the rules in the rules section of the base config.The same config works on macOS but fails to start on Android
tun, redir-port, and tproxy-port are desktop and Linux conventions; on Android the client's own VPN service handles that. And when external-controller is set to 0.0.0.0:9090 without a secret, some clients refuse to start at all. Move these fields out of the base config and into each platform's merge layer.Will TUN mode conflict if several devices enable it at once?
allow-lan enabled, make sure other devices point at the right device IP; the panel port listens only on localhost by default, so cross-device access requires opening it explicitly and setting a secret.