Skip to content

DNS Tools

As seen in DHCP Basics, a DHCP server hands a client DNS server addresses along with its IP address. But there's more to "knowing where the DNS server is" than actually using it — several Linux-specific layers sit in between: /etc/hosts, /etc/nsswitch.conf, resolv.conf, and, on modern systems, systemd-resolved. The DNS protocol itself — query types, recursive versus iterative resolution, A/AAAA/MX/CNAME records — is covered in DNS Resolution; this article covers how a Linux host actually resolves a name, and the tools for diagnosing that process.

How a Name Becomes an Address: NSS and nsswitch.conf

On Linux, an application doesn't send a hostname directly to a DNS server — it calls into glibc's getaddrinfo()/gethostbyname() functions, which consult the Name Service Switch (NSS) to decide which sources to check, and in what order. That order is written in the hosts: line of /etc/nsswitch.conf:

grep '^hosts:' /etc/nsswitch.conf
hosts:          files dns

files dns means: check /etc/hosts first, and if the name isn't found there, query DNS. On systems using modern systemd-resolved, this line often also includes a resolve module:

hosts:          files resolve [!UNAVAIL=return] dns

In this case, a lookup first checks /etc/hosts, then the systemd-resolved service, and only falls through directly to DNS if that service is unavailable — for instance, if it isn't running.

/etc/hosts: Local, Manually Managed Entries

cat /etc/hosts
127.0.0.1       localhost
127.0.1.1       webprod01
192.168.1.50    db-internal.local db-internal

Format: <IP> <primary-name> [additional-names...]. Entries in this file have no connection to any DNS server — they only apply on this specific host. This is typically used for:

  • a small lab or test environment that doesn't need a DNS server;
  • temporarily naming a new service not yet registered in DNS;
  • deliberately pointing a specific hostname at a different IP — pointing a production domain at a local server for testing, for example.

Warning

An entry in /etc/hosts always takes priority over DNS, per the nsswitch.conf order shown above. This is a useful debugging tool, but a forgotten test entry can cause confusing "it only behaves differently on this one server" problems in production. Always remove a temporary entry once testing is done.

The Modern Resolver: systemd-resolved

On most modern Ubuntu/Debian installs, /etc/resolv.conf doesn't point at a real DNS server's IP — it points at a local stub resolver:

cat /etc/resolv.conf
nameserver 127.0.0.53
options edns0 trust-ad

127.0.0.53 is the systemd-resolved service running on the machine itself. This service keeps track of the actual DNS servers obtained from DHCP or static configuration, caches queries, and forwards them to the real servers. To see the real, external DNS servers, cat /etc/resolv.conf isn't enough — a dedicated command is needed:

resolvectl status
Link 2 (enp0s3)
    Current Scopes: DNS
         Protocols: +DefaultRoute +LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
Current DNS Server: 192.168.1.1
       DNS Servers: 192.168.1.1 1.1.1.1

Current DNS Server is the server queries are currently being sent to; DNS Servers is the full list available for this interface, usually obtained via DHCP. Per-interface statistics, and clearing the cache:

resolvectl statistics
sudo resolvectl flush-caches

flush-caches clears out stale cached results — for example, when a DNS record has changed but the resolver is still returning the old IP. This command doesn't require restarting the service, and is safe to run.

Note

On systems where systemd-resolved isn't running, or on minimal systems (some container images, for instance), /etc/resolv.conf may contain real DNS server IPs directly. Whether resolvectl status succeeds or returns an error tells you which situation you're in.

Sending a Manual DNS Query: dig, host, nslookup

Warning

dig, host, and nslookup all query DNS directly — they ignore /etc/hosts and the nsswitch.conf order entirely. That means dig can return the "correct" DNS answer while an application on the same host still connects to a different IP, because the application resolves through NSS (which checks /etc/hosts and systemd-resolved first). To resolve a name the same way an application does, use getent:

getent hosts app.example.com

getent hosts walks the full hosts: chain from nsswitch.conf, so its answer reflects /etc/hosts overrides and the files/resolve modules — not just raw DNS. When dig and getent disagree, that difference is the bug: something in the local NSS path (most often a stray /etc/hosts entry) is overriding DNS.

dig — the most detailed, and the most script-friendly

dig example.com
;; QUESTION SECTION:
;example.com.                  IN      A

;; ANSWER SECTION:
example.com.            86400   IN      A       93.184.216.34

;; Query time: 24 msec
;; SERVER: 192.168.1.1#53(192.168.1.1)

The parts that matter:

  • ANSWER SECTION — the requested record type (A, here) and the value returned;
  • 86400 — the TTL (time to live), in seconds; other resolvers may cache and reuse this answer for that long — this is often exactly why an old value still shows up right after a DNS record was changed;
  • SERVER — which DNS server the answer came from.

