When a website stops loading or email starts bouncing, DNS is one of the first things to rule out. The three standard command-line tools for that are dig, host and nslookup: they send DNS queries directly and show you exactly what a resolver or an authoritative nameserver answers. In this tutorial you will use them on Ubuntu 24.04 to work out whether a problem is in your server's local resolver, in a public resolver's cache, or in the zone itself.
Prerequisites
To follow this tutorial you need:
- A server running Ubuntu 24.04 LTS, for example a CubePath VPS, with outbound UDP and TCP port 53 allowed.
- A non-root user with
sudoprivileges. - A domain to test. This guide uses
your_domainand the documentation address203.0.113.10; replace them with your own values.
Step 1 - Installing the DNS tools
On Ubuntu and Debian, dig, host, nslookup and delv all come from the bind9-dnsutils package. It is usually preinstalled on Ubuntu Server, but install it to be sure:
sudo apt update
sudo apt install bind9-dnsutils
On Rocky Linux, AlmaLinux and other RHEL-family systems the package is called bind-utils:
sudo dnf install bind-utils
Confirm the tools are available:
dig -v
DiG 9.18.30-0ubuntu0.24.04.2-Ubuntu
Step 2 - Checking the local resolver
Before querying anything, find out which resolver your server actually uses. On Ubuntu 24.04, /etc/resolv.conf is a symlink managed by systemd-resolved and points to a local stub at 127.0.0.53, which forwards queries to the real upstream servers.
resolvectl status
Global
Protocols: -LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
resolv.conf mode: stub
Link 2 (eth0)
Current Scopes: DNS
Protocols: +DefaultRoute -LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
Current DNS Server: 1.1.1.1
DNS Servers: 1.1.1.1 8.8.8.8
The DNS Servers line shows the upstream resolvers. Next, test resolution through the same path applications use (the Name Service Switch, which also reads /etc/hosts):
getent hosts your_domain
203.0.113.10 your_domain
If getent returns nothing, separate a DNS failure from a network failure by querying a public resolver directly by IP:
dig @1.1.1.1 your_domain +short
Interpret the result like this:
getent hosts | dig @1.1.1.1 | Likely cause |
|---|---|---|
| Fails | Works | Local resolver or systemd-resolved configuration |
| Fails | Times out | Outbound port 53 blocked or no network connectivity |
| Fails | Returns nothing | The record does not exist or the zone is broken |
| Wrong IP | Correct IP | Stale cache or an entry in /etc/hosts |
Check /etc/hosts for an override whenever the answer looks wrong:
grep your_domain /etc/hosts
Step 3 - Reading a dig response
dig gives the most complete view of a DNS answer. Query the A record of your domain:
dig your_domain A
; <<>> DiG 9.18.30-0ubuntu0.24.04.2-Ubuntu <<>> your_domain A
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 41872
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; QUESTION SECTION:
;your_domain. IN A
;; ANSWER SECTION:
your_domain. 300 IN A 203.0.113.10
;; Query time: 12 msec
;; SERVER: 127.0.0.53#53(127.0.0.53) (UDP)
;; WHEN: Fri Sep 25 10:30:00 UTC 2026
;; MSG SIZE rcvd: 56
The parts that matter when troubleshooting:
- status:
NOERRORmeans the query succeeded.NXDOMAINmeans the name does not exist.SERVFAILmeans the resolver could not get a valid answer (often a broken delegation or failed DNSSEC validation).REFUSEDmeans the server will not answer you. - flags:
aa(authoritative answer) appears only when you query an authoritative nameserver.rameans the server offers recursion. - ANSWER SECTION: the record, its type and its TTL in seconds. A cached answer shows a TTL that counts down on repeated queries.
- SERVER: which server answered. Here it is the local
systemd-resolvedstub.
A NOERROR response with ANSWER: 0 is called NODATA: the name exists but has no record of the type you asked for. This is common when a domain has an A record but no AAAA record.
To see only the records, use +short, or +noall +answer if you also want the TTL and type:
dig your_domain MX +noall +answer
your_domain. 3600 IN MX 10 mail.your_domain.
Note
digexits with status 0 even when the answer isNXDOMAINor empty. In scripts, test whether the+shortoutput is empty instead of relying on the exit code.
Querying the ANY type is not useful for troubleshooting. Most resolvers and many authoritative servers now return a minimal response to ANY (RFC 8482), so query each record type you care about explicitly.
Step 4 - Comparing resolvers and authoritative nameservers
Most "DNS is not updating" problems come down to caching: you changed a record at your DNS provider, but a resolver still serves the old value until its TTL expires. The way to prove it is to ask the authoritative nameservers directly and compare their answer with the resolvers'.
First, find the nameservers the domain is delegated to:
dig your_domain NS +short
ns1.your_dns_provider.com.
ns2.your_dns_provider.com.
Query one of them directly. The +norecurse option asks for the server's own data only, and the aa flag in the header confirms the answer is authoritative:
dig @ns1.your_dns_provider.com your_domain A +norecurse
Compare the SOA serial number on every nameserver. If the serials differ, a secondary has not picked up the latest zone yet:
dig your_domain SOA +short @ns1.your_dns_provider.com
dig your_domain SOA +short @ns2.your_dns_provider.com
ns1.your_dns_provider.com. hostmaster.your_domain. 2026092501 7200 3600 1209600 300
ns1.your_dns_provider.com. hostmaster.your_domain. 2026092501 7200 3600 1209600 300
Then check what large public resolvers return:
for server in 1.1.1.1 8.8.8.8 9.9.9.9; do
printf '%-10s %s\n' "$server" "$(dig @"$server" your_domain A +short | tr '\n' ' ')"
done
1.1.1.1 203.0.113.10
8.8.8.8 198.51.100.7
9.9.9.9 203.0.113.10
In this example the authoritative servers already return the new address and Google's resolver still has the old one cached. Nothing is broken: that resolver will fetch the new value when the cached record's TTL runs out. Before a planned change, lower the record's TTL (for example to 300 seconds) a day in advance so the switch propagates quickly.
If the authoritative servers themselves return the wrong data, or the domain returns SERVFAIL, follow the delegation from the root with +trace:
dig your_domain A +trace
+trace performs the resolution itself, starting at the root servers, and prints every referral: root, then the TLD servers, then your domain's nameservers. Look for the step where the answer stops making sense, such as the TLD delegating to nameservers you no longer use, or a nameserver that times out.
For DNSSEC-signed zones, delv validates the chain of trust and tells you why validation fails:
delv your_domain A
; fully validated
your_domain. 300 IN A 203.0.113.10
An unsigned zone prints ; unsigned answer instead, which is normal. A message such as resolution failed: no valid RRSIG points to a DNSSEC problem, commonly a stale DS record left at the registrar after changing DNS providers.
Step 5 - Quick lookups with host and nslookup
host prints short, human-readable answers and is handy for quick checks:
host your_domain
your_domain has address 203.0.113.10
your_domain mail is handled by 10 mail.your_domain.
Use -t to pick a record type, and add a server name at the end to query a specific resolver:
host -t TXT your_domain 1.1.1.1
nslookup is available on almost every operating system, including Windows, which makes it useful when you compare results with a colleague's workstation. The syntax is similar:
nslookup -type=MX your_domain 8.8.8.8
Server: 8.8.8.8
Address: 8.8.8.8#53
Non-authoritative answer:
your_domain mail exchanger = 10 mail.your_domain.
"Non-authoritative answer" simply means the reply came from a resolver's cache rather than from the zone's own nameserver. For anything beyond a quick check, prefer dig: its output shows the status code, flags and TTLs that host and nslookup hide.
Step 6 - Checking reverse DNS
Reverse DNS maps an IP address back to a hostname through a PTR record. Mail servers in particular check it, and many reject mail from IPs without a matching PTR. Query it with dig -x:
dig -x 203.0.113.10 +short
mail.your_domain.
For mail servers, the PTR name should resolve forward to the same IP (forward-confirmed reverse DNS):
dig mail.your_domain A +short
203.0.113.10
PTR records live in the zone of whoever owns the IP block, not in your domain's zone. If the result is empty or generic, set the reverse DNS name through your hosting provider's control panel or support, not at your domain's DNS provider.
Step 7 - Checking email DNS records
When mail is rejected or lands in spam, verify the records that receiving servers check. The MX records tell other servers where to deliver mail for your domain:
dig your_domain MX +short
SPF is a TXT record at the domain apex that starts with v=spf1. There must be exactly one:
dig your_domain TXT +short | grep 'v=spf1'
"v=spf1 mx ip4:203.0.113.10 -all"
DKIM public keys are published under selector._domainkey. The selector depends on your mail software or provider and appears in the s= tag of the DKIM-Signature header of a message you sent:
dig your_selector._domainkey.your_domain TXT +short
DMARC lives at _dmarc:
dig _dmarc.your_domain TXT +short
"v=DMARC1; p=quarantine; rua=mailto:dmarc@your_domain"
An empty result for any of these means the record is missing or published under the wrong name, a frequent mistake when a DNS panel automatically appends the domain and you typed the full name as well (producing _dmarc.your_domain.your_domain).
Step 8 - Fixing local resolver problems
If Step 2 showed that public resolvers answer correctly but your server does not, the problem is on the server.
Flush the systemd-resolved cache so it fetches fresh answers:
sudo resolvectl flush-caches
Check cache statistics to confirm the flush (the current cache size drops to 0):
resolvectl statistics
To change the upstream DNS servers, do not edit /etc/resolv.conf directly: it is generated, and on Ubuntu 24.04 your edits are overwritten. Create a drop-in file for systemd-resolved instead:
sudo mkdir -p /etc/systemd/resolved.conf.d
sudo nano /etc/systemd/resolved.conf.d/dns.conf
Add the resolvers you want to use:
[Resolve]
DNS=1.1.1.1 9.9.9.9
FallbackDNS=8.8.8.8
Restart the service and confirm the new servers are in use:
sudo systemctl restart systemd-resolved
resolvectl status
Servers received through DHCP or configured in Netplan are still listed per interface. If you want to replace them rather than add to them, edit your Netplan file under /etc/netplan/: list the resolvers under nameservers: addresses: for the interface, add dhcp4-overrides: use-dns: false if the interface uses DHCP, and run sudo netplan apply.
Troubleshooting
;; communications error to 1.1.1.1#53: timed out: outbound DNS is blocked. Check your firewall rules (sudo ufw status verbose) and any cloud firewall in front of the server. Trydig +tcpto see whether only UDP is blocked.status: SERVFAILfrom resolvers, but the authoritative servers answer: usually DNSSEC. Rundelv your_domainand check the DS record at your registrar withdig your_domain DS +short.status: NXDOMAINfor a domain you just registered: the delegation is not active yet.dig your_domain NS +traceshows whether the TLD servers already list your nameservers.- Correct answers in
digbut the application still connects to the old IP: the application or a language runtime (Java, for example) caches DNS itself. Restart the application after the change.
Conclusion
You now have a repeatable method for DNS problems: check what the local resolver returns, compare it with public resolvers and the authoritative nameservers, and use +trace and delv when the delegation or DNSSEC is suspect. In most cases the result tells you whether to wait for a TTL, fix a record, or fix the server's resolver configuration. As next steps, review your mail setup with the email records from Step 7, or lower TTLs ahead of your next migration.
