Best Free VPN for Chrome + Always-On (2026)

How to Set Up Always-On VPN on Android: Technical Implementation Guide

Always-on VPN is a critical security feature that ensures your Android device maintains a constant VPN connection, automatically reconnecting when connectivity is restored. This technical guide walks you through the implementation options, from built-in Android features to custom VPN app configurations.

Understanding Always-On VPN in Android

Android provides native always-on VPN support since Android 4.4 (API 20), but the implementation has evolved significantly. There are two primary approaches to achieving always-on VPN:

  1. Android’s native always-on VPN feature - Built into the OS, works with VPN apps that support the API
  2. Third-party VPN applications - Custom implementations with additional features

Understanding the difference between these approaches matters before you configure anything. The native Android feature relies on the OS itself enforcing the VPN policy. meaning it kicks in before any app starts, survives app crashes, and persists across reboots without any additional setup on your part. Third-party implementations give you more control (custom kill switch logic, split tunneling per app, protocol selection) but introduce more moving parts that can fail independently.

For personal devices where you want simple, reliable protection, the native feature is the right starting point. For enterprise deployments or cases where you need granular control over what traffic goes through the VPN, a third-party implementation or MDM-enforced configuration gives you more use.

How Android Enforces Always-On VPN Internally

When always-on VPN is enabled at the OS level, Android places a routing rule at the top of the policy routing table that sends all traffic through the VPN interface. If the VPN interface is not available. because the app has not started yet or the connection dropped. Android consults the block-untunneled-traffic policy. With the kill switch enabled, it drops traffic rather than routing it over the plain network. Without the kill switch, traffic falls through to the default network.

This is why the kill switch setting matters so much. Always-on VPN without a kill switch is not actually always-on for privacy purposes. there are windows during startup and reconnection where traffic can leak.

Method 1: Using Android’s Native Always-On VPN

Prerequisites

  • Android 5.0 (API 21) or higher for full functionality
  • A VPN app that supports the always-on VPN API
  • Device administrator privileges (for some configurations)

Not all VPN apps support Android’s always-on API even if they claim always-on behavior. To check whether an app supports it, look for the android.net.VpnService.SUPPORTS_ALWAYS_ON metadata entry in the app’s manifest. If it is absent or set to false, Android will grey out the always-on toggle in Settings for that app.

Popular VPN apps with confirmed native always-on support include: WireGuard for Android, OpenVPN for Android, Mullvad, ProtonVPN, and the built-in IKEv2/IPsec VPN client on devices running Android 8 or newer.

Step-by-Step Configuration

For Users (Device Settings)

  1. Open Settings on your Android device
  2. Navigate to Network & Internet → VPN (or Connections → VPN on older versions)
  3. Tap the gear icon next to your configured VPN
  4. Enable Always-on VPN toggle
  5. Optionally, enable Connect on demand for automatic connection rules

On Samsung devices running One UI, the path is Settings → Connections → More connection settings → VPN. On Pixel devices running stock Android, it is Settings → Network & internet → VPN.

Alternative: Using adb to check VPN status
adb shell settings get global always_on_vpn_mode
adb shell settings get global always_on_vpn_enabled

You can also use adb to set always-on VPN programmatically, which is useful for testing MDM configurations or automating device setup:

Enable always-on VPN for a specific package
adb shell settings put global always_on_vpn_mode <package-name>
Enable block untunneled (kill switch)
adb shell settings put global always_on_vpn_lockdown 1
Verify the settings took effect
adb shell settings get global always_on_vpn_mode
adb shell settings get global always_on_vpn_lockdown

For Developers (Programmatic Implementation)

If you’re building a VPN app, implement the AlwaysOnVpnService:

// AndroidManifest.xml additions
<service
 android:name=".MyVpnService"
 android:permission="android.permission.BIND_VPN_SERVICE"
 android:exported="false">
 <intent-filter>
 <action android:name="android.net.VpnService" />
 </intent-filter>
 <meta-data
 android:name="android.net.VpnService.SUPPORTS_ALWAYS_ON"
 android:value="true" />
