DNS and name resolution
hosts, resolved, dig and getent.
Programs connect to names such as api.example.com, and before any connection starts the system has to turn the name into an address. This lesson follows that lookup on Ubuntu 26.04 and RHEL 10: which sources are consulted and in what order, the local resolver Ubuntu runs, why getent and dig can disagree, the record types you will meet, and what each error message says about which part failed. Many reports that "the network is down" turn out to be name resolution, and the error text usually tells you so.
Where a name is looked up
Programs do not usually speak DNS themselves. They call the C library, which reads the hosts line of /etc/nsswitch.conf to learn which sources to consult, in order:
files is the file /etc/hosts and dns means the DNS servers named in /etc/resolv.conf. The first source that knows the name wins. RHEL adds myhostname, which always resolves the machine's own name and localhost even when the other sources fail. /etc/hosts is a plain list of addresses and names:
The first lines map localhost and the IPv6 loopback names. 127.0.1.1 web01 is the Debian and Ubuntu convention for the machine's own name, written here by cloud-init. The host.lima.internal line was added by the lab's virtualisation tool. getent hosts asks the same question a program asks, through the same sources in the same order, so it shows what programs will get:
The local resolver on Ubuntu
On Ubuntu, programs send their DNS queries to systemd-resolved, a local service that forwards them to the real DNS servers and caches the answers. /etc/resolv.conf points at it:
The file is a link to one that systemd-resolved maintains, and the only server it names is 127.0.0.53, the resolver's stub listener on the loopback interface. The real servers are shown by resolvectl status:
Current DNS Server is the server queries go to, learned from DHCP for eth0. In this lab it is the virtualisation tool's gateway, which passes queries on to the workstation's own resolver. +mDNS (multicast DNS for names on the local network) is switched on by the virtualisation tool; Ubuntu ships it off. The resolver also listens on 127.0.0.54, a plain proxy that passes queries to the same servers without adding its own features. Ubuntu configures systemd-resolved with Cache=no-negative (in /usr/lib/systemd/resolved.conf.d/cache-no-negative.conf), so it caches answers that exist but not "no such name" replies.
RHEL does not run systemd-resolved. NetworkManager writes the DNS server it received straight into /etc/resolv.conf, and programs query that server directly:
getent and dig ask different questions
getent hosts answers "what address will a program get for this name?". dig, from the bind9-dnsutils package that Ubuntu Server installs by default (bind-utils on RHEL), answers a narrower question: "what does this DNS server say?". It sends one DNS query and prints the whole reply:
Read the reply from the top. status: NOERROR means the server answered normally. The QUESTION SECTION repeats what was asked: the name, class IN (internet) and type A (an IPv4 address, the default). Each line of the ANSWER SECTION is a record: name, TTL, class, type and data. The TTL, time to live, is how many seconds a resolver may cache the answer; this lab's upstream server always reports 0, which real resolvers do not. SERVER shows who answered, the local stub. dig +short prints only the data.
The difference matters when /etc/hosts is involved. Add a name that exists only in that file:
getent finds the name in /etc/hosts, as every program on the machine will. dig itself never reads that file, yet here it gets the answer too: systemd-resolved also reads /etc/hosts and answers from it, which resolvectl query marks as Data from: synthetic. It also reports such local data as authenticated, because resolved trusts what it reads from its own machine; no DNSSEC check was involved. Only when dig asks a real DNS server directly, here the public resolver Quad9 at 9.9.9.9 with @9.9.9.9, does the truth appear: NXDOMAIN, no such name. On RHEL, which has no systemd-resolved, a plain dig already asks the real server. So to learn what DNS itself says, name the server: dig @SERVER NAME. Remove the test line again; systemd-resolved notices the change within a few seconds:
Record types
A DNS name can hold records of several types. Because this lab's upstream server reports every TTL as 0, this section asks Quad9 directly, and +noall +answer prints just the answer lines:
A records hold IPv4 addresses and AAAA records IPv6 addresses; a name can have several of each. MX names the mail server for a domain; 0 . is a "null MX", which says the domain accepts no mail. TXT holds free text, used for things such as SPF mail policies (v=spf1 -all says no server may send mail for this domain) and ownership checks. CNAME makes a name an alias: www.iana.org points to a name at its content delivery network, and the resolver follows it to the A records. NS lists the servers that are authoritative for a domain, the ones that hold its records, and PTR maps an address back to a name, which dig -x asks for.
The TTL is why a DNS change takes time to reach everyone: a resolver that cached the old answer keeps using it until its TTL runs out. sudo resolvectl flush-caches empties the cache on this machine only; other resolvers keep theirs.
When a name does not resolve
Names under .invalid are reserved and never exist, which makes them safe for producing the "no such name" error. Each tool words it differently:
getent prints nothing and exits with status 2. Name or service not known is the C library's message for a name that does not exist, and ssh and many other programs print the same text. In DNS terms the answer was NXDOMAIN, "no such domain". A different answer, SERVFAIL, means the resolver could not get a trustworthy answer at all. The domain below has deliberately broken DNSSEC signatures (DNSSEC signs DNS records so resolvers can check them), and Quad9, which checks them, refuses to answer:
EDE, an extended DNS error, gives the reason. In daily work SERVFAIL usually means the domain's own servers are down or misconfigured, which you cannot fix from your side.
The third failure is a resolver that does not answer at all. To produce it safely, the lab uses a network namespace, an isolated copy of the network stack in which nothing listens on 127.0.0.53, as if systemd-resolved had stopped. Commands prefixed with sudo ip netns exec ess-dns-off run inside it:
Names from /etc/hosts still resolve, because files comes first and needs no network. Everything else fails with Temporary failure in name resolution, which means no DNS server answered: the name may be fine. dig shows why, with connection refused on 127.0.0.53. curl prints the same message for both failures, so check with getent or dig before deciding which one you have. On a real Ubuntu server, resolvectl status and systemctl status systemd-resolved are the next commands.
Because files comes first, a line in /etc/hosts silently overrides DNS for every program on the machine, which is why attackers with root access sometimes add entries there, for example to redirect an update or licence server. Whoever controls the resolver can redirect every lookup, so check that resolvectl status (or /etc/resolv.conf on RHEL) names the servers you expect. DNS can also carry data out of a network, encoded in the names being looked up, so a flood of lookups for odd names under one unfamiliar domain is worth investigating.
Try this
Point a name that exists in DNS somewhere else and watch who notices: add 192.0.2.50 example.com to /etc/hosts with echo "192.0.2.50 example.com" | sudo tee -a /etc/hosts. Wait a few seconds, then predict and check each result: getent hosts example.com and dig +short example.com both return 192.0.2.50 (the second through systemd-resolved), dig +short @9.9.9.9 example.com returns the real addresses, and the first line of ping -c 1 -W 2 example.com shows that ping uses 192.0.2.50. Remove the line with sudo sed -i "/192.0.2.50 example.com/d" /etc/hosts, wait a few seconds, and check that getent returns the real addresses again.
Takeaway
When a name will not resolve, compare getent hosts NAME (what programs get) with dig @SERVER NAME (what DNS says), then read the message: Name or service not known means the name does not exist, Temporary failure in name resolution means no DNS server answered.