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-ad
  • nameserver: DNS server IP (up to 3, tried in order)
  • search: appends domain to single-label queries (curl appcurl 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 resolvectl or systemd-resolve --interface=

/etc/nsswitch.conf

cat /etc/nsswitch.conf
# hosts:     files mdns4_minimal [NOTFOUND=return] dns mdns4
# networks:  files

This 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.local

Simple 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.53

systemd-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=localdomain

Querying 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.com

Common 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.conf

DNS 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.53

DNSSEC 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.internal

Reverse DNS

dig -x 8.8.8.8
host 8.8.8.8
nslookup 8.8.8.8