</service>
// MyVpnService.kt
class MyVpnService : VpnService() {
 override fun onCreate() {
 super.onCreate()
 // Initialize VPN interface
 }
 override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
 // Handle always-on VPN triggers
 return START_STICKY
 }
 companion object {
 // Indicate always-on support
 const val SUPPORTS_ALWAYS_ON = true
 }
}

The START_STICKY return value in onStartCommand is critical. It tells Android to recreate the service if it is killed. without this, the VPN drops and never comes back after the OS reclaims memory under pressure. The always-on API also sends an explicit ACTION_VPN_STOPPED broadcast when it detects the service has died, which your implementation should handle to trigger reconnection:

// Register a BroadcastReceiver for VPN state changes
class VpnStateReceiver : BroadcastReceiver() {
 override fun onReceive(context: Context, intent: Intent) {
 when (intent.action) {
 "android.net.VpnService.ACTION_VPN_CONNECTED" -> {
 Log.d("VPN", "VPN connected")
 }
 "android.net.VpnService.ACTION_VPN_DISCONNECTED" -> {
 Log.d("VPN", "VPN disconnected. triggering reconnect")
 context.startService(Intent(context, MyVpnService::class.java))
 }
 }
 }
}

Configuring via MDM/EMM

For enterprise deployments, always-on VPN can be enforced via mobile device management:

<!-- mdm_configuration.xml -->
<device-admin xmlns:android="http://schemas.android.com/apk/res/android">
 <policies>
 <force-on-vpn-profiles>
 <profile name="corporate-vpn">
 <vpn-profile>
 <always-on>true</always-on>
 <connect-on-boot>true</connect-on-boot>
 <connect-on-demand>true</connect-on-demand>
 </vpn-profile>
 </profile>
 </force-on-vpn-profiles>
 </policies>
</device-admin>

MDM-enforced always-on VPN uses the DevicePolicyManager.setAlwaysOnVpnPackage() API introduced in Android 7.0. When set by an MDM, users cannot disable the always-on behavior in Settings. the toggle appears but is grayed out with a note that it is managed by the organization. This is the correct approach for corporate devices where VPN enforcement is a compliance requirement.

// MDM/DeviceOwner implementation
val dpm = context.getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager
val adminComponent = ComponentName(context, MyDeviceAdminReceiver::class.java)
// Set always-on VPN package and enable lockdown
dpm.setAlwaysOnVpnPackage(
 adminComponent,
 "com.example.myvpnapp",
 /* lockdown = */ true,
 /* lockdownWhitelist = */ emptySet()
)

The lockdownWhitelist parameter, added in Android 11, allows you to specify a set of package names that are permitted to bypass the VPN even when lockdown is enabled. This is useful for allowing system update services or certificate validation to operate while still blocking all other untunneled traffic.

Method 2: Third-Party VPN Apps with Always-On

Popular VPN providers implement their own always-on mechanisms:

OpenVPN for Android

OpenVPN configuration for always-connect
Add to your .ovpn file
keepalive 10 60
persist-tun
persist-key
pull
nobind

The keepalive 10 60 directive instructs the client to send a ping every 10 seconds and restart the connection if no response is received within 60 seconds. persist-tun prevents the tunnel interface from being closed on reconnect, which reduces the window during which traffic could leak. persist-key does the same for TLS keys, avoiding a full re-handshake on every reconnection.

For OpenVPN for Android specifically, you should also configure the app-level settings:

  • Enable “Reconnect on reboot” in the app’s settings
  • Set “Network state change reconnect” to “Always”
  • Enable “Pause VPN while screen is off” only if battery life is more important than continuous protection

WireGuard (Android)

WireGuard offers excellent performance with always-on capabilities:

