A basic framework for tracing battery drain: classify first, then localize

"Severe battery drain" reported on mobile is rarely a single cause — it's usually a combination of config-layer and system-layer issues stacking up. The config layer refers to the Clash or mihomo core's own runtime parameters, such as rule-provider refresh frequency, log output level, and DNS query behavior. The system layer refers to Android's or iOS's power-saving restrictions on background processes, and the throttling/sleep policies triggered once the OS marks the app as "inactive." When troubleshooting, narrow things down layer by layer in this order:

  1. First confirm whether drain is abnormal only when locked/backgrounded, or also high during active foreground use — the former usually points to OS scheduling, the latter to config or proxy-chain issues.
  2. Check the core's log output volume to see whether there's frequent reconnection, DNS timeout retries, or looping requests caused by rule-match failures.
  3. Check the auto-update cycle for subscriptions and rule providers, confirming whether the polling interval is set too short.
  4. Confirm whether the client has been flagged as "restricted" in the OS battery manager and is being repeatedly woken to reconnect.

The sections below expand on each of these four layers with concrete checks and parameter adjustments.

Rule providers and subscription polling: a misconfigured update cycle is a common culprit

The mihomo core supports an independent interval field (in seconds) under rule-providers in the config file, controlling how often that rule set is auto-fetched. Some client GUIs simply reuse the interval hard-coded in the config file; if the subscription author set the interval to just a few minutes, the phone will periodically wake the network module to send an HTTP request even while locked — this is one of the easiest things to overlook when troubleshooting mobile battery drain.

rule-providers:
  reject-list:
    type: http
    behavior: domain
    url: "https://example.com/rules/reject.yaml"
    path: ./rules/reject.yaml
    interval: 86400

For rule providers that don't change often (ad-block lists, direct-connection lists, etc.), set interval to 43200 seconds (12 hours) or higher, and reserve shorter cycles only for scenarios where the split-tunneling rules genuinely change frequently. Likewise, the subscription's own auto-update (the update-interval at the profile level, or the "auto-update subscription" toggle in the client GUI) should also avoid being set below one hour — otherwise the phone gets repeatedly woken in the background to run a full download-parse-reload cycle, which itself involves disk writes and a core restart, and isn't cheap per occurrence.

Log level and DNS concurrency: easily overlooked power drains in the config file

When the log level is set to debug or info, the core writes a log entry for every connection established, rule match, and DNS resolution — the sustained disk I/O and string formatting add extra CPU load. For everyday mobile use, set the log level to warning, and only switch back to debug temporarily when actively debugging connectivity issues, reverting once done.

log-level: warning

DNS settings are worth checking too. Enabling enhanced-mode: fake-ip alongside concurrent queries across multiple nameserver entries is common, but if too many upstream DNS servers are configured (especially servers outside your region) without a sensible fallback-filter, every domain resolution fires off multiple concurrent requests and waits for a timeout-based decision — which noticeably extends radio active time on unstable mobile connections.

dns:
  enable: true
  enhanced-mode: fake-ip
  nameserver:
    - 223.5.5.5
    - 119.29.29.29
  fallback:
    - "tls://8.8.8.8:853"
  fallback-filter:
    geoip: true
    geoip-code: CN

Trim the nameserver list down to two or three reliably responsive servers, and only enable fallback for domains explicitly identified as being served from outside your region, to reduce unnecessary concurrent resolution requests.

If TUN mode is enabled alongside per-packet routing logs or connection tracking, mobile CPU usage will be noticeably higher than with plain system-proxy mode. For general browsing where it isn't strictly needed, prefer system-proxy mode, and only switch to TUN mode when you need transparent global traffic routing or UDP forwarding.

Android: battery-optimization whitelist and background autostart permissions