Querying a different record type, and getting a short-form answer:

dig MX example.com
dig +short A example.com

Querying a specific server directly, bypassing the local resolver:

dig @1.1.1.1 example.com

This is especially useful: if dig example.com gives a wrong result but dig @1.1.1.1 example.com gives the correct one, the problem isn't in external DNS — it's in the local resolver or its cache.

Reverse lookup (IP to name):

dig -x 93.184.216.34 +short

host — short, quick check

host example.com
example.com has address 93.184.216.34
example.com mail is handled by 10 mail.example.com.

nslookup — older, but still widely seen

nslookup example.com

nslookup still shows up in older documentation and scripts; it returns functionally the same information as dig/host, but its own developers officially recommend dig for more complex diagnostics.

Practical Scenario: One Server Still Points at the Old Address After a DNS Change

Problem: the DNS record for app.example.com was changed to point at a new server, but a query from one particular application server is still returning the old IP, while other servers resolve correctly.

Step 1 — isolate the problem:

dig +short app.example.com
dig @1.1.1.1 +short app.example.com

If the local query returns the old value while the external server query returns the new one, the problem isn't global DNS — it's specific to this host's local resolution chain.

Step 2 — check /etc/hosts:

grep app.example.com /etc/hosts

If a forgotten test entry turns up here, that's the cause — removing it is enough.

Step 3 — if /etc/hosts is clean, check the cache:

resolvectl status | grep -A2 'Current DNS'
sudo resolvectl flush-caches
dig +short app.example.com

Step 4 — result: if the new IP shows up after clearing the cache, the problem was a stale answer cached before the TTL expired. If the old IP is still returned, the next step is verifying the DNS server list obtained via DHCP itself is correct (from the lease file covered in DHCP Basics).

Step 5 — documentation: record which layer the problem was in — the hosts file, the cache, or the DNS server itself — and how it was found.

Common Mistakes

Forgetting an Old Test Entry in /etc/hosts

A line added for a temporary redirect stays behind after testing, and later causes confusing errors that reproduce on exactly one server. Keep track of when each entry you add should be removed.

Editing resolv.conf by Hand

On a system running systemd-resolved, /etc/resolv.conf is normally an auto-generated symlink or file; manual edits disappear the next time it's regenerated. To change the DNS server, do it through netplan or NetworkManager configuration instead.

Interpreting a Stale Cached Answer as "the DNS Change Didn't Work"

Returning an old answer until the TTL expires is normal DNS behavior, not a bug. Check the TTL value in dig's output first, then the local cache if needed, before drawing a conclusion.

Declaring "DNS Is Completely Broken" After Querying Only One Server

A single server can be temporarily down. Don't reach a conclusion without checking at least a second, independent source with dig @<other-server>.

Interview angle

A frequent interview probe is: "a record's TTL was just lowered to speed up a planned migration — did that take effect immediately?" It didn't. The old TTL still governs how long any resolver that already cached the record keeps using it; lowering the TTL in the zone only changes how long future lookups get cached, going forward. This is exactly why DNS migrations are planned well ahead of the cutover — the TTL needs to be lowered first, left to propagate for at least its old duration, and only then is the actual record changed.

Exercises

  1. Find the hosts: line in /etc/nsswitch.conf and explain the meaning of every keyword in it.
  2. Compare the Current DNS Server and DNS Servers values from resolvectl status, and determine where they came from — static configuration or DHCP.
  3. Compare the results of dig example.com, dig @1.1.1.1 example.com, and dig +short MX example.com, and write down the difference between each.
  4. On a test VM, deliberately add an incorrect entry to /etc/hosts, detect it with ping or dig, then remove it and re-verify the result.

Verification criterion: for exercise 4, after removal, dig and ping must both resolve the hostname to the same address that an independent query against dig @1.1.1.1 returns — if they still disagree, another cached or local override is still in play.

References

  • man7.org: nsswitch.conf(5): https://man7.org/linux/man-pages/man5/nsswitch.conf.5.html
  • man7.org: hosts(5): https://man7.org/linux/man-pages/man5/hosts.5.html
  • man7.org: resolv.conf(5): https://man7.org/linux/man-pages/man5/resolv.conf.5.html
  • freedesktop.org: resolvectl(1): https://www.freedesktop.org/software/systemd/man/latest/resolvectl.html
  • freedesktop.org: systemd-resolved.service(8): https://www.freedesktop.org/software/systemd/man/latest/systemd-resolved.service.html
  • ISC BIND: dig(1) manual: https://bind9.readthedocs.io/en/latest/manpages.html#dig-dns-lookup-utility
  • man7.org: nslookup(1): https://man7.org/linux/man-pages/man1/nslookup.1.html