wg0.conf - WireGuard interface configuration
[Interface]
PrivateKey = <your-private-key>
Address = 10.0.0.2/32
DNS = 1.1.1.1
Table = auto
PostUp = iptables -I FORWARD -i wg0 -j ACCEPT
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT
[Peer]
PublicKey = <server-public-key>
AllowedIPs = 0.0.0.0/0, ::/0
Endpoint = vpn.example.com:51820
PersistentKeepalive = 25

The PersistentKeepalive = 25 line is the most important setting for always-on behavior. WireGuard is designed to be silent. it does not send traffic until there is traffic to send. Behind NAT (which is nearly universal on mobile networks), the NAT mapping expires after a period of inactivity. PersistentKeepalive forces the client to send a keepalive packet every 25 seconds, preventing NAT timeout and keeping the tunnel ready to receive incoming traffic.

Setting AllowedIPs = 0.0.0.0/0, ::/0 routes all IPv4 and IPv6 traffic through the tunnel. This is the correct setting for always-on full-tunnel VPN. If you use split tunneling, restrict this to specific subnets. but note that split tunneling reduces the protection always-on VPN is meant to provide.

Comparing OpenVPN vs WireGuard for Always-On Use

Feature OpenVPN WireGuard
Battery impact Higher (software crypto) Lower (kernel-level crypto)
Reconnection speed Slower (full TLS handshake) Fast (stateless reconnect)
Protocol overhead Higher Lower
Android support Via third-party app Native app + official API support
Kill switch (app-level) App-dependent Relies on Android native
Configuration complexity Higher (.ovpn file format) Simpler (INI format)
Firewall traversal Easier (uses TCP 443) Harder (UDP only)

For always-on use cases where the device will frequently switch between WiFi and cellular, WireGuard reconnects faster and drains less battery. OpenVPN over TCP port 443 is more reliable in environments with restrictive firewalls (airports, hotels, corporate networks that block non-HTTP traffic).

Tasker Automation (For Advanced Users)

Create custom always-on logic using Tasker:

<TaskerData sr="" dvi="1" tv="5.12">
 <Task sr="task4">
 <cdate>1700000000000</cdate>
 <edate>1700100000000</edate>
 <id>4</id>
 <nme>VPN Auto-Reconnect</nme>
 <pri>100</pri>
 <Action sr="act0">
 <code>37</code>
 <Int sr="arg1">0</Int>
 <Str sr="arg2">VPN Connected</Str>
 <Str sr="arg3">%VPNSTATE</Str>
 </Action>
 <Action sr="act1">
 <code>123</code>
 <Str sr="arg0">net.vpn.connect</Str>
 <Str sr="arg1">your.vpn.app</Str>
 </Action>
 </Task>
</TaskerData>

The Tasker approach lets you create conditional always-on logic that the native OS feature cannot handle. Common use cases include: reconnecting only when on untrusted networks (anything that is not your home WiFi SSID), pausing the VPN when connected to a specific corporate network that blocks VPN, and logging connection drops with timestamps for auditing.

Understanding VPN Kill Switch

A kill switch prevents data leaks when the VPN connection drops unexpectedly:

Implementation Types

  1. System-level kill switch (Android 11+)
    • Integrated into the OS VPN API
    • Blocks all non-VPN traffic when disconnected
  2. Application-level kill switch
    • Implemented within the VPN app
    • May allow some traffic leakage during transition

The system-level kill switch is implemented via the BLOCK_UNTUNNELED_TRAFFIC policy in the VPN lockdown API. When enabled, Android inserts an UNREACHABLE rule into the routing table for all non-VPN routes. Traffic that would previously fall back to the plain network instead hits this rule and is dropped with no further routing attempts. The gap between the VPN dropping and this rule taking effect is measured in milliseconds at the kernel level. effectively zero from a practical standpoint.

Application-level kill switches work differently. When the VPN app detects a disconnect, it programmatically blocks outbound traffic using Android’s VpnService.protect() mechanism or custom iptables rules. The enforcement depends entirely on the app’s reliability and response time, which varies. App-level kill switches from reputable providers are generally reliable but are technically inferior to the system-level approach because they have a small reactive gap.