Doze mode, introduced in Android 6.0, along with various manufacturer-customized power-saving frameworks, restricts apps that stay resident in the background and keep network connections alive after the screen turns off. In practice, the OS kills the service, the VPN framework then attempts to reconnect, and that reconnection process briefly wakes the radio hardware multiple times — creating what looks like battery drain but is actually repeated forced restarts. The fix is to add the client to the battery-optimization whitelist and keep the necessary autostart permissions enabled:

  1. Open the system battery settings

    Go to Settings → Battery → Battery Optimization (or the equivalent "power-saving policy" page on your manufacturer's OS) and find the Clash client app.

  2. Set the app to unrestricted

    Choose "Don't optimize" or "Allow background activity" to prevent the system from suspending the process's network connection after the screen locks.

  3. Check autostart and associated-launch permissions

    In the app permission manager, confirm "Autostart" is enabled. Some OS variants also require "Associated launch" permission — otherwise, once the system reclaims the VPN service, it can't recover on its own and requires the user to manually reopen the app.

  4. Confirm the persistent notification permission hasn't been disabled

    The VPN service relies on a foreground notification to maintain process priority. If notification permission is disabled by the system or the user, the service's lifespan shortens noticeably, triggering more frequent reconnection loops.

Note that whitelisting itself doesn't increase battery use — it just stops the OS from repeatedly killing and restarting the process. The real drain still comes from the rule-polling cycle and DNS concurrency settings covered earlier. Whitelisting addresses "indirect drain caused by unstable connections," and both need to be handled together to see a real improvement.

iOS: On-Demand connection and power-saving VPN profile configuration

iOS's NetworkExtension framework imposes tighter restrictions on background proxy processes, dynamically adjusting the extension's scheduling priority based on battery level, network type, and usage frequency. On iOS, the client typically connects to the system as a VPN profile, and the "On-Demand" rules in that profile directly determine when the VPN tunnel is established. Misconfigured rules cause the tunnel to repeatedly connect and disconnect in situations where it shouldn't:

  1. Open the VPN configuration management page in the client's settings and find "On-Demand" or "On-Demand Rules."
  2. Confirm the rule isn't set to "Always Connect," but instead differentiated by network type — for example, only auto-establishing a tunnel on cellular data or on networks other than a specified Wi-Fi name.
  3. If you've configured "exclude specific Wi-Fi networks," check whether the list covers trusted networks you're on for extended periods, like home or office, to avoid the VPN tunnel staying pointlessly established in those settings.
  4. In "VPN & Device Management" under system settings, confirm the profile status is normal — an abnormal status causes the system to attempt tunnel renegotiation more frequently, which also increases drain.

Once suspended by the system, an iOS network extension process can't stay active in the background the way a regular app can. Rule-provider and subscription auto-updates simply don't run while the extension is asleep — this is a design limitation, not a malfunction, and doesn't need further troubleshooting. What actually matters is how often the tunnel gets established, not whether the extension process is "always running."

It's also worth keeping the log level low on iOS, for the same reason as on Android or desktop: once the extension process is woken up to perform high-frequency log writes, that wake-up lasts longer, indirectly affecting how the system prioritizes scheduling that extension.

Long-term monitoring: watch battery trends using the built-in panel

After making these adjustments, use the client's built-in connections panel or traffic statistics to observe behavior over time, rather than judging by feel alone. Focus on three metrics: whether the active connection count keeps climbing after the screen locks, whether the DNS query count is noticeably higher than what normal browsing requires, and how often rule-provider reload events show up in the logs. If the active connection count doesn't drop noticeably after locking, that usually indicates some other background app is continuously making network requests — the root cause often lies with another app at the system level rather than Clash itself, and you can temporarily enable per-app mode to pinpoint which app stays active after the lock screen.

Troubleshooting battery drain is fundamentally a process of elimination, layer by layer. The rule-polling cycle, log level, and DNS concurrency settings at the config layer determine the core's baseline load; the battery-optimization whitelist and On-Demand rules at the system layer determine how efficiently the app stays alive in the background. Getting either layer wrong will amplify battery impact — go through this article's order step by step rather than tweaking just one setting and calling it done.

Get the Clash Client

After adjusting the rule-polling cycle, log level, and OS background policy per this article, download the latest client version and check that the default config already includes sensible power-saving parameters.

Get Client