On this page
A Linux VPN can route web connections correctly while DNS queries still leave through the Wi-Fi interface. The usual cause is not broken encryption: it is a mismatch between the program managing the tunnel and the program choosing DNS servers.
This guide uses NetworkManager with systemd-resolved as a concrete example. You will identify resolver ownership, assign VPN DNS deliberately, and verify the result with packet captures rather than relying on a single browser leak-test page.
Record a resolver baseline you can compare
A DNS leak is a query taking a path outside your intended policy. For a full-tunnel privacy profile, that might mean contacting an ISP resolver directly. For a corporate split tunnel, sending public names to the normal local resolver may be intentional. Define the desired policy before calling every outside query a leak.
Use the JSON Formatter & Syntax Linter below to maintain a small, redacted audit record containing the connection name, intended resolver, domain scope, and test outcome. It helps catch malformed JSON and makes before-and-after changes readable. It does not inspect your system, change DNS settings, or detect leaks by itself.
{
"connection": "privacy-vpn",
"policy": "full-tunnel",
"expected_dns": ["10.66.0.1"],
"routing_domain": "~.",
"physical_dns_packets_seen": false,
"ipv6_tested": false
}Do not record real browsing history or internal hostnames in a shared diagnostic document. That example describes intent, not proof: a false value must come from a completed observation, and an untested address family should remain explicitly unverified.
Identify who owns DNS on this machine
On many desktop distributions, NetworkManager supplies per-link DNS settings to systemd-resolved. Applications contact a local stub, commonly at 127.0.0.53, and resolved selects an upstream server. Other installations use dnsmasq, a static resolver file, or a VPN application's own service. Determine which arrangement you actually have.
Start with read-only inspection:
readlink -f /etc/resolv.conf
cat /etc/resolv.conf
systemctl is-active systemd-resolved
resolvectl status
nmcli -f NAME,TYPE,DEVICE connection show --activeA stub address in the file is not the upstream resolver. Inspect the per-link servers and routing domains in resolvectl status. Conversely, if resolved is not active, its commands cannot establish your application's resolver path. Consult your distribution's configuration instead of enabling a second resolver blindly.
Do not replace /etc/resolv.conf with a static public DNS address as a universal fix. That can break split DNS, local name resolution, and updates from the connection manager. First identify whether the file is a managed symlink and which component is expected to maintain it.
The authoritative systemd-resolved manual explains per-link routing and stub behavior. Its selection rules matter more than the superficial order of nameservers in a file.
Set VPN DNS and its routing scope
For this example, the VPN is already working, NetworkManager owns it, and 10.66.0.1 is a DNS server actually provided on the tunnel. A WireGuard gateway does not automatically run a resolver. Confirm reachability and service availability before assigning that address.
The following profile changes configure IPv4 DNS and the resolved route-only domain ~., which makes the VPN link the preferred scope for ordinary names. Use the actual profile name, and reactivate it only when you can tolerate a brief interruption.
nmcli connection modify "privacy-vpn" ipv4.ignore-auto-dns yes ipv4.dns "10.66.0.1" ipv4.dns-search "~." ipv4.dns-priority -50
nmcli connection down "privacy-vpn"
nmcli connection up "privacy-vpn"
resolvectl flush-caches
resolvectl statusWith NetworkManager, a negative DNS priority can exclude higher-numbered DNS configurations when active; check the installed version's behavior and active profiles. These properties do not establish VPN routes or a kill switch. The resolver address must itself travel through the intended tunnel.
For a split-DNS profile, use a specific route-only domain such as ~internal.example.com instead of ~. and avoid a global exclusion policy that suppresses required public resolution. This intentionally lets unrelated queries use other links. The NetworkManager documentation describes profile properties and DNS backend differences.
Audit IPv6 DNS separately. If the profile receives automatic IPv6 DNS servers, decide whether they belong in the policy and configure the corresponding IPv6 properties. Do not disable IPv6 merely to hide an untested configuration; either support it through the tunnel or enforce a deliberate, tested restriction.
Observe actual traffic, including application exceptions
Choose a hostname under a domain you control, vary the query label to reduce cache hits, and resolve it while capturing the physical interface. The expected result for a full-tunnel policy is no direct plaintext DNS from that interface. Queries inside the encrypted tunnel should not appear there as port 53 traffic.
Use a short packet capture with tcpdump, installed from your distribution packages. On the physical interface identified earlier, run sudo tcpdump -ni enp1s0 'udp port 53 or tcp port 53 or tcp port 853', replacing the interface name. In another terminal run resolvectl query audit-001.example.com using your controlled domain. An NXDOMAIN answer can still demonstrate that a query occurred; the packet path is what matters.
Check both UDP and TCP because DNS can use either. Port 853 observations can reveal DNS-over-TLS connections, but this capture cannot identify DNS-over-HTTPS hidden among HTTPS traffic. The Wireshark documentation is useful when a saved capture needs deeper inspection.
Review browser secure-DNS settings, container resolver files, and application-specific proxy settings. A browser may deliberately use an independent encrypted resolver. That can hide queries from the local network while still violating a policy requiring the VPN's resolver. Test the application itself, not just the operating system resolver.
Test reconnects and distinguish routing from blocking
A correct resolver choice while connected is not a fail-closed policy. When the VPN stops, NetworkManager may restore the Wi-Fi resolver and normal routing. If that is unacceptable, use a tested client kill switch or an independently maintained firewall design covering DNS and ordinary traffic.
Test startup, reconnect, suspend and resume, and tunnel failure. Repeat the physical-interface capture while a controlled application generates queries. Check IPv6 destinations as well as IPv4. An unavailable resolver can create long delays without providing any evidence that fallback is safely blocked.
Keep a rollback record before changing a working corporate or remote-access profile. Save the original DNS values locally and confirm how to restore them through the connection manager. Avoid editing a production server's resolver remotely without console access, especially when management tools depend on internal DNS.
Use the JSON formatter again to compare sanitized findings after each test. Record which cases passed and which remain untested rather than reporting one blanket “no leaks” result. For scripted checks, continue with the VPN connection automation guide. Follow employer policy when changing managed DNS, since internal resolvers may provide required security and service discovery.
Key takeaways
- Establish resolver ownership before editing files or adding another DNS service.
- Make routing scope explicit: full-tunnel DNS and split DNS solve different problems.
- Verify real packets, browser overrides, IPv6, and reconnect behavior rather than trusting one test page.
- Separate resolver selection from fail-closed enforcement, and preserve a tested rollback path.
Reach designers & developers at work
Put your product next to the tools people use every day. One sponsor per placement, no tracking pixels.
Become a sponsor