Testing Your Kill Switch

Test script to verify kill switch functionality
#!/bin/bash
Connect to VPN
vpn-connect
Start traffic monitoring
tcpdump -i any -w /tmp/vpn_test.pcap &
Simulate disconnect
vpn-disconnect
Check if any packets leaked
If kill switch works, no packets should be captured
Alternative: Check routing table
ip route
Should show "unreachable" or VPN-only routes

For a more thorough test, use a purpose-built leak testing tool rather than manual tcpdump. The recommended approach on Android is to use an app like DNS Leak Test or ipleak.net in a browser while manually disconnecting the VPN from Settings. Watch for the displayed IP address changing to your ISP’s IP. that is a kill switch failure. The IP should either remain the VPN server’s IP or the page should fail to load entirely.

You can also verify kill switch behavior with adb:

After disabling VPN, check if traffic is blocked
adb shell ping -c 1 8.8.8.8
Should return: Network is unreachable (if kill switch is active)
Or: 64 bytes from 8.8.8.8 (kill switch is NOT active. a leak)

Troubleshooting Common Issues

Always-On VPN Not Connecting

  1. Check VPN app permissions
     adb shell pm grant <vpn-app> android.permission.INTERNET
     adb shell pm grant <vpn-app> android.permission.ACCESS_NETWORK_STATE
    
  2. Verify VPN service is running
     adb shell dumpsys vpn
    
  3. Check for conflicting VPN profiles
     adb shell settings list global | grep vpn
    

If adb shell dumpsys vpn shows the service in a perpetual “connecting” state, the most common causes are: incorrect server address or credentials, a firewall blocking the VPN protocol port, or a certificate validation failure. Check the VPN app’s internal logs first. most apps expose diagnostic logs somewhere in Settings → Advanced.

If always-on VPN connects on demand but drops after a few minutes, the issue is usually Android’s battery optimization killing the VPN service process. Navigate to Settings → Apps → [VPN app] → Battery and set it to “Unrestricted.” On some OEM Android versions (MIUI, One UI, ColorOS), there is an additional background process whitelist that must be configured separately.

Battery Drain Issues

Always-on VPN can impact battery life. Mitigate by:

  • Using WireGuard (more efficient than OpenVPN)
  • Configuring appropriate keepalive intervals
  • Using split tunneling when possible
  • Disabling always-on for trusted networks

Keepalive interval tuning deserves attention. A keepalive every 10 seconds keeps NAT mappings alive but sends 86,400 keepalive packets per day. each requiring the radio to wake up briefly. A keepalive every 60 seconds reduces radio wakeups by 6x with negligible impact on NAT reliability in most network environments. Test your specific network conditions: some cellular carriers have aggressive NAT timeouts (as short as 30 seconds on some T-Mobile configurations) that require more frequent keepalives.

WireGuard’s kernel-level implementation means the crypto operations run without crossing the user-space/kernel-space boundary. OpenVPN on Android runs entirely in user space, which adds overhead for every packet. On modern Android devices with AES hardware acceleration, the difference is smaller than it once was, but WireGuard still wins on battery in real-world testing by a meaningful margin on high-traffic workloads.

Network Transition Issues

When switching between WiFi and cellular:

// Handle network transitions in VpnService
override fun onNetworkChanged(network: Network?) {
 super.onNetworkChanged(network)
 // Automatically reconnect on network change
 if (network != null) {
 connect()
 }
}

Android 9 introduced the ConnectivityManager.NetworkCallback API that VPN services can use to receive network change events before the routing table updates. Implementing this in your VPN service allows proactive reconnection rather than reactive reconnection. the tunnel is re-established on the new network before the OS tries to route traffic through the old interface.

private val networkCallback = object : ConnectivityManager.NetworkCallback() {
 override fun onAvailable(network: Network) {
 super.onAvailable(network)
 // New network available. migrate tunnel
 migrateTunnel(network)
 }
 override fun onLost(network: Network) {
 super.onLost(network)
 // Network lost. prepare for reconnect on next available network
 pauseTunnel()
 }
}
// Register in onCreate
connectivityManager.registerDefaultNetworkCallback(networkCallback)

