DNS Resolution
DNS resolution converts hostnames to IP addresses. On Linux, this is handled by the NSS (Name Service Switch) system, which consults multiple sources (files, DNS, LDAP) in order.
The Resolution Chain
Application calls getaddrinfo("example.com")
↓
NSS (/etc/nsswitch.conf) says: hosts: files dns
↓
1. Check /etc/hosts (files source)
↓
2. Query DNS (dns source) → /etc/resolv.conf nameservers
↓
Returns IP address to application
/etc/resolv.conf
cat /etc/resolv.conf
# Generated by NetworkManager (or systemd-resolved)
nameserver 8.8.8.8
nameserver 1.1.1.1
search localdomain home
options edns0 trust-adnameserver: DNS server IP (up to 3, tried in order)search: appends domain to single-label queries (curl app→curl app.localdomain)options:edns0,timeout,attempts,rotate(round-robin nameservers)
This file is often auto-generated by NetworkManager or systemd-resolved. Edits may be overwritten on reboot or network change. To make permanent custom nameservers:
- NetworkManager: set in connection profile
- systemd-resolved: use
resolvectlorsystemd-resolve --interface=
/etc/nsswitch.conf
cat /etc/nsswitch.conf
# hosts: files mdns4_minimal [NOTFOUND=return] dns mdns4
# networks: filesThis controls the order of lookup sources. The hosts line is what matters for DNS.
# Common sources for hosts:
files → /etc/hosts
dns → /etc/resolv.conf (DNS queries)
mdns4 → multicast DNS (Bonjour, .local resolution)
nis → NIS (legacy, rarely used)
ldap → LDAP directory
myhostname → the local hostname (127.0.0.1 resolution)
# Force /etc/hosts first, DNS as fallback:
hosts: files dns/etc/hosts
cat /etc/hosts
# 127.0.0.1 localhost
# ::1 localhost
# 192.168.1.100 myserver.localSimple static mappings. files source in nsswitch means this is checked before DNS.
systemd-resolved
Modern distros (Ubuntu 18+, Fedora, etc.) use systemd-resolved as a local DNS stub resolver:
systemctl status systemd-resolved
# Enabled: creates /run/systemd/resolve/stub-resolv.conf
# which points to 127.0.0.53 as the DNS resolver
# Check:
cat /run/systemd/resolve/stub-resolv.conf
# nameserver 127.0.0.53systemd-resolved provides:
- Local caching (DNS cache, reduces DNS queries)
- Split-horizon DNS (different DNS based on interface)
- DNSSEC validation
- LLMNR (Linux-native mDNS replacement)
resolvectl
resolvectl status # show DNS state per interface
resolvectl dns # show current DNS servers
resolvectl query example.com # do a DNS lookup
resolvectl flush-caches # flush DNS cache
resolvectl log-level debug # debug logging
# Set DNS per interface:
resolvectl dns eth0 8.8.8.8 8.8.4.4
resolvectl domain eth0 localdomain
# Or via systemd .network file:
# /etc/systemd/network/eth0.network
[Network]
DNS=8.8.8.8
Domains=localdomainQuerying DNS
# dig (most detailed)
dig example.com
dig example.com A
dig example.com AAAA
dig example.com MX
dig example.com NS
dig example.com TXT
dig @1.1.1.1 example.com # query specific server
dig +short example.com # just the answer
dig +trace example.com # full resolution chain
# host (simpler)
host example.com
host -a example.com # all record types
# nslookup (older, interactive)
nslookup example.com
# getent (NSS-aware — uses nsswitch.conf)
getent hosts example.com
getent ahosts example.comCommon Issues
resolv.conf gets overwritten
# Check what manages it:
# On Ubuntu with NetworkManager:
ls -la /etc/resolv.conf
# lrwxrwxrwx 1 root root 32 ... /run/systemd/resolve/stub-resolv.conf
# Disable systemd-resolved if you want manual /etc/resolv.conf:
systemctl disable --now systemd-resolved
echo "nameserver 8.8.8.8" > /etc/resolv.confDNS not resolving after VPN
# VPN splits the DNS (only VPN DNS knows internal domains):
# Check what resolv.conf looks like:
cat /etc/resolv.conf
# nameserver 10.0.0.53 ← VPN DNS
# Use local caching:
systemctl enable --now systemd-resolved
resolvectl dns eth0 10.0.0.53DNSSEC validation failures
# dig shows SERVFAIL or BOGUS:
dig +dnssec example.com
# Check if DNSSEC is breaking something:
dig +cd example.com # CD flag = disable checking
# Disable DNSSEC validation (if needed, temporarily):
# In /etc/systemd/resolved.conf:
[Resolve]
DNSSEC=no/etc/hosts not being checked
# Verify nsswitch order:
getent hosts localhost # should use files first
grep ^hosts /etc/nsswitch.conf # should start with "files"Search Domains
# If search domain is "home":
# curl app → tries app.home → app.home. (FQDN)
# For split-horizon (internal DNS):
# resolv.conf: search corp.internal
# Then: curl app-server → app-server.corp.internalReverse DNS
dig -x 8.8.8.8
host 8.8.8.8
nslookup 8.8.8.8