skip to content

On a Linux host running systemd-resolved, /etc/resolv.conf contains the single line `nameserver 127.0.0.53`. What is answering at that address, where are the real upstream servers configured, and why does editing that file usually not stick?

level: middleimportance: should knowfreq 47%

answer

  1. a loopback address, not your DNS server
  2. something local is listening on port 53
  3. the file is generated, not authored
  4. configuration is per network link
  5. resolvectl status shows the real servers

basics

~20 s

127.0.0.53 is systemd-resolved's local stub listener, a caching forwarder running on the host itself. The real upstream servers come from the network configuration or resolved.conf and are shown by resolvectl status. The file is a generated symlink, so edits are overwritten.

solid answer

~40 s

`127.0.0.53` is the **stub listener** that systemd-resolved opens on the loopback interface. It is a local caching forwarder, not a nameserver you administer and not a server for the LAN. Applications send ordinary DNS queries there; resolved decides which upstream to forward each one to, caches the results, and handles feature negotiation such as EDNS and DNSSEC. The upstream servers come from DHCP, NetworkManager or systemd-networkd per link, or statically from `DNS=` in `/etc/systemd/resolved.conf`, and `resolvectl status` shows them link by link. Editing `/etc/resolv.conf` normally does nothing lasting because it is a symlink to a file under `/run` that resolved regenerates — change `resolved.conf` or the link's configuration instead. The per-link model also gives you split DNS: a routing domain such as `~corp.example.com` on the VPN link sends only those names down the tunnel.

code

bash · 2 lines
bash
readlink -f /etc/resolv.conf
resolvectl status

go deeper

for a junior

Recognise 127.0.0.53 as a local resolver run by systemd-resolved rather than a broken configuration, and know not to hand-edit /etc/resolv.conf on such a host.

for a middle

Explain that resolved is a caching forwarder holding its configuration per network link, that the file is a generated symlink under /run, and where the upstream servers actually come from.

for a senior

Show you can debug split DNS: reason about which link a query is routed to, why a name resolves differently on and off VPN, and how a local cache can make the host disagree with upstream after a record change.

for a principal

Take a position on the resolver layer for a fleet: a per-host caching stub versus centralised resolvers, what each does to blast radius, cache-flush operations and observability, and how DNS configuration is delivered to machines consistently.

## What the loopback address means Seeing `nameserver 127.0.0.53` tells you the machine resolves through a **local stub**. systemd-resolved binds a DNS listener on that loopback address, port 53. Anything that speaks plain DNS — including `dig` and any application whose NSS path reaches the `dns` module — sends its query there, and resolved forwards it upward. The address is deliberately not `127.0.0.1`: that keeps the stub out of the way of any real DNS server an administrator might want to run on loopback, and it makes the configuration self-identifying at a glance. A second path exists alongside the stub. When `/etc/nsswitch.conf` lists the `resolve` module (nss-resolve), applications reach resolved through its bus API instead of by sending a DNS packet, and never touch the stub at all. Both paths end at the same daemon. ## Where the real servers live systemd-resolved keeps DNS configuration **per network link**, which is the central design idea and the reason a single flat file cannot represent it. Servers arrive from several sources: - DHCP, applied by NetworkManager or systemd-networkd to the link that got the lease. - Static per-link configuration in a networkd `.network` file or a NetworkManager connection profile. - Global `DNS=` and `FallbackDNS=` settings in `/etc/systemd/resolved.conf`. - Runtime overrides with `resolvectl dns <link> <address>`. `resolvectl status` prints the merged result: the global configuration, then each link with its current servers and domains. That output, not `/etc/resolv.conf`, is the truth on such a host. ## Routing domains and split DNS Each link also carries a domain list. A plain entry like `corp.example.com` is a *search* domain. An entry prefixed with a tilde, `~corp.example.com`, is a **routing domain**: it does not get appended to short names, it declares that queries for names under it should be sent to *this link's* servers. The special routing domain `~.` makes a link the default route for every name. This is what makes split DNS work without a proxy: connect a VPN, give its link `~corp.example.com`, and internal names go down the tunnel while everything else keeps using the servers your local network handed out. It is also the first thing to inspect when a name resolves correctly off-VPN and wrongly on-VPN, or the reverse. ## Why your edit disappeared On a resolved-managed host `/etc/resolv.conf` is a symlink, most often to `/run/systemd/resolve/stub-resolv.conf`, and `/run` is a tmpfs regenerated at boot. Editing through the symlink writes to a runtime file that resolved owns and rewrites; editing after replacing the symlink with a real file leaves you with configuration that the rest of the system does not know about. Either way, the change is either reverted or ineffective. The supported choices are to point the symlink at a different generated file, or to configure the servers where they belong: ```bash readlink -f /etc/resolv.conf resolvectl status ``` A second generated file, `/run/systemd/resolve/resolv.conf`, lists the current upstream servers directly rather than the stub. Linking `/etc/resolv.conf` there makes applications bypass the stub — losing the cache and split-DNS routing, which is occasionally what you want for software that refuses to talk to a loopback resolver. ## Consequences to be ready for - **Answers are cached locally**, so a record change may be visible upstream while the host still returns the old value until the cache entry expires or is flushed. - **`dig` output can mislead.** A query to the stub is answered by resolved after its own routing and synthesis, including entries it reads from `/etc/hosts`. Querying an upstream address explicitly isolates that. - **The stub is host-local.** It listens on loopback only; nothing outside the machine can use it, and it is not a substitute for a real internal resolver. - **Statistics and cache control** are part of the daemon, not the file: `resolvectl statistics` and `resolvectl flush-caches` operate on it directly.

  • What is the difference between the domain entries `corp.example.com` and `~corp.example.com` on a link?
    The first is a search domain: it may be appended to short, unqualified names. The second is a routing domain: it is never appended, and instead tells resolved that queries for names under corp.example.com should be sent to that link's servers. Routing domains are the mechanism behind split DNS on a VPN link.
  • Why does systemd-resolved keep DNS configuration per link rather than as one global list?
    Because a host is often on several networks at once — a corporate VPN alongside home Wi-Fi, for example — and each supplies its own servers that are authoritative for different names. A single flat list would force one of them to win globally. Per-link configuration plus routing domains lets each network answer only for what it owns.
  • An application refuses to use a loopback nameserver. What are your options?
    Point /etc/resolv.conf at the other generated file, /run/systemd/resolve/resolv.conf, which lists the upstream servers directly, or configure the application with the upstream addresses. You then give up resolved's cache and its split-DNS routing, so verify that names which relied on a per-link routing domain still resolve correctly.

saying these in an interview costs you the question

  • Thinks the machine is running a DNS server for the network
  • Assumes 127.0.0.53 means DNS is misconfigured
  • Hand-edits /etc/resolv.conf and expects it to persist
  • Believes resolv.conf shows the real upstream servers
  • Confuses the loopback stub with a public resolver address

context