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.

DataStored inAuto-sync?
Subscription linkEach client's config storeNo — entered once per device
Config file config.yamlClient config directory or hosted URLYes — replaced wholesale on subscription update
Node and ruleset providers./providers and ./ruleset cacheYes — fetched on the interval schedule
Currently selected node, run modeClient runtime stateNo — 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.

Add a subscription

Go to Subscriptions → New, paste the subscription URL, and save. Don't turn on the proxy yet.

Change the auto-update interval

Right-click the subscription card → Edit, and change the auto-update interval from the default 1440 minutes to 360–720 minutes.

Update it manually once

Right-click the subscription card → Update and confirm you get a full node list, not just a config name.

Decide the proxy entry point per device

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.

Repeat the steps on the remaining devices

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.

Subscription updates replace the whole config file If the subscription file includes 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.

Two details that trip people up 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

FieldAvailable sinceWhat it does
format: mrsmihomo 1.18.0 and laterBinary ruleset format — smaller and faster to load
tcp-concurrentmihomo 1.16 and laterConcurrent connection setup, less handshake waiting
unified-delaymihomo 1.16 and laterUnified latency measurement across protocols
geodata-modemihomo 1.16 and laterChoose 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.

Locate the config directory

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.

Copy the cache directories too

If you copy only config.yaml, the first launch shows an empty node list because the providers cache is missing.

Include the date in the file name

For example config-20260830.yaml, so the client doesn't end up with several same-named configs overwriting each other.

Import on the target device

On iOS, save the YAML to the device with the Files app or cloud storage first, then choose Import Config in the client.

Compare right after importing

Check the node count on the Proxies page and the rule count on the Rules page — they should match the source device.

Manual import overwrites everything There is no merge logic: local rules and port settings already on the target device are overwritten by the imported file. Export a copy of the target device's config before importing.

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

ItemWhereExpected result
Active portSettings → Port, or GET /configs7890 on every device
Node countProxies pageSame node count from the same provider
Rule countRules pageMatches the number of rules in the hosted file
Provider update timeGET /providers/proxiesWithin interval
Run modeTray menu or home screenKeep 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-controller listens only on 127.0.0.1 by default. To reach the panel from another device, change it explicitly to 0.0.0.0:9090 and set a secret.
  • Several devices running allow-lan at 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

MethodBest forUpkeepConflict handling
Subscription link auto-sync1–3 devices, rules follow the subscriptionLowEach update overwrites; local edits are lost
Self-hosted config3+ devices, custom rulesMediumLayered merge; conflicts are predictable
Manual export/importOffline devices, routers, one-off debuggingHighFull 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
Update manually once on both devices, then compare again. If they still differ, check that the subscription URLs are identical — some subscriptions return different formats by client type, and dropping a trailing parameter like &flag=clash when copying will change the result.
Local rules vanish after a subscription update — how do I keep them?
Don't put rules in the subscription itself. On desktop, put local rules in the merge chain's 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
Platform-specific fields differ. 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?
No — each device's TUN only affects that machine. If they're all on the same LAN with 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.
Download Clash Client