systemd‑resolved is the default DNS resolver on most modern distros. It pulls /etc/hosts unless you flip the --no-hosts flag. In 2026 a subtle tweak in its default behaviour started swallowing local hostname lookups, and no one noticed until a service went down.
Why it happens
systemd-resolved now gives the DNS stack a higher priority than the hosts file when you call getaddrinfo(3). If a name shows up in both /etc/hosts and a DNS zone, the DNS answer wins. When the zone is empty or the query times out, it silently falls back to the hosts file. But if the DNS server is unreachable and the hosts entry points to 127.0.0.1 or ::1, the resolver drops that entry and returns ENOTFOUND. The change was meant to stop a compromised DNS server from poisoning local lookups.
Symptoms
- A service that relies on a local hostname (e.g.,
db.example.local) refuses to start. systemctl statusshows “Failed to resolve db.example.local”.dig db.example.localsays NXDOMAIN, even though/etc/hostshas the mapping.- No obvious logs in
/var/log/syslogorjournalctl -u systemd-resolved.
Quick check
# Verify the entry exists
grep db.example.local /etc/hosts
# Test resolution with systemd-resolved
systemd-resolve --status db.example.local
If the second command reports “Address: (none)”, the resolver is discarding the entry.
Work‑arounds
| Approach | When to use | Command |
|---|---|---|
| Disable DNS for the hostname | The hostname is only used locally. | systemd-resolve --no-poll db.example.local |
| Add a static DNS record | You control the zone and want consistency. | `echo “db.example.local. IN A 127.0.0.1” |
| Force hosts file usage | You must keep the entry in /etc/hosts. | systemd-resolve --no-hosts db.example.local (per‑query) or set DNSStubListener=no in /etc/systemd/resolved.conf and restart. |
Tweak /etc/nsswitch.conf | Want files to win over dns globally. | Edit hosts: files dns to hosts: files mdns4_minimal [NOTFOUND=return] dns |
The most common single‑service fix is to add a local DNS record in /etc/systemd/resolved.conf.d/local.conf:
[Resolve]
DNS=127.0.0.1
Domains=~.
This forces the resolver to treat the local domain as a search domain and keeps the hosts file in the resolution path.
Security implications
- DNS spoofing mitigation – By dropping ambiguous hosts entries, systemd‑resolved reduces the risk of an attacker poisoning the
/etc/hostsfile via DNS. - Potential service outages – Legitimate local services can be unintentionally disabled if the hosts file isn’t mirrored in DNS.
- Audit trail –
journalctl -u systemd-resolvednow logs “Dropped hosts entry for db.example.local” when the drop occurs. Check those logs regularly.
Checklist for operators
- Audit local hostnames – Run
grep -E '^[0-9.]+[[:space:]]+[a-zA-Z0-9.-]+' /etc/hostsand compare against your DNS zones. - Enable DNSStubListener only if you need local DNS queries served on 127.0.0.53.
- Use systemd‑resolved’s
--no-pollfor services that must resolve local names regardless of DNS. - Document the change – Add a note in your infrastructure docs that systemd‑resolved may drop hosts entries if DNS is unreachable.
TL;DR
- systemd‑resolved prefers DNS over
/etc/hostsand may silently drop local entries when DNS is unreachable. - Check
systemd-resolve --statusandjournalctl -u systemd-resolvedfor clues. - Fix by adding a local DNS record, disabling DNS for the hostname, or tweaking
nsswitch.conf. - Be aware of the security trade‑off: DNS spoofing protection vs. accidental service outages.
See also
- When resolvectl Caches Fail: Quick Steps to Clear Stale DNS Entries Without Restarting NetworkManager
- When ss Shows No Open TCP Ports Even Though Services Are Running
- When nftables silently drops HTTP traffic: a step‑by‑step port‑filter investigation
- Why My Nginx Service Stops After a Kernel Upgrade: A Systemd Timer Debugging Session in a Home‑Lab Server
- Hardening Serverless Function IAM: A Step‑by‑Step Fix for Over‑Permissive Execution Roles