Linux DNS Configuration
Configure Linux DNS end-to-end: understand /etc/resolv.conf, manage systemd-resolved, use /etc/hosts for static overrides, and verify with dig.
Before you start
- ▸sudo or root access on the target system
- ▸Basic familiarity with a terminal text editor (nano, vim)
- ▸The dnsutils or bind-utils package installed for dig
DNS configuration on Linux has two layers that confuse even experienced admins: the file /etc/resolv.conf and the resolver daemon sitting in front of it. On modern systems that daemon is systemd-resolved, which manages a local stub resolver, caching, and per-link DNS settings — while /etc/resolv.conf often becomes a symlink rather than a real file. This guide walks through both layers, shows you how /etc/hosts fits in, and gives you concrete tools to verify everything is working.
How Linux Resolves Names
Name resolution order is controlled by /etc/nsswitch.conf. The relevant line almost always reads:
grep hosts /etc/nsswitch.conf
You will see something like hosts: files dns myhostname. This means the kernel checks /etc/hosts first (files), then a DNS resolver (dns), then systemd-resolved's hostname logic (myhostname). Understanding this order prevents hours of debugging surprises.
/etc/hosts — Static Overrides
Before any DNS query leaves your machine, the resolver checks /etc/hosts. It is a flat text file: one IP address per line followed by one or more hostnames.
cat /etc/hosts
A minimal hosts file looks like this (output will vary):
127.0.0.1 localhost
127.0.1.1 myhostname.local myhostname
::1 localhost ip6-localhost ip6-loopback
Add a static override by appending a line. For example, to point dev.example.com at a local VM:
echo '192.168.1.50 dev.example.com' | sudo tee -a /etc/hosts
Changes take effect immediately — no service restart needed. Remove the line when you no longer need it; stale entries are a common source of mysterious failures.
Understanding /etc/resolv.conf
/etc/resolv.conf tells the C library resolver (used by almost every program) which nameservers to query and what search domains to append. A traditional hand-edited file looks like:
nameserver 1.1.1.1
nameserver 8.8.8.8
search example.com
On modern distros, however, this file is usually a symlink managed by systemd-resolved or NetworkManager. Editing it directly will either be overwritten on next network event or have no effect at all. Check what you actually have:
ls -la /etc/resolv.conf
Common targets you may see:
/run/systemd/resolve/stub-resolv.conf— the stub mode used by most Ubuntu and Debian systems; points to127.0.0.53/run/systemd/resolve/resolv.conf— the uplink mode; lists the actual upstream servers/run/NetworkManager/resolv.conf— NetworkManager is managing DNS directly- A regular file — either a container/VM environment or a manually managed server
systemd-resolved
systemd-resolved is the recommended resolver on systemd-based distros. It provides a local stub at 127.0.0.53:53, caches responses, validates DNSSEC (optionally), and applies per-link DNS configuration pushed by NetworkManager or networkd.
Check Status
resolvectl status
This shows the global DNS servers, the per-link servers, DNSSEC mode, and cache hits. Pay attention to the Current DNS Server line for each interface.
Enable and Start (if not already running)
On most distros it ships enabled. If not:
sudo systemctl enable --now systemd-resolved
Then symlink /etc/resolv.conf to the stub file:
sudo ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
Set Global DNS Servers
Edit (or create) /etc/systemd/resolved.conf and set your preferred servers:
sudo nano /etc/systemd/resolved.conf
Relevant options under [Resolve]:
[Resolve]
DNS=1.1.1.1 1.0.0.1
FallbackDNS=8.8.8.8 8.8.4.4
Domains=~.
DNSSEC=allow-downgrade
DNSOverTLS=opportunistic
Domains=~. makes this the resolver for all domains (the wildcard routing domain). Restart after changes:
sudo systemctl restart systemd-resolved
Per-Link DNS with networkd
If you manage interfaces with systemd-networkd instead of NetworkManager, set DNS per interface in /etc/systemd/network/20-wired.network:
[Match]
Name=eth0
[Network]
DHCP=yes
DNS=192.168.1.1
Distro Differences
Fedora / RHEL 9+ / Rocky 9+ default to NetworkManager managing DNS. NetworkManager can forward to systemd-resolved or write /etc/resolv.conf itself. Check the active plugin:
NetworkManager --print-config | grep dns
To hand control to systemd-resolved, add to /etc/NetworkManager/conf.d/dns.conf:
[main]
dns=systemd-resolved
sudo systemctl restart NetworkManager
Arch Linux ships neither resolved nor NetworkManager enabled by default; enable whichever you choose after installation.
Ubuntu 20.04+ enables systemd-resolved by default with the stub symlink already in place — no manual setup needed on a fresh install.
Testing DNS with dig
dig is the most reliable DNS troubleshooting tool. Install it if needed:
# Debian/Ubuntu
sudo apt install dnsutils
# Fedora/RHEL
sudo dnf install bind-utils
# Arch
sudo pacman -S bind
Basic Lookup
dig linux.org
Look for the ANSWER SECTION and the SERVER line at the bottom. The server line tells you which resolver actually answered — this is critical when diagnosing cache or stub issues.
Query a Specific Nameserver
Bypass the system resolver and query directly:
dig @1.1.1.1 linux.org
Query the systemd-resolved Stub
dig @127.0.0.53 linux.org
If this works but dig linux.org does not, your /etc/resolv.conf is not pointing at the stub.
Reverse Lookup
dig -x 1.1.1.1
Check DNSSEC
dig +dnssec linux.org
Look for the ad flag (authenticated data) in the flags line to confirm DNSSEC validation succeeded.
Flush the resolved Cache
When troubleshooting stale records:
resolvectl flush-caches
Verification Checklist
- Run
resolvectl statusand confirm a valid upstream DNS server appears for your active interface. - Run
dig @127.0.0.53 linux.org— you should get an answer in under 100 ms. - Run
dig linux.orgwithout specifying a server — the result should match the above. - Check
cat /etc/resolv.confpoints tonameserver 127.0.0.53(stub mode) or a valid external IP. - Test a hostname from
/etc/hosts:getent hosts dev.example.comshould return the IP you set.
Troubleshooting
| Symptom | Likely Cause | Fix |
|---|---|---|
dig times out at 127.0.0.53 | systemd-resolved not running | sudo systemctl start systemd-resolved |
| DNS works in terminal, fails in browser | MDNS or DNS-over-HTTPS in browser overriding system DNS | Disable DoH in browser settings or check MDNS firewall rules |
| Edits to resolv.conf disappear | NetworkManager or networkd overwrites on link-up | Configure DNS through NetworkManager or systemd-resolved, not the file directly |
NXDOMAIN for internal hostnames | Search domain not set | Add Domains=yourdomain.internal in resolved.conf or via DHCP option 15 |
| Slow resolution (200ms+) | Resolver falling back to secondary after primary timeout | Check resolvectl statistics for timeouts; switch primary nameserver |
Frequently asked questions
- Why does my /etc/resolv.conf keep getting overwritten?
- NetworkManager or systemd-networkd rewrites it on every link-up event. Configure DNS through those tools instead of editing the file directly, or lock it with 'chattr +i' as a last resort on static servers.
- What is the difference between /run/systemd/resolve/stub-resolv.conf and /run/systemd/resolve/resolv.conf?
- The stub file points to 127.0.0.53 (the local resolved daemon). The non-stub file lists actual upstream nameservers. Use the stub version so resolved can cache and apply per-link settings.
- Can I use systemd-resolved and a local DNS server like dnsmasq together?
- Yes. Configure dnsmasq to listen on a different port or address, and set it as the upstream in resolved.conf using the DNS= directive, or disable resolved and let dnsmasq write resolv.conf directly.
- Does /etc/hosts work for IPv6 addresses?
- Yes. Add a line starting with the full IPv6 address followed by the hostname, for example '::1 localhost'. The resolver checks it the same way it checks IPv4 entries.
- How do I set a per-connection DNS server in NetworkManager without editing config files?
- Use 'nmcli con mod <connection-name> ipv4.dns "1.1.1.1 8.8.8.8" ipv4.ignore-auto-dns yes' then reconnect. NetworkManager will pass those servers to systemd-resolved automatically.
Related guides
Build a Mesh VPN with Nebula
Build a fully self-hosted mesh VPN with Nebula: create a CA, sign node certs, configure lighthouses, enforce group-based firewall rules, and run as a systemd service.
Common Linux Network Ports Reference
Learn Linux port ranges, read /etc/services, find what's listening with ss and nmap, and apply solid firewall rules to expose or block the right ports.
How to Configure a Static IP on Linux
Configure a static IP on Linux using Netplan, NetworkManager (nmcli), or systemd-networkd across Ubuntu, Fedora, Debian, and Arch with verified steps.
Expose a Service with Cloudflare Tunnel
Expose local services to the internet without port-forwarding using Cloudflare Tunnel. Install cloudflared, create a named tunnel, configure ingress rules, and run as a systemd service.