Two machines on the same VPN, same hostname, and only one of them resolves it: the difference is usually not the tunnel but which resolver answered. On a Linux desktop that name can travel through the name service switch, through systemd-resolved's stub, or through a resolver the application brought with it, and each of those applies its own rule to the same suffix. The suffix is where the rules diverge, because .local is not a neutral label. Work out which path the failing program actually takes before you change the order anything runs in.

Two resolvers, two different answers

A traditional glibc lookup walks the hosts: line of nsswitch.conf and asks each module in turn until one answers. A client that talks to systemd-resolved through the stub listener asks a single process instead, and that process applies per-link policy plus its own special cases. An application that ships its own resolver, or links a library that does, can bypass both. One hostname can therefore have three owners on one machine, and only one of them knows what the VPN intended.

Identify the owner before changing anything, because the fix differs for each of the three. A browser and a command-line tool on the same desktop can legitimately disagree, and that disagreement is the clue rather than the bug. If the browser resolves the name and the tool does not, you are debugging a resolver path and not a VPN route. Confirm the route separately, so a routing fault is not mistaken for a name service fault. When both agree, the fault is upstream of the host and the resolver configuration here is not worth another edit.

Which command actually asked your resolver

Two read-only commands answer related questions, and comparing them localises the problem faster than reading configuration. Use a real internal name and keep the output private, because internal records are still internal. Run both from the same session the failing program runs in:

getent ahosts files.corp.local
resolvectl query files.corp.local
resolvectl status

The first asks through the configured modules, which is what a typical program experiences. The second asks systemd-resolved directly and reports the link, the protocol and the server that answered. Querying the corporate server by address proves only that the server holds the record, because it says nothing about whether the application ever asked it. Read the link that carries the VPN and the domains it claims, then compare that list with the suffix in the failing name.

Why .local skips your unicast zone

systemd-resolved's reference states that multi-label names ending in .local normally go to multicast DNS when mDNS is enabled, rather than to ordinary unicast DNS. RFC 6762 sets that namespace aside for link-local multicast, so a corporate zone can hold a correct record that is never consulted for that suffix. The tunnel can route the resolver perfectly and still lose the race. A ping that succeeds from a cache says nothing about the lookup that just happened.

There is a documented exception, and it settles most of these cases: when a link declares that suffix as a search domain or a routing domain, the daemon keeps it on the unicast path and skips the multicast special case. That is per-link policy rather than a global preference. Two machines with the same VPN server can behave differently when one network profile declares the domain and the other does not, so read the profile that is active instead of the one you remember editing.

The narrow exception beats a global reorder

For an existing zone you cannot rename, have the resolver and VPN owners declare the domain on the connection that carries it. A routing-domain entry for that one suffix plus the intended server address is a small change with a named owner, and it lives with the link rather than in a hand-edited file.

It also survives a disconnect, because the link policy is re-applied when the link returns. Editing the shared nsswitch.conf is the tempting alternative and the more expensive one, because it changes lookup behaviour for every name on the machine, local discovery included. Disabling the multicast daemon is not equivalent either: it removes the local behaviour without teaching any resolver about the corporate zone. If the service is new rather than inherited, put it under a subdomain your organisation controls and skip the competition entirely. Choose the narrow exception when the name is fixed in firmware or printed on a label, and choose a new subdomain when you still control the client.

What to prove after the change

Prove the fix with the application's own lookup rather than a tool you installed to test with, and clear any cache that could answer for it. Then confirm the resolver path and the link that served the answer. Finally check that local discovery still behaves as it did before the change, because the printer that stops appearing is the part people notice last. These three commands check the name, the path and the TLS identity in that order:

getent ahosts files.corp.local
resolvectl query files.corp.local
curl -sv --max-time 5 https://files.corp.local/ 2>&1 | head -n 20

A correct address with the wrong certificate still fails a strict client, so check the name the application expects once resolution works. Confirm the service accepts connections from the VPN address and not only from the office range. Write the domain scope, the server and the owner into the connection's documentation. Otherwise the next person to meet this failure reaches for the same global edit you just avoided.

Artiglyn mynjee