You added a name to /etc/hosts, connected the VPN, and expected the record to appear for everyone. It does not, and the surprise usually comes with a second symptom: the name resolves on your machine and fails on a colleague's, which looks like a broken VPN DNS service and is actually the hosts file doing exactly what it was designed to do. A hosts file is local name-resolution configuration, not a zone update, and nothing in a normal VPN connection uploads it.

Hosts is a local override, not a zone update

The file is a list of mappings that name-service libraries consult before, or instead of, asking a DNS server. A line such as 10.40.0.20 build.corp.example changes what applications on that one machine conclude. It does not create a record, it does not travel through the tunnel, and the DNS server the tunnel points at has no idea it exists.

That is why the failure is asymmetric. Your client gets an answer from the file. Another client asks the VPN DNS server, which returns nothing, and the application reports that the name does not exist. Both machines are behaving correctly; only one of them has an override the other has never seen.

Two commands, two different components

The fastest way to stop arguing about this is to make the difference visible in two commands, both run on the client that has the hosts entry:

getent ahosts build.corp.example
dig @10.40.0.53 build.corp.example A

The first asks the system's own lookup path, which on a typical glibc system reads local files before DNS. The second sends a DNS query directly to the server at that address and does not consult local files at all. When the first returns your entry and the second returns NXDOMAIN, you have proved that the record does not exist in DNS and that your machine is answering from a local override.

The order is configurable, which is worth checking before you conclude anything about a particular distribution. Look at the hosts: line in /etc/nsswitch.conf; hosts: files dns consults files first. Do not copy that example over a working configuration just to change the order, because real systems often list additional sources and conditional service entries that the example does not carry.

systemd-resolved blurs the test

On a system using systemd-resolved, the local stub also serves names from the hosts database. Querying 127.0.0.53 therefore is not an independent test of the remote server, even though it looks like a DNS query and behaves like one. You can confirm what the resolver knows and which servers it would ask with:

resolvectl query build.corp.example
resolvectl status

The status output names the DNS servers per link, which is how you find out whether the VPN link actually received a resolver at all. That question is separate from the hosts entry and is the one worth asking when a private name fails for every user rather than one.

Applications can add another layer again. A browser with its own DNS-over-HTTPS setting, a container with its own resolver configuration, or a runtime that caches lookups for the life of the process can each answer differently from the system. Test the application you care about rather than assuming one command describes every program on the machine.

What actually shares a name across the VPN

If a private name has to work for everyone, it needs an owner that everyone's resolver can reach. The clean answer is to publish the record in a managed zone and make that zone resolvable through the intended VPN DNS path. The record then has one source of truth, an audit trail and a lifecycle, and nobody has to remember which laptop carries a local override.

Distributing hosts files is a legitimate alternative, and it is endpoint management rather than name service. If you choose it, the file has to be deployed, versioned and removed by whatever manages the fleet, and it should be described that way in the design. "The VPN handles it" is not a description of that process.

Dynamic DNS registration is a third mechanism, with its own permissions, update authentication and record lifetime. It does not follow from a hosts entry, and it does not follow from a client simply connecting either, unless the server and the client were both configured to do it. A record that appears by itself is a signal that someone enabled secure updates and a client that registers itself, and that configuration belongs in the design record rather than in a troubleshooting thread. If nobody can name who owns that setting, treat the automatic record as unverified and keep the managed zone as the source of truth.

A resolved name is not a route

Name resolution and reachability are separate, and a stale local mapping is the classic way to confuse them. A hosts entry that points at an address which has moved will keep selecting the obsolete host long after DNS was corrected, and the symptom looks like a routing failure on a name that resolves fine.

So when the report is "the name resolves but the service is unreachable", compare the system lookup with an explicit query to the VPN DNS server, then check the address you got against the one the service actually uses. The systemd-resolved reference documents the local sources it consults, which is the map you need for that comparison.

Then fix the override through its owner. Removing a line from your own hosts file does nothing for the twelve laptops that received the same line from a deployment script, and that is the version of this problem people actually have.

Inyandiko zihuye