An application container that shares a VPN container's network namespace has no interface of its own, so it is easy to assume it cannot send anything when the tunnel fails. That assumption is wrong in the failure mode that matters. If the VPN process dies while the namespace survives, the application keeps its sockets and the namespace keeps its routes, and traffic continues on whatever path is still installed. A real kill switch is an explicit filtering policy plus tests of the runtime lifecycle, not a startup dependency.

Why namespace sharing is not a kill switch

Sharing a namespace means the app uses the VPN container's interfaces, addresses, and route table. It removes the app's independent path to the network while the shared namespace is intact, and it says nothing about what that namespace will do when the tunnel goes away.

Compose dependency conditions can delay an app's startup until a dependency is healthy. That covers one moment in time and no later failure, so it is a startup ordering tool rather than a continuous egress firewall.

What actually happens when the container exits

Distinguish three events. The VPN process inside the container can die while the container stays running. The container can stop or be removed, which destroys the namespace. The container can be recreated, which builds a new namespace and new rules.

In the first case the namespace persists, the default route may still point at the physical interface, and the application keeps sending directly. In the second case the dependent app usually loses connectivity entirely, because its namespace disappeared. In the third case the app may reconnect to a fresh namespace before the tunnel is ready. Each case needs a different test, and a design that only covers the orderly disconnect covers the least interesting one.

Build an explicit egress policy

Write the policy inside the shared namespace so it applies to the application's packets. The shape is a default drop with narrow exceptions: loopback, the tunnel interface, and the specific endpoint address and port the tunnel transport needs.

The endpoint exception has to be in place before the tunnel is up, or the VPN client cannot reach its server. That is why the rule set allows the outer transport explicitly instead of allowing the physical interface in general. Add IPv6 rules as well if the namespace has IPv6 connectivity, because an IPv4-only policy leaves the other family open.

DNS belongs in the policy too. If the app resolves names through a resolver reachable only inside the tunnel, the drop rule will break resolution when the tunnel is down, which is the correct behavior for a kill switch. Decide deliberately whether that is what you want.

Test the lifecycle, not just the disconnect

Send a fresh harmless request while the tunnel is healthy, then kill the VPN process and send another. Repeat after a container stop, after a restart, and after recreation. A fresh request matters because an existing connection may survive a rule change that blocks new ones.

Do not rely on a health check or an exit-IP lookup as the blocking mechanism. A check observes state and reports it; a policy controls traffic. The two belong in the same deployment, and they are not substitutes.

Record the rules so the test survives an update

Write the expected outcome for each failure case into the deployment documentation, along with the rules that implement it. That record lets the same test run after an image update or a runtime upgrade, when namespace behavior or default routes can change without anyone editing the compose file.

Keep the VPN endpoint's required outer path separate from unrestricted application egress, and keep DNS and IPv6 inside the intended coverage. If the policy allows the whole physical interface for convenience, the kill switch is gone. The Docker networking documentation describes the shared-namespace arrangement that the policy is built on, and it is the reference to check when the runtime's behavior is in question.

Lahatsoratra mifandraika