Walk the connection from the moment it comes up: the client authenticates, pppd negotiates, and then something writes DNS settings into the operating system. If all you know is that the VPN changed your resolver, you do not yet know which of those stages did it. Two of them can, and on a client using the documented pppd integration they run close enough together to look like a single actor. Naming the writer is the first step; choosing its replacement is the second, and it is a separate decision with its own failure modes.

The sequence below follows the client outward: what the connection changed, which switch suppresses which writer, who is supposed to take over, and how to confirm the result survives a reconnect. Each stage leaves a different trace, and the trace is what tells you whether the handoff actually happened.

Start with what the connection actually changed

Connect once with your normal invocation and compare resolution before and after. On a Linux host running systemd-resolved, resolvectl status prints per-link DNS servers and search domains, and a new tun or ppp link carrying its own servers is the fingerprint of a client-side update. On macOS, scutil --dns shows the resolver configuration in the order applications consult it.

Compare that against the file you expected to change. A connection-scoped entry in the per-link view and a rewritten /etc/resolv.conf are two different outcomes, and the second may be a symlink that something else manages entirely. Record the resolver address, the search domains and the interface name at the same moment; a change in any one of them points at a different writer. Without that baseline, every later fix is a guess about which of three components moved.

The two switches and the writer each one silences

openfortivpn documents an option fragment that turns off both writers it can control, and each switch has a spelled-out equivalent that the manual treats as the same setting.

openfortivpn gw.example.com:443 --no-dns --pppd-no-peerdns
# written out in long form
openfortivpn gw.example.com:443 --set-dns=0 --pppd-use-peerdns=0

--no-dns stops the client from applying the DNS servers it learned from the gateway, while --pppd-no-peerdns stops pppd from pushing peer DNS into the system. The upstream manual warns that PPP DNS updates can race the client's own DNS updates, which is why both switches belong in the fragment instead of either one being treated as sufficient.

Neither switch names a replacement resolver, and neither stops a network manager, a DHCP client or a separate VPN integration from writing resolution afterwards. Suppressing two writers leaves the field open rather than owned. If a third component already manages DNS, the fragment changes nothing you can observe; if nothing manages it, you have just removed the only source of private name resolution. That is a valid arrangement only when the replacement owner was chosen first and tested on its own.

Choose the owner before you disable anything

Decide who answers queries for work names first, because that choice constrains everything after it. Keeping the existing resolver means the private zone must already be reachable from wherever the client sits, and a private resolver inside the office needs a route that survives the tunnel coming up. Sending every query to one internal service is a third policy with a sharper failure mode: when that service is unreachable, public names fail along with the private ones.

Route ownership stays separate on purpose. The route options are not required because you changed DNS, and turning off route installation without installing a replacement can make the resolver you just selected unreachable. Check the routing table for the resolver address specifically, not only for the application subnet you happened to test.

Check resolution per link, not per file

A single /etc/resolv.conf is not an inventory of what applications actually use on a host with scoped or per-domain resolution, so read the resolver state the way the platform exposes it.

resolvectl status
resolvectl query vpn.internal.example.com
resolvectl query example.com
ip route get 10.20.0.53

Query one private name and one public name once the connection is up, and note which link answered each. Where the VPN link carries a per-domain search list, a private name can resolve correctly while a public name still goes to the local resolver, and that split is the design working rather than a fault. If the private query fails, inspect the route to the resolver before touching DNS again; if it succeeds while an application still fails, the application may be reading a cached or separately configured resolver of its own.

Reconnect and confirm the disconnected state

Bring the connection down and repeat both queries. Whatever the chosen owner installed should be removed or restored by that owner, not left behind by the client you silenced. A stale per-link entry that outlives the tunnel keeps sending private queries to an address that is no longer reachable.

Then reconnect and read the same three values again: resolver address, search domains, interface.

If they match the policy you wrote down, the handoff is complete and the VPN client is no longer one of the writers. If a value drifts back a few seconds after link-up, something else is still applying configuration, and the next step is to identify that component rather than to add another flag. The end state you want is uneventful: one owner, one documented policy, and a disconnected host that resolves exactly as it did before the tunnel existed.

Artaigilean co-cheangailte