linuxlab.io
Tutorials▾
  • Linux & networking
    File system, processes, TCP/IP, BGP and OSPF
    →
  • Terraform & IaC
    HCL, state, plan/apply on a LocalStack sandbox
    →
  • Git & GitHub
    Object model, plumbing, branching, GitHub Actions
    →
All tutorials →
PricingAboutSign inCreate account
/
  • Introduction
  • Lessons
  • How it works
  • Simulator
  • Knowledge base
  • Interview prep
Index
Categories
All entries
Footer
linuxlab-TutorialsPricingAboutPrivacy & cookies
Copyright © 2026 LinuxLab. All rights reserved.
home/linux/kb/Processes & resources/systemd-resolved

kb/processes ── Processes & resources ── intermediate

systemd-resolved: the local DNS stub

systemd-resolved is a DNS stub resolver. It listens on `127.0.0.53:53` and proxies queries to upstream DNS, aggregating data from NetworkManager, DHCP, and VPN. You drive it with `resolvectl`.

view as markdownaka: resolvectl, stub-resolver, systemd-resolved-service

What it is and why

/etc/resolv.conf used to be a plain text file. You wrote nameserver lines into it by hand or through a DHCP client. The old approach had problems:

  • Three nameservers maximum (per RFC).
  • Every interface shared one list. There was no per-link DNS, which matters for VPN.
  • No caching, so every query went out to the network.
  • No DNSSEC or DoT (DNS-over-TLS) out of the box.

systemd-resolved is a stub daemon that solves this. It listens on the address 127.0.0.53:53 (the last octet is 53, the DNS port) and:

  • Accepts queries from local applications through /etc/resolv.conf.
  • Keeps separate DNS-server configs for each network interface (enp0s3 goes through the ISP, wg0 through the VPN, so each name resolves where it should).
  • Caches answers.
  • Supports DNSSEC, DoT, LLMNR, and mDNS.
  • Receives upstream DNS from NetworkManager, systemd-networkd, or DHCP.

The basic flow:

curl example.com
     ↓ (libc gethostbyname)
/etc/resolv.conf → nameserver 127.0.0.53
     ↓ (UDP to 127.0.0.53:53)
systemd-resolved
     ↓ (picks an interface by routing, sends upstream)
External DNS (ISP / 8.8.8.8 / 1.1.1.1)

/etc/resolv.conf is a symlink

On current Ubuntu 22+, Fedora, and RHEL 9+, the file is a symlink:

bash
ls -l /etc/resolv.conf

▸../run/systemd/resolve/stub-resolv.conf

It contains only nameserver 127.0.0.53 plus the search domains. The real upstream DNS shows up through resolvectl, not in this file.

The symlink can point at one of three targets:

Symlink targetBehavior
/run/systemd/resolve/stub-resolv.confthrough 127.0.0.53 (default)
/run/systemd/resolve/resolv.confstraight to upstream, no stub
your own static /etc/resolv.confbypass systemd-resolved fully

If you need to turn resolved off and go back to the old way:

bash
sudo systemctl disable --now systemd-resolved
sudo rm /etc/resolv.conf
echo 'nameserver 1.1.1.1' | sudo tee /etc/resolv.conf
# But NetworkManager may overwrite this, so disable its management

resolvectl, the main tool

bash
resolvectl status                      # all links plus upstream DNS on each one
resolvectl query example.com           # resolve through resolved (like dig, but via the stub)
resolvectl statistics                  # cache hits/misses
resolvectl flush-caches                # drop the cache
resolvectl dns enp0s3 1.1.1.1 9.9.9.9  # set DNS on a specific interface
resolvectl domain wg0 '~corp.local'    # split-DNS: only corp.local queries go through wg0

A ~ prefix on a domain means routing-only (use this link only for those domains). Without ~, it is a regular search domain.

Configuration: /etc/systemd/resolved.conf

Global settings live here. Do not edit the file itself. Use a drop-in:

ini
# /etc/systemd/resolved.conf.d/dns.conf
[Resolve]
DNS=1.1.1.1#cloudflare-dns.com 9.9.9.9
FallbackDNS=8.8.8.8
Domains=~.                # all queries through DNS= (not per-link)
DNSSEC=allow-downgrade
DNSOverTLS=opportunistic
Cache=yes

After editing:

