migrating-sing-box-configs
GitHub用于在 sing-box 版本升级时迁移配置文件,覆盖 1.8.0 至 1.14.0 版本的弃用字段处理。流程包括识别版本、顺序应用迁移规则,并强制使用 sing-box check 验证配置有效性,确保兼容新版。
Trigger Scenarios
Install
npx skills add JohanChane/clashtui --skill migrating-sing-box-configs -g -y
SKILL.md
Frontmatter
{
"name": "migrating-sing-box-configs",
"description": "Use when migrating sing-box configuration files between versions, when `sing-box check` reports deprecated fields or validation errors after an upgrade, or when a config written for an older sing-box version fails to work with a newer version."
}
Migrating Sing-Box Configs
Overview
Sing-box deprecates and removes configuration fields across versions. This skill covers the migration patterns for versions 1.8.0 through 1.14.0, with a mandatory sing-box check verification step after each migration.
Core Workflow
- Identify the current config version and the target version
- Apply migrations sequentially from old to new (e.g. 1.8 → 1.10 → 1.11 → 1.12 → 1.14)
- After each migration step, run
sing-box check -c <config>to verify - If
sing-box checkfails, fix the reported errors before proceeding
Mandatory Verification
After every migration change, run:
sing-box check -c /path/to/config.json
Never skip this step. A passing sing-box check is the only reliable indicator that the config is valid for the target version.
If the binary for the target version is not installed, install it first or use -D to specify a different working directory. Ask the user which version they are targeting and how to obtain the correct binary.
When the Skill Doesn't Know a Configuration
If encountering a config field or migration not covered here, ask the user for:
- The sing-box docs repository URL (e.g. a local clone of
https://github.com/SagerNet/sing-boxon thedocsbranch) - The official website URL (
https://sing-box.sagernet.org) for the current documentation - The specific source and target versions involved
Use WebFetch against the official site (https://sing-box.sagernet.org/migration/) for the latest migration guide, and https://sing-box.sagernet.org/configuration/ for current config reference.
Migration Reference
1.14.0 → current
Inline ACME to certificate provider
tls.acme → tls.certificate_provider (inline) or certificate_providers[] top-level array with tls.certificate_provider: "<tag>".
// Before
{ "tls": { "enabled": true, "acme": { "domain": ["example.com"], "email": "a@b.com" } } }
// After (inline)
{ "tls": { "enabled": true, "certificate_provider": { "type": "acme", "domain": ["example.com"], "email": "a@b.com" } } }
// After (referenced)
{ "certificate_providers": [{ "type": "acme", "tag": "my-cert", "domain": ["example.com"], "email": "a@b.com" }],
"tls": { "enabled": true, "certificate_provider": "my-cert" } }
Address filter fields to response matching
DNS rules using rule_set with ip_cidr items → wrap with evaluate action + match_response: true.
// Before: DNS rule referencing geoip rule-set
{ "rule_set": "geoip-cn", "action": "route", "server": "local" }
// After: add evaluate action before the rule
{ "action": "evaluate", "server": "remote" },
{ "match_response": true, "rule_set": "geoip-cn", "action": "route", "server": "local" }
DNS: remove independent_cache
Simply delete the field — DNS cache now always keys by transport name.
DNS: store_rdrc → store_dns
In experimental.cache_file: rename store_rdrc: true → store_dns: true.
DNS: ip_version / query_type behavior
These fields now apply on every DNS rule evaluation (not just client queries). They are incompatible with legacy address filter fields in the same config. Migrate to evaluate + match_response first (see above).
1.12.0 → 1.13+
DNS server address → type
The address field with URI scheme prefixes is replaced by type + server:
Old address |
New type |
New server |
|---|---|---|
"local" |
"local" |
(none) |
"tcp://1.1.1.1" |
"tcp" |
"1.1.1.1" |
"1.1.1.1" |
"udp" |
"1.1.1.1" |
"tls://1.1.1.1" |
"tls" |
"1.1.1.1" |
"https://dns.google/dns-query" |
"https" |
"dns.google" |
"quic://1.1.1.1" |
"quic" |
"1.1.1.1" |
"h3://1.1.1.1/dns-query" |
"h3" |
"1.1.1.1" |
"dhcp://auto" |
"dhcp" |
(none) |
"dhcp://en0" |
"dhcp" |
"interface": "en0" |
"fakeip" |
"fakeip" |
(none; inet4_range/inet6_range move into server) |
"rcode://refused" |
(removed) | Use action: "predefined" with rcode in a DNS rule |
Additional changes:
address_resolver→domain_resolveron the serverstrategyon server →strategyon DNS rule, ordns.strategyas defaultclient_subneton server →client_subneton DNS rule
Outbound DNS rules → domain_resolver
Remove dns.rules entries with outbound field. Add domain_resolver to the outbound's dial fields, or set route.default_domain_resolver.
// Before
{ "dns": { "rules": [{ "outbound": "any", "server": "local" }] } }
// After — per-outbound
{ "outbounds": [{ "type": "socks", "server": "example.org", "server_port": 2080,
"domain_resolver": { "server": "local", "rewrite_ttl": 60 } }] }
// After — global default
{ "route": { "default_domain_resolver": { "server": "local" } } }
domain_strategy → domain_resolver
In outbound dial fields: remove domain_strategy, add domain_resolver with strategy.
// Before
{ "domain_strategy": "prefer_ipv4" }
// After
{ "domain_resolver": { "server": "local", "strategy": "prefer_ipv4" } }
1.11.0 → 1.12+
Legacy special outbounds → rule actions
| Old | New |
|---|---|
"type": "block" outbound + route rule |
Route rule with "action": "reject" |
"type": "dns" outbound + route rule |
Route rule with "action": "hijack-dns" |
Remove the block/dns outbound entirely, use rule actions instead.
Legacy inbound fields → rule actions
Move sniff, sniff_timeout, domain_strategy from inbound config into route rules keyed by inbound tag.
// Before
{ "inbounds": [{ "type": "mixed", "sniff": true, "sniff_timeout": "1s", "domain_strategy": "prefer_ipv4" }] }
// After
{ "inbounds": [{ "type": "mixed", "tag": "in" }],
"route": { "rules": [
{ "inbound": "in", "action": "resolve", "strategy": "prefer_ipv4" },
{ "inbound": "in", "action": "sniff", "timeout": "1s" }
] } }
Destination override → route options
Move override_address / override_port from direct outbound to route rules with action: "route-options".
// Before
{ "outbounds": [{ "type": "direct", "override_address": "1.1.1.1", "override_port": 443 }] }
// After
{ "route": { "rules": [{ "action": "route-options", "override_address": "1.1.1.1", "override_port": 443 }] } }
WireGuard outbound → endpoint
Move from outbounds[{type:"wireguard"...}] to endpoints[{type:"wireguard"...}]. Key field renames:
| Old (outbound) | New (endpoint) |
|---|---|
server / server_port |
peers[].address / peers[].port |
local_address |
address |
peer_public_key |
peers[].public_key |
pre_shared_key |
peers[].pre_shared_key |
reserved |
peers[].reserved |
system_interface |
system |
interface_name |
name |
mtu |
mtu |
1.10.0 → 1.11+
TUN address merge
Merge inet4_address/inet6_address → address, same for route_address and route_exclude_address.
// Before
{ "inet4_address": "172.19.0.1/30", "inet6_address": "fdfe:dcba:9876::1/126",
"inet4_route_address": ["0.0.0.0/1"], "inet6_route_address": ["::/1"] }
// After
{ "address": ["172.19.0.1/30", "fdfe:dcba:9876::1/126"],
"route_address": ["0.0.0.0/1", "::/1"] }
1.9.0 → 1.10
domain_suffix behavior change
Values not prefixed with . now match (domain|.+\.domain) instead of literal prefix. Add . prefix to preserve old behavior, or verify intent.
process_path on Windows
Format changed from device path (\Device\HarddiskVolume1\...) to Win32 path (C:\...). Update all process_path values accordingly.
1.8.0 → 1.9+
Clash API cache → independent cache_file
Move experimental.clash_api.cache_file, cache_id, store_fakeip into experimental.cache_file. Add "enabled": true.
// Before
{ "experimental": { "clash_api": { "cache_file": "cache.db", "store_fakeip": true } } }
// After
{ "experimental": { "cache_file": { "enabled": true, "path": "cache.db", "store_fakeip": true } } }
GeoIP → rule-sets
Replace route.geoip, route.rules[].geoip, route.rules[].source_geoip with rule_set entries and rule_set rule references.
geoip: "private"→"ip_is_private": truegeoip: "cn"→"rule_set": "geoip-cn"+ remote rule-set definitionsource_geoip→rule_set_ipcidr_match_source: trueon the rule- Use
sing-box geoip exportto convert custom GeoIP databases
Requires experimental.cache_file.enabled: true for rule-set caching.
Geosite → rule-sets
Replace route.geosite, route.rules[].geosite with rule_set references.
// Before
{ "route": { "rules": [{ "geosite": "cn", "outbound": "direct" }],
"geosite": { "download_detour": "proxy" } } }
// After
{ "route": { "rules": [{ "rule_set": "geosite-cn", "outbound": "direct" }],
"rule_set": [{ "tag": "geosite-cn", "type": "remote", "format": "binary",
"url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-cn.srs",
"download_detour": "proxy" }] },
"experimental": { "cache_file": { "enabled": true } } }
Common Mistakes
- Skipping intermediate versions. Migrate step by step. A 1.8 config cannot jump directly to 1.14.
- Not running
sing-box checkafter each step. Each migration introduces changes that depend on prior steps being correct. - Forgetting
experimental.cache_file.enabled: truewhen migrating to rule-sets. - Rcode server removal.
rcode://refusedDNS servers are gone; replace withaction: "predefined"DNS rules withrcodefield. Alternatively, use a DNS rule withaction: "reject". - WireGuard endpoint address. The endpoint's
addressis the interface's own address, NOT the outbound'slocal_address. The peer address goes intopeers[].address. - Using
ENABLE_DEPRECATED_LEGACY_DNS_SERVERS=trueenv var. This masks real migration errors. Always fix the config instead. - Not tagging DNS servers. When migrating to
domain_resolver, DNS servers referenced by tag (e.g.domain_resolver: "tcp-dns") must have atagfield set.
Red Flags
- Editing a config without knowing the source version — ask the user first
- Migrating without
sing-box checkavailable — install the target binary first - Encountering unknown fields — ask for docs URLs, do not guess
sing-box checkpasses only withENABLE_DEPRECATED_*env var — the config is still brokendomain_resolverreferences a DNS server without atag— addtagto the server- Making changes without running
sing-box check— every change must be verified
Version History
- 935d27c Current 2026-07-24 11:43