WireGuard handles this more gracefully than OpenVPN by design. Because WireGuard tunnels are identified by key pairs rather than TCP connections, switching from WiFi to cellular does not require tearing down and rebuilding the tunnel. the packets simply start arriving from a new source IP, and the server’s WireGuard implementation handles the transition transparently.

Security Considerations

Certificate Pinning

Implement certificate pinning to prevent MITM attacks:

val certificatePinner = CertificatePinner.Builder()
 .add("vpn.example.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
 .build()
val okHttpClient = OkHttpClient.Builder()
 .certificatePinner(certificatePinner)
 .build()

Certificate pinning is relevant primarily for VPN management traffic. the API calls your VPN app makes to your server to authenticate, fetch configuration, or check for updates. The tunnel traffic itself is protected by the VPN protocol’s own cryptography. For WireGuard, the public key embedded in the configuration IS the pin. you cannot be MITMed without the attacker having your server’s private key.

DNS Leak Prevention

Ensure all DNS queries route through the VPN:

// In your VPN service configuration
protect(socket) // Protect the socket from being bypassed

DNS leaks are among the most common VPN security failures and among the hardest to detect without testing. Even with a properly configured VPN, Android’s system DNS resolver can send queries directly to the carrier DNS server under certain conditions. particularly during the brief window between network connection and VPN tunnel establishment.

Configure the VPN tunnel to specify explicit DNS servers rather than relying on DHCP-provided DNS:

val builder = VpnService.Builder()
builder.addDnsServer("10.8.0.1") // VPN server's DNS
builder.addDnsServer("1.1.1.1") // Fallback. still routes through tunnel
// Do NOT add the carrier DNS server here

Also consider using Android 9’s Private DNS (DNS over TLS) feature as an additional layer, pointed at a DNS provider that logs nothing. This protects DNS queries even in the brief windows where the VPN is not tunneling traffic.

Verifying Your Configuration Is Working

After setup, verify that always-on VPN is genuinely protecting all traffic:

  1. With VPN connected, visit ipleak.net or dnsleaktest.com. your shown IP should be the VPN server’s IP, and DNS servers shown should be the VPN’s DNS, not your ISP’s
  2. Restart the device and check whether the VPN reconnects automatically before you open any apps
  3. Toggle WiFi off and back on. the VPN should reconnect within a few seconds
  4. Uninstall and reinstall the VPN app. always-on should re-engage when the app reinstalls and starts

Conclusion

Setting up always-on VPN on Android requires understanding both the native OS capabilities and third-party implementation options. For most users, enabling the built-in always-on VPN feature provides adequate protection. Advanced users and enterprises can use custom implementations with additional features like split tunneling, custom kill switches, and granular control.

The single most important configuration choice is the kill switch. Always-on VPN without a kill switch leaves traffic-leak windows that undermine the feature’s purpose. Enable system-level lockdown mode whenever possible, verify it is working with a leak test, and prefer WireGuard over OpenVPN for the combination of performance, battery life, and clean network transition handling.

Remember to regularly test your VPN configuration to ensure it functions correctly, especially after system updates or VPN app changes.


Try it: Paste your error into our Error Diagnostic for an instant fix.

I'm a solo developer in Vietnam. 50K Chrome extension users. $500K+ on Upwork. 5 Claude Max subscriptions running agent fleets in parallel. These are my actual CLAUDE.md templates, orchestration configs, and prompts. Not a course. Not theory. The files I copy into every project before I write a line of code. **[See what's inside →](https://zovo.one/lifetime?utm_source=ccg&utm_medium=cta-default&utm_campaign=how-to-set-up-always-on-vpn-on-android-technically)** $99 once. Free forever. 47/500 founding spots left.

Related Reading

Built by theluckystrike. More at zovo.one