bash
sudo systemctl restart systemd-resolved
resolvectl status     # check that it took effect

A drop-in (see systemd-drop-ins) matters because package updates can overwrite /etc/systemd/resolved.conf.

/etc/nsswitch.conf: the resolver order

Name resolution runs through the chain in nsswitch.conf:

hosts: files myhostname mdns4_minimal [NOTFOUND=return] resolve [!UNAVAIL=return] dns

Left to right:

  1. files → /etc/hosts (static entries)
  2. myhostname → the host's own name plus localhost
  3. mdns4_minimal → Avahi/mDNS for the local network
  4. resolve → systemd-resolved over D-Bus (fast, supports DNSSEC)
  5. dns → fallback to the classic glibc resolver through /etc/resolv.conf

If you do not need the cross-check and want only resolved, leave files resolve [!UNAVAIL=return] dns. You rarely have to change nsswitch, but it is the first thing you look at when "host pings but dig fails" (or the other way around).

DNSSEC, DoT, LLMNR: what to enable

  • DNSSEC validates DNS-zone signatures. DNSSEC=allow-downgrade is a sensible default: it tries, but it does not break resolution when upstream cannot do it.
  • DNSOverTLS encrypts DNS traffic to the upstream server. opportunistic tries TLS and falls back to plain. For real guarantees you need an upstream that actually supports it (1.1.1.1, 9.9.9.9).
  • LLMNR is link-local multicast, a WINS replacement, meant for home networks. It is noisy and you can turn it off (LLMNR=no).
  • MulticastDNS resolves .local names on the LAN. If Avahi is installed, turn it off in resolved (MulticastDNS=no) so the two do not overlap.

Debugging: why a name does not resolve

bash
resolvectl status                              # which upstreams on which link
resolvectl query --cache=no example.com        # bypass the cache
journalctl -u systemd-resolved -f              # stream the resolver logs
sudo resolvectl log-level debug                # verbose logging
ss -ulnp 'sport = :53'                         # who actually listens on 53
dig @127.0.0.53 example.com                    # hit the stub directly

Common problems:

  • Empty DNS Servers in resolvectl status: NetworkManager did not hand any over. Check nmcli dev show | grep DNS.
  • It resolves through dig but not through the application: the application does not use libc. It reads /etc/resolv.conf itself and sees 127.0.0.53, but the glibc cache is empty. Restart the application.
  • VPN broke DNS: the VPN client overwrote /etc/resolv.conf on top of the symlink. Use resolvectl domain wg0 '~.' instead of swapping the file out.

§ команды

bash
resolvectl status

The main overview: which DNS servers, on which links, which protocols are active

bash
resolvectl query example.com

Resolve through resolved (shows the cache hit, link, and time), more precise than dig

bash
sudo resolvectl flush-caches

Drop the cache, after switching upstream or for a test

bash
resolvectl domain wg0 '~corp.local'

Split-DNS: only corp.local queries go through the VPN interface

bash
journalctl -u systemd-resolved -f

Live resolver logs, to see DNSSEC failures, timeouts, and upstreams that dropped out

§ см. также

  • systemdsystemd: the init system and service managersystemd is the Linux init system: PID 1 that starts everything else, tracks dependencies, restarts what crashes, and collects the logs.
  • dns-resolutionDNS: ResolutionName-to-IP resolution goes through NSS: first `/etc/hosts`, then DNS via `/etc/resolv.conf`. The order is set in `/etc/nsswitch.conf`.
  • bind-dns-serverBIND: Authoritative and Caching DNS ServerBIND (Berkeley Internet Name Domain) is the most widely deployed DNS server on Linux. The daemon is `named`, the config is `/etc/named.conf` or `/etc/bind/named.conf`, and control goes through `rndc`.
  • cmd-journalctljournalctl: systemd journal`journalctl` reads the binary journal written by systemd-journald. It is the central log for the system: kernel, systemd services, syslog, all through one interface.
  • chrony-and-ntpchrony and NTP: clock synchronizationNTP is the clock synchronization protocol (about millisecond accuracy over the internet). On modern Linux the implementation is `chronyd` (default) or `systemd-timesyncd` (lightweight).
Footer
linuxlab-
Copyright © 2026 LinuxLab. All rights reserved.
Tutorials
Pricing
About
Privacy & cookies