Start your 3-day free trial
Sign up to experience all premium features at no cost.
*Available only to new users. Each user is limited to one trial.


sing-box failover uses a urltest outbound that probes each exit and routes new connections to the best healthy one, while Mihomo offers a fallback group that always prefers the first available exit in your list. This guide configures both for two exits you own, then shows how to measure switching time, recovery, and what happens to open connections, DNS, and UDP.
Key Takeaways
- Failover keeps one exit active and moves to another when checks fail; it does not add bandwidth.
- sing-box
urltestchooses by measured latency within a tolerance, not by your list order.- Mihomo
fallbackpicks the first available proxy in order, so it returns to your preferred exit after recovery.- Switching takes up to about one check interval plus the timeout, so measure it instead of assuming.
- An HTTP health check can pass while UDP on the same exit is broken.
This procedure is based on the official sources listed below; a complete end-to-end test has not been performed. It assumes sing-box 1.14.3 or Mihomo v1.19.32 on a Linux client and two working proxy exits you administer or are authorized to use. Plan ownership and recovery first with the self-hosting checklist.
Failover keeps traffic on one exit and changes exit only when that one stops passing checks. Load balancing spreads connections across several exits at once, and bonding combines links for one flow. The comparison of connection bonding, load sharing, and failover explains why only bonding can raise single-connection throughput.
| Tool and type | How it picks an exit | After the preferred exit recovers |
|---|---|---|
sing-box urltest | Lowest measured delay, with a tolerance to avoid flapping | Moves back only if the recovered exit is faster by more than the tolerance |
sing-box selector | Manual choice, controlled through the Clash API | Stays where you set it |
Mihomo fallback | First available proxy in list order | Returns to the first proxy once it passes again |
Mihomo url-test | Lowest measured delay, with a tolerance | Like sing-box urltest |
The sing-box urltest outbound tests a list of outbounds against a URL, by default every 3m with a 50 ms tolerance.[1] Its selector outbound can be switched only through the Clash API.[2] Mihomo's fallback group selects "the first available node" in the order of proxies when the current one times out.[3] Running two exits doubles your maintenance work, so weigh self-hosted servers against a managed VPN if uptime is the only goal.
You need two independent exits, here Shadowsocks 2022 servers at 203.0.113.10 (exit A) and 198.51.100.20 (exit B), each with its own key. They should sit in different data centers or providers, otherwise one outage takes both down. Confirm each works on its own before grouping them.
Both tools test a URL through each exit. sing-box falls back to a Google generate_204 address when url is empty.[1] Use an endpoint you control instead, such as https://check.example.net/generate_204, and add a second path such as /ip that returns the caller's address. Probing a third-party service every minute from several exits should respect that service's terms, and your own endpoint gives you logs to confirm which exit made each probe.
If sing-box is not installed yet, the sing-box Linux client guide covers installing a pinned build and running a local listener. Save this as a private failover.json:
{
"log": {"level": "warn"},
"inbounds": [{"type": "mixed", "tag": "local", "listen": "127.0.0.1", "listen_port": 1084}],
"outbounds": [
{
"type": "urltest",
"tag": "auto-exit",
"outbounds": ["exit-a", "exit-b"],
"url": "https://check.example.net/generate_204",
"interval": "1m",
"tolerance": 300,
"interrupt_exist_connections": true
},
{"type": "shadowsocks", "tag": "exit-a", "server": "203.0.113.10", "server_port": 8388,
"method": "2022-blake3-aes-128-gcm", "password": "REPLACE_KEY_A"},
{"type": "shadowsocks", "tag": "exit-b", "server": "198.51.100.20", "server_port": 8388,
"method": "2022-blake3-aes-128-gcm", "password": "REPLACE_KEY_B"}
],
"route": {"final": "auto-exit"}
}
A shorter interval detects failure sooner at the cost of more probes. A larger tolerance stops sing-box urltest from moving between two healthy exits whose delays differ slightly, which matters when sessions dislike address changes. interrupt_exist_connections closes inbound connections when the selected outbound changes; internal connections are always interrupted.[1] Leave it false if you prefer old connections to fail on their own.
Check the file with sing-box check -c failover.json using your pinned binary. A passing check confirms syntax, not that either exit works.
For Mihomo, save a private config.yaml in its own directory:
mixed-port: 7891
allow-lan: false
bind-address: 127.0.0.1
mode: rule
log-level: warning
external-controller: 127.0.0.1:9090
secret: "REPLACE_LONG_RANDOM_SECRET"
proxies:
- {name: exit-a, type: ss, server: 203.0.113.10, port: 8388, cipher: 2022-blake3-aes-128-gcm, password: "REPLACE_KEY_A", udp: true}
- {name: exit-b, type: ss, server: 198.51.100.20, port: 8388, cipher: 2022-blake3-aes-128-gcm, password: "REPLACE_KEY_B", udp: true}
proxy-groups:
- name: failover
type: fallback
proxies: [exit-a, exit-b]
url: https://check.example.net/generate_204
interval: 60
timeout: 5000
lazy: false
rules:
- MATCH,failover
The Mihomo fallback group tests only the proxies listed in its proxies field; interval is in seconds, timeout in milliseconds, and lazy defaults to true, which skips tests for a group that is not selected.[4] Setting lazy: false keeps the group tested even when idle, so it has a current answer when traffic starts. max-failed-times (default 5) forces a fresh health check after repeated failures.[4] The 2022 cipher names come from Mihomo's Shadowsocks reference.[5]
Keep allow-lan: false and bind-address: 127.0.0.1 so other devices cannot use the port.[6] The controller lets you read the group state; protect it as described in the guide to securing the Mihomo controller API.
Start one tool at a time in the foreground and point a test terminal at its local port (1084 for sing-box, 7891 for Mihomo). For Mihomo, read the group's current choice through the API, which accepts a bearer secret:[7]
curl -s -H "Authorization: Bearer $MIHOMO_SECRET" http://127.0.0.1:9090/proxies/failover
The response names the proxy the group is using now. For sing-box, compare the address returned by your /ip endpoint with each exit's known egress address. In a browser set to the same local port, the IP address tool should show exit A or exit B, never your own connection.
Run a timestamped probe loop through the local port, then stop the proxy service on exit A during a maintenance window. Do not cut a shared server that other people depend on.
while true; do
printf '%s ' "$(date +%T)"
curl -s -m 5 --proxy socks5h://127.0.0.1:1084 https://check.example.net/ip || printf 'FAIL'
echo; sleep 2
done
| Check method | Expected behavior | If it differs |
|---|---|---|
| Both exits up | Loop shows one exit address consistently | Address alternates between exits: raise the sing-box tolerance |
| Stop exit A's proxy service | FAIL lines, then exit B's address within about one interval plus timeout | Group never switches: check the URL is reachable through exit B |
Restore exit A, Mihomo fallback | Returns to exit A after the next successful check | Stays on B: confirm list order and lazy: false |
Restore exit A, sing-box urltest | Stays on B unless A is faster by more than the tolerance | Constant switching: tolerance is too low for your delay spread |
| Stop both exits | All requests fail | Requests succeed: traffic is bypassing the group |
Record the time from stopping exit A to the first successful line through exit B. That gap, not the configured interval, is your real switching delay. Repeat the test a few times, because the check timer is not synchronized with the moment you stop the service.
Open TCP connections stay on the exit they started on. They fail if that exit dies, or close early when sing-box interrupt_exist_connections is on; applications must reconnect either way. New connections use whatever exit the group has chosen at that moment.
Health checks are HTTP requests, so they exercise TCP through each exit. An exit can pass the check while its UDP relay is blocked or broken, and UDP-based apps will then fail without triggering a switch. Test a UDP workload through each exit separately, and in Mihomo use the group's disable-udp option if you would rather block UDP than send it to an unreliable path.[4]
DNS behavior depends on your mode. With FakeIP, the client hands applications synthetic addresses and the real lookup happens later through the selected exit; the FakeIP versus redir-host comparison explains why a switch can leave cached mappings that look stale. Clear application caches or restart the app if a site keeps failing after a switch.
Keep each exit's standalone profile. To roll back, stop the failover client, restore the application's previous proxy setting, and confirm a fresh request through the single exit. Remove the test endpoint's probe logs on your schedule, and rotate any key you pasted into a shared terminal during testing.
urltest when you want the fastest healthy exit, and Mihomo fallback when you want a fixed preference order.The documented urltest outbound selects by measured delay within a tolerance, not list order. For a fixed preference, use Mihomo's fallback group or switch a selector manually.
Expect up to about one test interval plus the probe time, depending on when the outage starts. Measure it with a timestamped loop instead of trusting the setting alone.
Usually not. Existing connections stay on the failed exit or are closed, and the application must reconnect through the new exit to continue its session.
The check is an HTTP request over TCP. It proves that TCP forwarding works through that exit, not that UDP relay or QUIC traffic is allowed.
You can, but your own endpoint avoids depending on someone else's service and its terms, and its logs show which exit made each probe.
It depends on your goal. Load balancing spreads connections across exits but cannot speed up a single flow, and it can confuse services that expect one address.
The fallback type always prefers the first available proxy in its list. Once the first exit passes a health check again, new connections move back to it.
Disclaimer: This article is for general informational purposes only and does not constitute legal, technical, or other professional advice. We make no guarantees regarding the accuracy, completeness, or timeliness of the content.
Sources:
Sources checked 14 October 2026.
Related Articles:
Sign up to experience all premium features at no cost.
*Available only to new users. Each user is limited to one trial.





