The pushed DNS server is in the log, the tunnel is up, and internal names still fail. Nothing is broken in the push itself; the setting arrived and stopped there. A pushed DNS option is a message to the client, not an instruction to the operating system resolver, and the handoff between them is where most of these cases end. Five checks, in order, find the handoff that did not happen.

Check one: how the connection starts

The owner of the connection determines who can apply resolver settings. A direct openvpn command from a shell owns nothing but its own process, while a systemd unit, NetworkManager or a vendor client each has its own way of publishing DNS. Read the process list and the unit name before assuming any of them.

A command-line process is not automatically a NetworkManager connection, and a NetworkManager connection is not automatically the thing that wrote resolv.conf. Those two assumptions account for most of the wasted hours in this investigation. Ask which component would publish DNS for the interface you can actually see.

Write the owner down before continuing. Every later check depends on it.

Check two: what the resolver link actually holds

On a systemd-resolved host, resolvectl status lists each link with its DNS servers and domains. Find the tunnel link and read its entries, and do not assume the interface is called tun0, because numbering follows the order of creation. If the pushed address is absent from the link, the handoff failed rather than the query.

If the address is present, the next question is which domains that link claims. A link with no domain list answers only names it is asked about directly, which is rarely what a split-DNS deployment wants. The address alone is necessary but not sufficient.

Compare the link name in the resolver output with the interface name in the VPN log before drawing any conclusion. A mismatch there usually means the log line came from a different process.

Check three: whether the namespace selects that link

A resolver can hold the right server and still not be consulted. The per-link domain decides which queries go to which link, and without a routing domain the private namespace falls back to whatever the default link says. That is why resolvectl query for the internal name can succeed while an application still fails. The application may be using a different resolver path entirely.

Check the domain assignment, not just the server address. A domain written as ~corp.example is a routing domain, and the tilde changes how the name is matched.

If the application has its own resolver, test with the same mechanism it uses rather than with the system tool. A query tool that bypasses the system resolver will succeed regardless of link state.

Check four: one owner, not two

If NetworkManager already manages the VPN and the resolver state, adding an unrelated hook script creates competing writers. The package-provided hook for systemd-resolved is a supported integration, and it expects a specific lifecycle: script-security 2, an up path, up-restart, a down path and down-pre. Those options make registration and removal happen at the right moments instead of only on the first connection.

Use the installed path from the package rather than an illustrative one copied from a guide. Permissions matter too, since the hook runs after privileges drop.

Editing a distribution resolver script to change its output target records one host's workaround. It is not a general repair.

Check five: the same result after a reconnect

Reconnect and re-read the link. A registration that appears once and disappears on the next transition is the failure this whole checklist exists to catch. Test after a suspend, after a forced reconnect, and after a restart of the resolver service. Each transition exercises a different ordering assumption.

Only when all five checks pass on a reconnect can you say the pushed settings are in use. Anything earlier is a snapshot of a lucky moment.

Lahatsoratra mifandraika