Whenever a Linux machine needs to turn a domain name like github.com into an IP address, it uses something called the DNS resolver. The resolver is the client-side component responsible for asking DNS servers questions and handing back answers to applications like your browser, curl, ping, or ssh.
Unlike a full DNS server (like BIND), the resolver on a typical Linux machine doesn’t store zone files or serve authoritative answers — it simply asks upstream DNS servers and returns the result. Understanding how to configure it properly is one of the most fundamental Linux networking skills.
This guide walks through resolver configuration from the ground up — covering legacy methods, modern systemd-based methods, and how to troubleshoot resolver problems.
What Is a DNS Resolver?
A DNS resolver (also called a “stub resolver”) is a small piece of software built into the operating system’s networking stack. Its job is:
- Accept a hostname lookup request from an application.
- Send a DNS query to one or more configured nameservers.
- Wait for the response.
- Return the resolved IP address (or an error) to the requesting application.
flowchart LR
A[Application: browser, curl, ssh] --> B[Stub Resolver on Linux]
B --> C[Configured DNS Server]
C --> B
B --> AThe resolver itself usually does not cache — that’s a separate job, often handled by systemd-resolved, nscd, or dnsmasq on modern systems.
The Classic Way: /etc/resolv.conf
For decades, the file /etc/resolv.conf has been the primary way Linux systems learn which DNS servers to use.
Example File
nameserver 8.8.8.8
nameserver 1.1.1.1
search example.com
options timeout:2 attempts:3Directive Breakdown
| Directive | Meaning |
|---|---|
nameserver | IP address of a DNS server to query. You can list up to 3. |
search | Domain suffixes automatically appended to unqualified hostnames (e.g., ping server1 becomes server1.example.com). |
options timeout | How long (seconds) to wait for a response before retrying. |
options attempts | How many times to retry a query before giving up. |
Editing It Manually
sudo nano /etc/resolv.conf⚠️ Important: On many modern distributions, this file is auto-generated and any manual edits get overwritten on reboot or network restart. We’ll cover how to make changes persistent below.
Modern Linux: systemd-resolved
Most current distributions (Ubuntu 18.04+, Fedora, RHEL 8+) use systemd-resolved as the resolver management service. It provides local caching, DNSSEC validation, and per-interface DNS configuration — features the old static file never had.
Checking If It’s Active
systemctl status systemd-resolvedHow It Works
flowchart TD
A[Application] --> B[systemd-resolved local stub 127.0.0.53]
B -->|cache miss| C[Real upstream DNS server]
C --> B
B -->|cached| AWith systemd-resolved active, /etc/resolv.conf is often a symlink pointing to:
/run/systemd/resolve/stub-resolv.confThis file typically just contains:
nameserver 127.0.0.53
options edns0 trust-adThe real upstream DNS servers are managed separately and can be viewed with:
resolvectl statusSetting DNS Servers with systemd-resolved
To set DNS servers for a specific interface (e.g., eth0):
sudo resolvectl dns eth0 1.1.1.1 8.8.8.8
sudo resolvectl domain eth0 example.comTo make this permanent, use netplan (Ubuntu) or NetworkManager, described next.
Configuring DNS via Netplan (Ubuntu Server)
Netplan configuration files live in /etc/netplan/. A typical file:
network:
version: 2
ethernets:
eth0:
addresses:
- 192.168.1.50/24
nameservers:
addresses:
- 1.1.1.1
- 8.8.8.8
search:
- example.com
routes:
- to: default
via: 192.168.1.1Apply the configuration:
sudo netplan applyConfiguring DNS via NetworkManager (Desktop Linux / RHEL)
If your system uses NetworkManager (common on desktops and RHEL-based servers):
nmcli con show # list connections
nmcli con mod "Wired connection 1" ipv4.dns "1.1.1.1 8.8.8.8"
nmcli con mod "Wired connection 1" ipv4.ignore-auto-dns yes
nmcli con up "Wired connection 1"Verify:
nmcli device show eth0 | grep DNSConfiguring DNS via Static Network Scripts (Older RHEL/CentOS)
On older RHEL/CentOS systems using the legacy network service:
sudo nano /etc/sysconfig/network-scripts/ifcfg-eth0Add or modify:
DNS1=1.1.1.1
DNS2=8.8.8.8
DOMAIN="example.com"
PEERDNS=noRestart networking:
sudo systemctl restart networkComparison Table: Resolver Configuration Methods
| Method | Distro Typical Use | Persistent? | Local Caching? | Notes |
|---|---|---|---|---|
/etc/resolv.conf manual edit | Legacy systems, containers | No (often overwritten) | No | Simple but fragile |
systemd-resolved | Ubuntu 18.04+, Fedora, RHEL 8+ | Yes | Yes | Modern default |
| Netplan | Ubuntu Server 18.04+ | Yes | Depends on backend | YAML-based |
NetworkManager (nmcli) | Desktops, RHEL | Yes | Optional | GUI + CLI support |
ifcfg-* scripts | RHEL/CentOS 6/7 | Yes | No | Legacy but still common |
dnsmasq | Home routers, small networks | Yes | Yes | Lightweight, DHCP+DNS combo |
Real-World Example: Multi-Interface Server
Imagine a Linux server with two network interfaces — one connected to a public internet uplink (eth0) and one to an internal management network (eth1). You may want DNS queries for internal hostnames to go to your internal DNS server, and everything else to a public resolver.
With systemd-resolved, this is done with per-link DNS + domain routing:
sudo resolvectl dns eth1 10.0.0.5
sudo resolvectl domain eth1 "~corp.internal"
sudo resolvectl dns eth0 1.1.1.1
sudo resolvectl domain eth0 "~."The ~corp.internal syntax tells the resolver: “route any query ending in corp.internal through this interface’s DNS server,” while ~. is a catch-all for everything else via eth0.
Cisco Example: DNS Resolver Settings on IOS
Cisco devices have their own resolver settings, conceptually similar to a Linux client:
ip domain name example.com
ip name-server 1.1.1.1
ip name-server 8.8.8.8
ip domain lookupTest resolution directly from the Cisco CLI:
ping www.example.comIf ip domain lookup is disabled, unqualified commands typed at the CLI won’t trigger accidental DNS lookups — a common gotcha for new network engineers who mistype a command and then wait for a long DNS timeout.
Python Example: Using the System Resolver
Python applications typically use the operating system’s resolver settings automatically via socket:
import socket
try:
ip = socket.gethostbyname('www.example.com')
print(f"Resolved IP: {ip}")
except socket.gaierror as e:
print(f"Resolution failed: {e}")For more control (e.g., specifying a custom DNS server rather than the system default), use dnspython:
import dns.resolver
resolver = dns.resolver.Resolver()
resolver.nameservers = ['1.1.1.1']
answer = resolver.resolve('www.example.com', 'A')
for rdata in answer:
print(rdata.address)Testing Your Resolver Configuration
# Check what resolver Linux is actually using
cat /etc/resolv.conf
# Query using the resolver's default settings
getent hosts www.example.com
# Detailed status when using systemd-resolved
resolvectl status
# Manual query bypassing the OS resolver entirely
dig @1.1.1.1 www.example.comBest Practices
- Use at least two nameservers for redundancy — if one is unreachable, the second is tried automatically.
- Prefer
systemd-resolvedor NetworkManager on modern systems rather than hand-editing/etc/resolv.conf, since manual edits are often lost on reboot. - Set a
searchdomain carefully — an overly broad search list can cause slow or incorrect lookups for typos. - Match internal and external DNS carefully in split-DNS environments to avoid leaking internal hostnames externally.
- Use DNS servers close to your network (geographically and topologically) to minimize latency.
- Enable DNSSEC validation where supported for extra protection against spoofed responses.
Troubleshooting
| Symptom | Likely Cause | Fix |
|---|---|---|
Name or service not known | No nameservers configured or unreachable | Check /etc/resolv.conf / resolvectl status |
Resolution works with dig @8.8.8.8 but not with plain dig | Local resolver misconfigured | Verify /etc/resolv.conf points to a working server |
Changes to /etc/resolv.conf disappear after reboot | File is auto-managed by systemd-resolved/NetworkManager | Configure DNS via resolvectl, nmcli, or netplan instead |
| Slow lookups | DNS server far away or overloaded, or search domain causing multiple lookups per query | Switch to a faster resolver; simplify search list |
| Internal hostnames not resolving | Split-DNS not configured correctly | Set per-domain DNS routing with resolvectl domain |
Temporary failure in name resolution | Network interface down or DNS server unreachable | Check connectivity: ping <dns_server_ip> |
Understanding Resolver Order and Failover Behavior
When a Linux resolver has multiple nameservers configured, it doesn’t load-balance between them — it tries them in order. The first nameserver listed is queried first; only if it fails to respond within the configured timeout does the resolver move on to the next one in the list.
nameserver 10.0.0.5
nameserver 1.1.1.1In this example, every query is attempted against 10.0.0.5 first. If that server is slow or down, there’s a timeout penalty (controlled by options timeout:N) before the resolver falls back to 1.1.1.1. This is worth knowing because a “half-broken” primary DNS server — one that’s up but not responding — can actually make name resolution slower than if it were fully down, since every query pays the timeout cost before failing over.
sequenceDiagram
participant App as Application
participant Resolver as Stub Resolver
participant DNS1 as Primary DNS 10.0.0.5
participant DNS2 as Secondary DNS 1.1.1.1
App->>Resolver: Resolve hostname
Resolver->>DNS1: Query
Note over Resolver,DNS1: Timeout (no response)
Resolver->>DNS2: Query (fallback)
DNS2-->>Resolver: Answer
Resolver-->>App: AnswerThe Role of /etc/hosts and nsswitch.conf
Before any resolver ever sends a query out onto the network, Linux checks a local static file: /etc/hosts. This file lets you hard-code specific name-to-IP mappings that bypass DNS entirely.
127.0.0.1 localhost
192.168.1.20 devserver.localWhether /etc/hosts is checked before or after DNS — and in what order various other sources like NIS or LDAP are consulted — is controlled by /etc/nsswitch.conf:
hosts: files dnsThis line means: “check /etc/hosts (files) first, then fall back to DNS.” This order is why adding an entry to /etc/hosts immediately overrides whatever DNS would otherwise return — useful for local testing, and a very common source of confusion when someone forgets they added a temporary entry months ago and can’t figure out why a hostname “won’t update.”
Testing Resolver Behavior Systematically
A methodical approach to resolver testing separates local overrides, DNS caching layers, and actual upstream DNS server behavior:
# 1. Check for local hosts file override
grep -i example.com /etc/hosts
# 2. Check the order of resolution sources
cat /etc/nsswitch.conf | grep hosts
# 3. Test resolution via the full OS stack
getent hosts www.example.com
# 4. Test resolution via the configured resolver directly
cat /etc/resolv.conf
dig www.example.com
# 5. Bypass everything and test the upstream server directly
dig @8.8.8.8 www.example.com
Running through these five layers in order will almost always reveal exactly where a resolution mismatch is occurring — whether it’s a stale hosts file entry, a caching layer, or the upstream DNS server itself giving an unexpected answer.
Comparison: Resolver Behavior Across Common Distributions
| Distribution | Default Resolver Manager | Local Caching by Default? | Typical /etc/resolv.conf Owner |
|---|---|---|---|
| Ubuntu 20.04+ | systemd-resolved | Yes | Symlink to systemd-resolved stub |
| Debian 11+ | systemd-resolved (or manual) | Depends on install | Varies |
| Fedora / RHEL 8+ | systemd-resolved / NetworkManager | Yes | Symlink or managed by NetworkManager |
| RHEL/CentOS 7 | Legacy network scripts | No | Directly written by DHCP client |
| Alpine Linux (containers) | Minimal, often static | No | Directly written, static |
| Docker containers | Inherited from host or --dns flag | No | Set at container creation |
Docker and Container DNS Resolution
Containers deserve special mention because their DNS resolution model often surprises people coming from traditional servers. By default, Docker containers get their /etc/resolv.conf populated based on the Docker daemon’s own configuration — not automatically inherited from complex host resolver setups like systemd-resolved.
# Run a container with a custom DNS server<br>docker run --dns=1.1.1.1 --dns=8.8.8.8 alpine nslookup example.com<For Docker Compose environments, DNS can be set per-service:
services:
app:
image: myapp
dns:
- 1.1.1.1
- 8.8.8.8This matters because a container silently using an unreachable or unexpected DNS server is a very common source of “works on my machine but not in the container” bugs.
Frequently Asked Questions
Why does editing /etc/resolv.conf sometimes get reverted automatically? Because on most modern systems, that file is actively managed by systemd-resolved, NetworkManager, or a DHCP client — any of which will overwrite manual edits the next time they run. Always configure DNS through the tool that’s actually managing the file (resolvectl, nmcli, or your DHCP/netplan config) rather than editing it directly.
Is it safe to just always use public DNS servers like 1.1.1.1 or 8.8.8.8? For general internet resolution, yes — these are fast, reliable, privacy-respecting options. However, they cannot resolve your internal/private hostnames (like fileserver.corp.local), so environments with internal DNS zones need a resolver that’s aware of those internal domains, either directly or via split-DNS routing.
What’s the difference between a resolver timing out and returning NXDOMAIN? A timeout means the resolver never got a response at all — the server is unreachable or too slow. NXDOMAIN means the server responded promptly, but explicitly said “this name does not exist.” These point to very different problems: timeouts suggest network/server issues, while NXDOMAIN suggests a naming/zone configuration issue.
Summary
The DNS resolver is the unsung workhorse behind almost every network operation on a Linux machine. Whether you’re editing the classic /etc/resolv.conf, managing things through systemd-resolved, or using Netplan/NetworkManager on modern distributions, understanding exactly how your system decides which DNS server to ask — and in what order — is essential for building both reliable and secure Linux systems.