Why /etc/hosts still matters
Even with systemd‑resolved becoming the default in most modern distros, that old /etc/hosts file is still the fastest way to pin a name to an IP on a single box or a tiny LAN. I’ve seen people drop the file entirely, thinking DNS is always the answer, and then run into a maze of “host not found” errors. The culprit? systemd‑resolved quietly skipping some /etc/hosts entries—especially the ones that look like subdomains of a local domain.
The default resolution order in systemd‑resolved
Systemd‑resolved hooks into the Name Service Switch (NSS). When a program asks for a hostname, NSS consults the modules listed in /etc/nsswitch.conf. The default line for host resolution is:
hosts: files resolve [!UNAVAIL=return] dns
files– reads/etc/hostsresolve– talks to systemd‑resolveddns– falls back to the configured DNS servers
The [!UNAVAIL=return] flag tells NSS to bail out if the resolve module can’t answer (e.g., no network). Because resolve comes before dns, a good /etc/hosts line should win. The hiccup happens when the resolve module decides a name isn’t “local” and skips the files lookup for that particular query.
When systemd‑resolved ignores /etc/hosts entries
Subdomains vs. top‑level hostnames
Systemd‑resolved treats names ending in a dot (.) or matching the local domain as local. The local domain is usually derived from the system’s hostname or from the Domains= setting in /etc/systemd/resolved.conf. For example, on a box named server1.example.com, the local domain is example.com. If you add this to /etc/hosts:
192.168.1.10 app.example.com
systemd‑resolved will see app.example.com as a subdomain of the local domain and won’t consult /etc/hosts. Instead, it will fire a DNS query for app.example.com to the upstream servers. If those servers don’t have the record, the lookup fails.
A top‑level name like app or a fully‑qualified name that does not match the local domain (e.g., app.local) will resolve via /etc/hosts as expected.
Domain search suffixes and DNS suffix search
When you type a bare hostname (app) in a browser or a command, systemd‑resolved appends the search suffixes defined in /etc/resolv.conf (or via Domains=). If the search suffix is example.com, the lookup becomes app.example.com. The same rule applies to /etc/hosts: if the name you query ends up matching the local domain, systemd‑resolved bypasses the files module.
DNSSEC and secure DNS
If systemd‑resolved is configured to use DNSSEC validation (DNSSEC=yes in resolved.conf), it will reject any DNS response that fails validation. In that case, even if a DNS server returns a record for app.example.com, the lookup will fail, and systemd‑resolved will not fall back to /etc/hosts. That can give the impression that /etc/hosts is ignored, when in fact the DNS query is simply rejected.
Practical troubleshooting steps
Verify /etc/hosts syntax
The simplest cause of a silent ignore is a syntax error. Each line must contain an IP address followed by one or more hostnames, separated by whitespace. Trailing comments are allowed but must start with #. Example:
192.168.1.10 app.example.com # local app
Run:
sudo systemd-resolve --flush-caches
to clear any cached results before testing.
Check systemd‑resolved config
Open /etc/systemd/resolved.conf and look for the Domains= line. If it is set to ~ (tilde), systemd‑resolved will use the system’s hostname as the local domain. If it is set to a specific domain, that domain becomes the local domain. Example:
[Resolve]
Domains=example.com
If you want all subdomains of example.com to be treated as local, you can add a trailing dot:
Domains=example.com.
A trailing dot forces systemd‑resolved to treat the domain as fully qualified and not apply search suffixes.
Use systemd-resolve --status
This command shows the current resolution state, including the local domain, DNS servers, and whether DNSSEC is enabled:
systemd-resolve --status
Look for the “DNS Servers” and “DNSSEC” sections. If DNSSEC is yes, consider disabling it temporarily to see if the issue persists.
Use dig +trace
dig can show the exact path the query takes. For a name that should resolve via /etc/hosts, run:
dig +trace app.example.com
If the output shows a query to your upstream DNS server, systemd‑resolved is not using /etc/hosts. If it shows ;; SERVER: 127.0.0.53#53, it is using the local stub resolver.
Workarounds and best practices
Use systemd‑resolved’s local domain feature
If you control a local domain, add it to Domains= in resolved.conf. This tells systemd‑resolved that any name ending with that domain is local. It will then consult /etc/hosts for those names. Example:
[Resolve]
Domains=example.com
After editing, reload:
sudo systemctl restart systemd-resolved
Use fully‑qualified names in /etc/hosts
Avoid relying on search suffixes. Write the full domain name in /etc/hosts:
192.168.1.10 app.example.com
and query it with the dot:
ping app.example.com.
The trailing dot forces a fully‑qualified lookup and bypasses search suffixes.
Use DNS over TLS/HTTPS for internal DNS
If you run a local DNS server (e.g., dnsmasq, bind9, or unbound), consider configuring systemd‑resolved to use DNS‑over‑TLS or DNS‑over‑HTTPS. That keeps the internal DNS traffic encrypted and can help avoid confusion between local and external names.
TAGS: #linux #dns #systemd #security
See also
- How to Fix Permission‑Denied Errors When Mounting Host Paths in Rootless Podman Containers
- When Ubuntu’s `apt dist-upgrade` Removes Your Custom Kernel Module – A Practical Pinning Fix
- When initramfs drops into emergency mode after a kernel upgrade: how to recover the root partition on Ubuntu 24.04
- Granting Group Write Access on a Shared /srv/web Directory Using ACLs Without Changing File Ownership
- Hardening a Home Assistant Docker Container with User Namespaces and Read‑Only Volumes