Search intent: keep the base profile clean
The typical story is familiar. A friend shares a remote URL; the client downloads a few hundred kilobytes of YAML; everything works until you need one extra corporate subnet on DIRECT, a backup subscription for streaming, or a DNS knob your provider enabled but you want off. Forking the upstream file breaks the next auto-update. Patching by hand inside the cached copy works once, then the refresh stomps it. What you want is a second document the client always merges after it materializes the provider profile and before the core loads—often labeled Merge, Mixin, or similar depending on the GUI. The engine still sees one flattened config; you keep two sources of truth: immutable remote base, version-controlled local overlay.
This article focuses on concepts that transfer across Mihomo-class stacks. Exact menu names differ, but the upstream syntax reference and desktop guides such as Clash Verge Rev’s merge documentation describe the same primitives: prepend and append lists for rules, proxies, groups, and selective dictionary overrides for DNS or tun. When your packaging does not offer merge at all, the equivalent is maintaining your own top-level YAML that references remote proxy-providers and rule-providers—more work, same idea. Our Docker deployment article and Linux TUN walkthrough assume you already control the file on disk; here we focus on the split between vendor profile and personal overlay.
profile: is not your mixin file
Beginners often grep the word profile and land in the wrong place. In Mihomo’s general configuration, profile: is a small cache section documented on the official wiki: flags such as store-selected (remember which node you picked in each group across restarts) and store-fake-ip (persist FakeIP mappings). Those options change how runtime state is serialized; they do not merge another subscription, append rules, or import a second YAML file. Thinking of profile: as “my overlay” leads to edits in the wrong block and confusing bug reports.
By contrast, a mixin / merge document is an intentional patch layer. Clients apply it to the working config tree: lists get concatenated in defined order, maps merge shallowly or by documented rules, and explicit override keys replace matching sections where allowed. Nothing about that lives inside profile:; keep the mental model crisp—persistence metadata versus structural overlay.
Merge primitives you will actually use
Desktop merge templates usually expose symmetric hooks. On the prepend side you inject items before the provider’s content so your high-priority exceptions win the first-match race. On the append side you add items after the imported lists—useful for trailing proxies when ordering does not fight the provider’s assumptions, but dangerous for rules when a catch-all MATCH already sits at the bottom. Typical skeleton (field names follow common Clash Verge Rev merge docs; confirm against your build):
# Merge / mixin skeleton (illustrative)
prepend-rules: []
prepend-proxies: []
prepend-proxy-groups: []
append-rules: []
append-proxies: []
append-proxy-groups: []
# Shorthand for inline providers (syntax varies by client version)
prepend-proxy-providers: {}
prepend-rule-providers: {}
append-proxy-providers: {}
append-rule-providers: {}
# Dictionary overrides (same shape as normal config)
# dns:
# ipv6: false
Newer releases sometimes fold provider hooks into the standard proxy-providers and rule-providers maps instead of separate prepend-only keys. If an option disappeared after an upgrade, read the release notes: the capability usually moved, not vanished. Regardless of labels, the invariant is unchanged—your extra subscription becomes another provider entry that feeds proxies, and your custom ladder slots into the rules: array ahead of or behind the provider’s bundle according to the hook you chose.
MATCH trap: append-rules often lands after the provider’s final MATCH, so new rows never run. Prefer prepend-rules for almost every custom domain or IP exception unless you deeply understand the imported ladder.
Prepend rules without touching the remote ladder
Suppose the base profile routes a SaaS domain through a congested default group. You want that hostname on a different policy tag or on DIRECT for a split-tunnel office scenario. Editing the downloaded file invites churn; instead, add a prepend-rules stanza in your merge file with explicit predicates—DOMAIN-SUFFIX, IP-CIDR with no-resolve when appropriate, or RULE-SET rows if you already maintain a small provider. Because prepend runs first, you respect the “who matches first” contract documented in our rule-order article without renumbering hundreds of vendor lines.
When Sniffer is involved, remember metadata timing: rules that need a recovered hostname still compete in the same order. If prepend lines never fire, open the log, read the matched rule index, and verify whether the provider placed a broad GEOIP or GEOSITE bucket above your exception in the merged array—not whether your exception “exists somewhere” in a disconnected file. For provider-specific vocabulary on GEOIP and GEOSITE, see the split-routing guide.
Stacking another subscription via providers
A second airline-lounge subscription does not require pasting nodes into the base file. Define a proxy-providers entry that points at the URL (or a local file) and let Mihomo expand it into outbound definitions at load time. In merge mode you typically add that map under the prepend hook or as a sibling override depending on the client. Then reference the provider’s generated names inside proxy-groups using the same tags the UI expects—either by extending an existing select group with use: clauses or by introducing a dedicated group in prepend-proxy-groups.
Name collisions are the sharp edge. If both providers emit name: Singapore-01, the later import wins or fails validation depending on build settings. Prefer providers that prefix node names, or wrap one side through Subconverter with a rename prefix so your merge stays deterministic. Health checks and url-test groups still behave as described in the failover tutorial; merging does not bypass latency logic.
Rule providers and maintenance
Large community bundles ship as rule-providers so your YAML stays short. In a merge file you can prepend a compact provider that tracks your personal allow/deny list while leaving the vendor’s GEOSITE archives untouched. Keep behavior and format aligned with Mihomo expectations, set sane interval values, and remember that the rule row that references the provider still obeys ordering—usually a single RULE-SET line in prepend-rules pointing at your lightweight set beats cloning thousands of static lines.
When a provider URL blocks automated fetches, mirror the artifact or fetch through a conversion pipeline you control; our Subconverter guide covers why pasting secrets into random web converters is a poor trade. For day-to-day edits, treat rule providers like code: small diffs, readable commit messages, and a quick reload cycle through the controller API if you run headless.
Overrides: DNS, tun, and what the GUI forbids
Merge is not a jailbreak. Clients rightly block overrides that would strand the UI—commonly mixed-port, external-controller, log-level, and other control-plane knobs the shell must own. Everything else is fair game within schema validation: flip dns.ipv6, add nameserver-policy, tune fake-ip-filter, or align DoH with the routing story in our DNS leak guide. If an override silently fails, assume the packaging pinned that key; check the merge panel’s warning text before chasing ghosts in the core.
TUN and interface-specific options can be merged too, but they interact with OS permissions and startup order. If you merge TUN snippets while the GUI also manages elevation, verify only one layer owns the final tun: dictionary to avoid contradictory keys. When in doubt, reproduce with a minimal merge containing a single boolean flip (for example DNS IPv6 off) before stacking larger blocks.
Multiple merge files and activation order
Some clients allow several merge profiles toggled independently. They apply sequentially in activation order—first card runs, then the second mutates the result, and so on. That is powerful for separating concerns (one file for office subnets, another for media providers) but difficult to reason about when two files both prepend rules. Document the chain, keep backups, and after edits use the client’s disable/enable cycle so the merged output actually reloads; stale cards are a frequent “it did not apply” culprit.
Tip: Export the effective runtime config through the REST API when debugging merges—diffing the flattened YAML beats staring at three partial files.
Headless Mihomo without a merge UI
On servers you often skip the GUI entirely. The equivalent to mixin is your checked-in config.yaml that already contains proxy-providers pointing at remote subscriptions plus static prepend-style sections you maintain yourself. Automation tools and CI can assemble that file from fragments; the engineering tradeoff is explicit merges in a build script versus implicit merges in a desktop shell. If you followed our Docker compose guide, mount the overlay file as a volume and keep provider URLs in environment variables rather than editing inside containers.
For operational visibility, wiring external-controller and a dashboard remains the shortest path to inspect effective rules; see the Yacd walkthrough for API basics. The vocabulary in our configuration documentation matches what the core expects after all merges settle.
Verify, then trust
1Reload and read the connections table
After any merge change, trigger a profile reload and open the live connections view. Hit the target domain once, confirm the outbound tag matches the prepend rule you added, and note the matched rule index if the UI exposes it.
2Diff effective config
When available, dump the merged config from the API and search for your injected provider or rule stanza. If it is missing, the merge file never applied or failed schema validation—check client logs for YAML errors before blaming the core.
3Watch for duplicate keys
YAML merges that produce duplicate map keys collapse unpredictably. Keep each top-level section owned by either the provider or your mixin, not both fighting the same key path.
Common pitfalls
- Assuming
profile:merges subscriptions: it only stores selections and FakeIP tables. - Using
append-rulesfor overrides: traffic may never reach rows belowMATCH. - Colliding proxy names: rename at conversion time when two providers export identical labels.
- Editing the cached remote file: the next refresh still wins—move edits into merge.
- Ignoring DNS alignment: a perfect rule plus a wrong resolver still looks like a routing bug; pair merges with DNS policy.
Source transparency and installers
Mihomo publishes sources and tags so you can diff behavior across semver bumps—valuable when a merge feature moves between keys. For teammates who need installers rather than raw binaries, prefer the official Clash download page as the primary channel; upstream GitHub remains the right place for license text, issues, and checksum verification, not an ad-hoc installer roulette.
Summary
Mixin and merge layers solve a maintenance problem: they let you evolve rules, add subscription feeds, and patch dns: while leaving the vendor’s base profile refreshable. The profile: section is unrelated—it persists UI choices and FakeIP state, not structural overlays. Prefer prepend-rules for custom policy, grow outbound lists through proxy-providers, and validate by inspecting the flattened config the core actually loads. Compared with endless manual rebases on someone else’s YAML, a disciplined merge file is the boring, reliable approach advanced users stick with for months.
Desktop clients that bundle Mihomo often ship the merge workflow out of the box; if you want that polish without stitching together merge files by hand, start from a maintained installer and keep your overlays small.