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 sudo privileges.
  • A domain to test. This guide uses your_domain and the documentation address 203.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 hostsdig @1.1.1.1Likely cause
FailsWorksLocal resolver or systemd-resolved configuration
FailsTimes outOutbound port 53 blocked or no network connectivity
FailsReturns nothingThe record does not exist or the zone is broken
Wrong IPCorrect IPStale 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: NOERROR means the query succeeded. NXDOMAIN means the name does not exist. SERVFAIL means the resolver could not get a valid answer (often a broken delegation or failed DNSSEC validation). REFUSED means the server will not answer you.
  • flags: aa (authoritative answer) appears only when you query an authoritative nameserver. ra means 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-resolved stub.

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.

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. Try dig +tcp to see whether only UDP is blocked.
  • status: SERVFAIL from resolvers, but the authoritative servers answer: usually DNSSEC. Run delv your_domain and check the DS record at your registrar with dig your_domain DS +short.
  • status: NXDOMAIN for a domain you just registered: the delegation is not active yet. dig your_domain NS +trace shows whether the TLD servers already list your nameservers.
  • Correct answers in dig but 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.