On Ubuntu (and many other distributions), name resolution goes through systemd-resolved: a local DNS resolver and cache. That’s why /etc/resolv.conf contains nameserver 127.0.0.53 instead of your provider’s DNS servers. Knowing how it works saves time when DNS breaks, or when you want to run your own DNS server on port 53.
How it fits together
app → /etc/resolv.conf (nameserver 127.0.0.53) → systemd-resolved (stub on 127.0.0.53:53)
→ upstream DNS servers (from netplan/DHCP/config)
- 127.0.0.53 is a local “stub” listener. It caches answers, and forwards queries upstream.
- The real upstream servers come from the network configuration (netplan, DHCP, NetworkManager), or from
/etc/systemd/resolved.conf.
ls -l /etc/resolv.conf
# → ../run/systemd/resolve/stub-resolv.conf
See what’s actually used
resolvectl status # global and per-interface DNS servers, DNSSEC, DoT
resolvectl dns # just the servers
resolvectl query example.com
resolvectl statistics # cache hits/misses
dig and nslookup query 127.0.0.53 by default. Query an upstream server directly to compare: dig @1.1.1.1 example.com.
Set DNS servers
Per interface (recommended), via netplan:
# /etc/netplan/50-cloud-init.yaml (excerpt)
network:
ethernets:
eth0:
nameservers:
addresses: [1.1.1.1, 9.9.9.9]
sudo netplan try # then: sudo netplan apply
See static IP with netplan and change DNS server on Linux.
Globally, via resolved.conf:
# /etc/systemd/resolved.conf (or a drop-in in /etc/systemd/resolved.conf.d/)
[Resolve]
DNS=1.1.1.1 9.9.9.9
FallbackDNS=8.8.8.8
DNSOverTLS=opportunistic
sudo systemctl restart systemd-resolved
Temporarily (until reboot): sudo resolvectl dns eth0 1.1.1.1 9.9.9.9.
DNS over TLS
DNSOverTLS=yes encrypts queries to the upstream resolver (port 853). The upstream must support DoT. Cloudflare, Quad9 and Google do. opportunistic tries TLS, and falls back to plain DNS. See DNS over HTTPS.
Flush the cache
sudo resolvectl flush-caches
resolvectl statistics
Useful after DNS changes. See DNS propagation.
Port 53 conflicts
Running your own DNS server (Pi-hole, AdGuard Home, BIND, dnsmasq, or Unbound) fails with “address already in use”, because resolved’s stub listens on 127.0.0.53:53. Disable the stub:
# /etc/systemd/resolved.conf.d/no-stub.conf
[Resolve]
DNSStubListener=no
sudo systemctl restart systemd-resolved
sudo ln -sf /run/systemd/resolve/resolv.conf /etc/resolv.conf # point to the real upstreams
See Pi-hole on a VPS and AdGuard Home.
Troubleshooting
| Symptom | Check |
|---|---|
| “Temporary failure in name resolution” | resolvectl status: any DNS servers? Is systemd-resolved running? |
Works with dig @1.1.1.1 but not normally |
Upstream config, or a broken stub. Restart resolved, check /etc/resolv.conf symlink |
| Docker containers can’t resolve | Containers can’t use 127.0.0.53; Docker reads the upstream file. Set "dns" in /etc/docker/daemon.json if needed |
| Old answers after a DNS change | resolvectl flush-caches |
/etc/resolv.conf keeps changing |
It’s managed. Change DNS via netplan or resolved.conf instead |
Logs: journalctl -u systemd-resolved. See journalctl.
Should you disable it?
For most servers, no. resolved’s cache speeds up repeated lookups (APIs, package mirrors, database hosts), and the per-interface configuration works well with netplan and VPNs. Disable or bypass it only when you run your own resolver on the server, or an application insists on reading real upstream servers from /etc/resolv.conf. If you do disable the service entirely, write a static /etc/resolv.conf with your chosen servers, and make sure nothing else (cloud-init, DHCP hooks) overwrites it.
Quick reference
| Task | Command |
|---|---|
| Status | resolvectl status |
| Test a lookup | resolvectl query example.com |
| Flush cache | resolvectl flush-caches |
| Set DNS (permanent) | netplan nameservers, or resolved.conf DNS= |
| Free port 53 | DNSStubListener=no |
Related: Linux DNS server configuration and ping, traceroute and mtr.