A per-app kill switch sounds like the careful choice: only the work client loses its network when the tunnel drops, and everything else keeps working. Then someone notices the personal browser still loaded a page, the sync helper shipped with the same suite kept retrying, and a cached name still resolved to an address that never touched the tunnel. The label on the feature and the set of connections it actually protects are two different things. Work out the protected set first, because that set decides whether per-app enforcement or a device-wide lockdown is the honest description.

Start with the set, not the label

A per-app policy protects selected applications. A device lockdown aims at a broader network boundary, usually with documented exceptions for local printing, management traffic, or captive portals. Neither label is complete until you can answer three questions: what happens to excluded apps, what happens to helpers those apps start, and what happens to connections opened while protection is unavailable.

Write those answers down before you touch a client setting. If the requirement is "this payroll client must never talk to the internet outside the tunnel, and my browser may", the set is small and per-app enforcement fits. If the requirement is "nothing on this device reaches the internet unprotected, except the update service", no per-app policy will produce it, because the apps you did not name are the problem.

Per-app enforcement matches identifiers, not program names

Android exposes per-app VPN through packages, and its VpnService documentation describes how that interacts with always-on operation and blocking. iOS configures per-app VPN through a management profile and matches by app or by domain. Windows and Linux have no per-process route at all, so the same idea is approximated with a firewall rule scoped to a program or with a rule that matches the owning user.

The boundary is the identifier, and identifiers are easy to step around. An application that hands a URL to the system browser, or opens a document in a viewer, crosses into a process your policy never named. A terminate-on-drop option is also not a packet filter: it kills the app you configured, while a child process, a queue worker, or a browser tab can keep an existing socket alive.

Lockdown covers what a per-app policy cannot see

Device-wide lockdown removes the choice. Every outbound packet that does not belong to the tunnel is dropped, including traffic from processes your app policy never knew about. On Android that is always-on VPN combined with the block-connections setting, and the enforcement happens in the system, not in the client window. On a desktop it is a persistent firewall rule or a default route pointed at an unreachable gateway.

The cost is that everything else stops, so the exemptions become part of the design rather than an afterthought. Local printers, service discovery, captive portal logins, management agents, and OS updates each need a documented hole, because each one is a path that stays open when the tunnel is down. The lockdown is only as strong as the shortest entry on that list.

DNS, IPv6 and background helpers hide the leaks

A protected app failing to resolve a name does not establish that cached answers or direct-IP traffic is blocked. If the resolver is still reachable outside the tunnel, the name resolves and the connect fails, which looks like a working kill switch and is not. Worse, a cached record can point at an address the tunnel does not carry, so the connection goes out directly while the app reports a VPN error.

IPv6 needs its own rule on every platform that runs it, because a v4 drop rule does not cover a v6 route. On mobile, a private DNS setting can move resolution inside an encrypted channel that the device policy does not inspect. And background helpers are the usual culprit on desktop: an updater or a sync tray process starts before the VPN client and keeps its own connection, so it never sees the changed route.

Test three apps and two transitions

TestWhat it proves
Included app, by name and by direct IPthe policy is applied, not just the resolver
Excluded app, same checksthe exclusion is intentional and documented
Helper or updater processnothing inherited an older route
Tunnel drop, then Wi-Fi to cellularthe block survives the transition

Run each row twice, once with a hostname and once with a literal address, and once more over IPv6 if the network offers it. A pass means the included app stopped, the excluded app behaved the way you wrote down, and the helper did not slip out. Record the direct behavior you decided was acceptable, so the next administrator can tell a deliberate exemption from a failure.

Then describe the result in one sentence that names the applications and the conditions, for example: the payroll client has no route outside the tunnel, the browser is direct by design, and everything else stops when the tunnel is down. If that sentence is hard to write, the policy is still two policies fighting each other.

Artihkkalat mat laktásit