Skip to main content
Debian DNS resolver stack showing resolv.conf management layers and systemd-resolved interaction

Debian DNS resolv.conf: Resolver Stack Behaviour and Configuration

The DNS resolver configuration on Debian-based systems has become one of those areas where a straightforward file — /etc/resolv.conf — sits at the intersection of multiple competing management layers, and editing it directly is almost always the wrong move even though it looks like the obvious one. This guide documents the full resolver stack behaviour on current Debian and Ubuntu systems: what manages resolv.conf, how systemd-resolved interacts with it, what resolvconf versus resolvectl actually do, the specific pitfalls that cause DNS to silently break after manual edits, and the diagnostic commands that reveal which resolver path your system is actually using. The observations come from configuring and troubleshooting DNS on Debian 11/12, Ubuntu 22.04/24.04, and derived distributions across bare metal, VM, and container environments. This guide is part of the how-to section and connects to the DNS SRV usage technical note and the Linux on Windows topic hub where WSL resolver behaviour introduces additional complexity.


The short version

→ Short Answer

On modern Debian and Ubuntu systems, /etc/resolv.conf is typically a symlink managed by systemd-resolved, resolvconf, or NetworkManager — not a static file you should edit directly. To change your DNS servers persistently, configure them in the service that manages the symlink: systemd-resolved via /etc/systemd/resolved.conf, NetworkManager via connection profiles, or Netplan via /etc/netplan/*.yaml. Direct edits to /etc/resolv.conf will be silently overwritten on the next network event — DHCP renewal, service restart, or reboot. To find out what is managing your resolver right now, check where the symlink points: ls -la /etc/resolv.conf.


What resolv.conf actually does

At the lowest level, /etc/resolv.conf tells the C library resolver — the getaddrinfo() and gethostbyname() functions that virtually every application uses — where to send DNS queries. The file format is simple: nameserver lines listing DNS server IP addresses, optionally search and domain directives for domain suffix handling, and options for resolver behaviour flags.

Classic resolv.conf content
nameserver 192.168.1.1
nameserver 8.8.8.8
search home.lan
options timeout:2 attempts:3

This file has existed in essentially the same format since 4.3BSD in the 1980s. The format is not the problem. The problem is that on modern Debian systems, multiple services want to write to this file, and the interactions between them create the confusion.


The management layers

On a current Debian 12 or Ubuntu 24.04 installation, the resolver stack typically involves three or four layers between your configuration intent and the actual /etc/resolv.conf content:

systemd-resolved

systemd-resolved is a system service that provides DNS resolution, caching, and DNSSEC validation. When active, it listens on 127.0.0.53:53 as a local DNS stub resolver. Applications send queries to this local address, and systemd-resolved forwards them to the actual upstream DNS servers configured elsewhere.

Check if systemd-resolved is running
systemctl status systemd-resolved

When systemd-resolved manages the resolver, /etc/resolv.conf is a symlink to one of several possible targets:

  • /run/systemd/resolve/stub-resolv.conf — the stub resolver configuration, containing nameserver 127.0.0.53. This is the recommended and default mode. Applications talk to the local stub, which handles caching and forwarding.
  • /run/systemd/resolve/resolv.conf — the "traditional" mode, containing the actual upstream DNS server addresses. Applications bypass the stub resolver and talk to upstream servers directly. No local caching.
  • Something else entirely — if someone or something has replaced the symlink with a static file or pointed it elsewhere.
⬡ Observed Behaviour

On a fresh Debian 12 installation with the default desktop environment, /etc/resolv.conf is a symlink to /run/systemd/resolve/stub-resolv.conf. The file contains nameserver 127.0.0.53 and a comment warning not to edit it. Upstream DNS servers are configured via DHCP and stored in systemd-resolved's internal state. On a fresh Debian 12 minimal server installation, systemd-resolved may not be installed at all, and /etc/resolv.conf is a static file written by the DHCP client or the installer.

NetworkManager

NetworkManager manages network connections on most Debian desktop installations and many server configurations. When it establishes a connection — wired, wireless, or VPN — it receives DNS server addresses from DHCP or static configuration and needs to make them available to the system resolver.

NetworkManager can push DNS information to systemd-resolved (the default on modern setups), write to /etc/resolv.conf directly, or use the resolvconf framework. The method depends on the dns setting in /etc/NetworkManager/NetworkManager.conf:

Check NetworkManager DNS mode
grep -i dns /etc/NetworkManager/NetworkManager.conf

Common values: dns=systemd-resolved (push to resolved), dns=default (write resolv.conf directly), dns=none (do nothing — someone else manages DNS).

resolvconf / openresolv

resolvconf (or openresolv on some systems) is a framework that mediates between multiple DNS information sources — DHCP clients, VPN clients, manual configuration — and produces a merged /etc/resolv.conf. It predates systemd-resolved and serves a similar coordination role but without the local caching resolver.

On systems where both resolvconf and systemd-resolved are installed, there is potential for conflict. Modern Debian resolves this by having systemd-resolved provide a resolvconf-compatible interface, so services that push DNS information via the resolvconf command end up talking to systemd-resolved rather than the legacy framework.

Diagram showing resolver management layers on Debian: NetworkManager, systemd-resolved, resolvconf, and resolv.conf relationships

Determining what manages your resolver

Before making any DNS configuration changes, establish which management layer is active on your system. The diagnostic sequence:

Step 1 — check the symlink
ls -la /etc/resolv.conf

If the output shows a symlink to /run/systemd/resolve/stub-resolv.conf, systemd-resolved is in stub mode — the standard configuration. If it points to /run/systemd/resolve/resolv.conf, resolved is active but in traditional mode. If it is a regular file (no symlink arrow), something has replaced the managed symlink with a static file.

Step 2 — check what the system is actually using
resolvectl status

This shows the per-interface DNS configuration as systemd-resolved sees it: which DNS servers are configured for each network interface, whether DNSSEC is active, and the current DNS-over-TLS mode. If resolvectl is not available, systemd-resolved is either not installed or not active.

Step 3 — verify actual DNS resolution path
# Which nameserver actually answers?
dig +short example.com @127.0.0.53
dig +short example.com @$(grep nameserver /etc/resolv.conf | head -1 | awk '{print $2}')

If both commands return the same result, the resolution path is working regardless of the management layer. If the first fails but the second succeeds (or vice versa), the resolver configuration is inconsistent.

⚠ Common Pitfall

Editing /etc/resolv.conf directly on a system where it is a symlink managed by systemd-resolved produces one of two outcomes: your edit is silently overwritten within seconds (because systemd-resolved regenerates the file on any network event), or you break the symlink by creating a new regular file in its place, which causes systemd-resolved to stop managing DNS configuration entirely — the system uses your static file until the next network reconfiguration, at which point behaviour becomes unpredictable. Neither outcome is what you intended.


Configuring DNS servers correctly

The correct configuration method depends on which layer manages your resolver.

With systemd-resolved

Edit the systemd-resolved configuration, not resolv.conf:

Configure upstream DNS in systemd-resolved
sudo nano /etc/systemd/resolved.conf

Set the [Resolve] section:

/etc/systemd/resolved.conf
[Resolve]
DNS=9.9.9.9 149.112.112.112
FallbackDNS=1.1.1.1 8.8.8.8
DNSSEC=allow-downgrade
DNSOverTLS=opportunistic

Then restart the service:

Apply the configuration
sudo systemctl restart systemd-resolved
resolvectl status

The resolvectl status output should now show your configured DNS servers. The /etc/resolv.conf symlink continues to point at the stub resolver (127.0.0.53), and systemd-resolved forwards queries to your specified upstream servers.

With NetworkManager

For systems where NetworkManager manages DNS, configure it per-connection:

Set DNS via nmcli
nmcli connection modify "Wired connection 1" ipv4.dns "9.9.9.9 149.112.112.112"
nmcli connection modify "Wired connection 1" ipv4.ignore-auto-dns yes
nmcli connection up "Wired connection 1"

The ipv4.ignore-auto-dns yes prevents DHCP-provided DNS servers from overriding your manual configuration. Without it, the DHCP-provided servers are prepended to your list on the next renewal.

With Netplan (Ubuntu)

Ubuntu server installations commonly use Netplan, which generates configuration for either NetworkManager or systemd-networkd:

/etc/netplan/01-netcfg.yaml
network:
version: 2
ethernets:
eth0:
dhcp4: true
nameservers:
addresses:
- 9.9.9.9
- 149.112.112.112
search:
- home.lan
Apply Netplan configuration
sudo netplan apply

For more detail on the Debian project's documentation of resolver management, the Debian wiki resolv.conf page covers the distribution-specific defaults and integration points.

Terminal output showing resolvectl status with per-interface DNS configuration and DNSSEC status

What changed across Debian releases

The resolver management approach has shifted significantly between Debian versions, and guides written for older releases give advice that actively breaks newer systems.

Then

Debian 9 (Stretch) and earlier: /etc/resolv.conf was typically a static file written by the DHCP client (dhclient) or manually configured. Editing it directly was the standard approach. systemd-resolved existed but was not installed or enabled by default. The resolvconf package, if installed, mediated between DHCP and VPN clients but was optional. DNS configuration was straightforward: edit the file, or configure your DHCP client to write the values you wanted.

Now

Debian 11 (Bullseye) and later: systemd-resolved is installed and active by default on desktop installations. /etc/resolv.conf is a managed symlink. Direct edits are overwritten. The correct configuration path goes through /etc/systemd/resolved.conf, NetworkManager connection profiles, or Netplan. The resolvconf command, if present, is a compatibility shim that talks to systemd-resolved rather than managing the file independently. Server minimal installations may still use a static file, but the trend is toward systemd-resolved everywhere.

Ubuntu's parallel evolution

Ubuntu adopted systemd-resolved earlier and more aggressively than Debian. Since Ubuntu 18.04, the stub resolver at 127.0.0.53 has been the default, and /etc/resolv.conf has been a symlink. This means Ubuntu-specific advice written after 2018 generally applies to Debian 11+, but Debian-specific advice written before 2021 may assume a static file that no longer exists on current installations.

↻ What Changed

The most disruptive change in the resolver stack was not technical but social: decades of Unix convention taught administrators that /etc/resolv.conf is the file you edit to change DNS servers. That convention held from the 1980s through the 2010s across every Unix and Linux distribution. The shift to a managed symlink that silently reverts manual edits broke muscle memory and invalidated the most commonly given DNS troubleshooting advice on the internet. Searching for "change DNS on Debian" still surfaces guides that instruct you to edit /etc/resolv.conf directly — advice that is correct for systems older than about 2020 and wrong for current systems.


Diagnostic commands

When DNS resolution fails or behaves unexpectedly, these commands reveal the actual resolver state:

What resolv.conf currently contains
cat /etc/resolv.conf
What systemd-resolved thinks the DNS config is
resolvectl status
resolvectl dns
Test resolution through the stub resolver
dig example.com @127.0.0.53
Test resolution bypassing the stub
dig example.com @9.9.9.9
Check which process is listening on port 53
sudo ss -tlnp | grep :53

If ss shows systemd-resolved listening on 127.0.0.53:53, the stub resolver is active. If nothing is listening on port 53, the system is resolving directly to upstream servers without a local resolver. If dnsmasq or unbound is listening, a third-party resolver is in the path.

⬡ Observed Behaviour

On systems where DNS resolution fails intermittently, the most common root cause is a race condition between systemd-resolved and NetworkManager during network transitions — Wi-Fi roaming, VPN connect/disconnect, DHCP renewal. The symptom is that resolvectl status shows the correct DNS servers, dig @127.0.0.53 fails with a timeout, but dig @<upstream-server> succeeds immediately. The fix is to restart systemd-resolved: sudo systemctl restart systemd-resolved. If this pattern recurs frequently, it indicates a timing issue in the NetworkManager-to-resolved handoff that may be addressed by pinning DNS configuration in /etc/systemd/resolved.conf rather than relying on per-interface DHCP-provided servers.


Container and WSL considerations

Container environments and WSL instances add another layer of resolver complexity.

Docker containers

Docker containers on Debian hosts inherit the host's DNS configuration by default, but the mechanism depends on the Docker network mode. In the default bridge mode, Docker generates a container-local /etc/resolv.conf based on the host's resolver configuration at container creation time. If the host uses the systemd-resolved stub at 127.0.0.53, Docker detects this and substitutes the upstream DNS servers instead — because 127.0.0.53 inside the container would try to reach a stub resolver that does not exist in the container's network namespace.

⚠ Common Pitfall

If Docker containers on a Debian host cannot resolve DNS, and the host uses systemd-resolved, verify that Docker is correctly extracting the upstream servers. Run docker run --rm alpine cat /etc/resolv.conf and check the nameserver entries. If the container shows nameserver 127.0.0.53, Docker's detection has failed and the container is trying to reach a stub resolver that only exists on the host. Fix by configuring Docker's DNS explicitly in /etc/docker/daemon.json: {"dns": ["9.9.9.9", "149.112.112.112"]}.

WSL instances

WSL 2 generates /etc/resolv.conf automatically from the Windows host's DNS configuration. The generated file typically contains the Windows host's virtual network adapter IP as the nameserver. This works for most cases but breaks when VPN software on the Windows side changes the DNS configuration without WSL's knowledge.

⚙ Compatibility Note

WSL's automatic /etc/resolv.conf generation can be disabled by adding [network] generateResolvConf = false to /etc/wsl.conf inside the distribution. If you disable it, you must create and maintain /etc/resolv.conf manually — which brings you back to the static-file model that is actually appropriate here, since WSL does not run systemd-resolved by default and does not have the management layer conflicts that native Debian installations face. The Linux on Windows topic hub covers WSL-specific networking in broader context.


Common failure patterns and fixes

DNS works for some domains but not others

This usually indicates a DNSSEC validation failure. systemd-resolved with DNSSEC=yes will refuse to return results for domains with broken DNSSEC chains. Set DNSSEC=allow-downgrade in /etc/systemd/resolved.conf to permit fallback to unauthenticated resolution when DNSSEC validation fails.

DNS stops working after VPN connects

VPN clients often push their own DNS servers and search domains. If the VPN DNS servers are only reachable through the VPN tunnel, and the system tries to use them for all DNS queries (not just the VPN's split-tunnel domains), resolution fails for everything outside the VPN's domain scope. The fix is per-interface DNS routing in systemd-resolved:

Route specific domains through VPN DNS
resolvectl dns tun0 10.0.0.1
resolvectl domain tun0 "~corp.example.com"

The ~ prefix tells systemd-resolved to use the VPN's DNS server only for queries within corp.example.com, and the default DNS servers for everything else.

resolv.conf is empty or contains only comments

This happens when the symlink target is regenerated but the management service has no DNS servers configured. Check resolvectl status — if no DNS servers are shown for any interface, the problem is upstream: DHCP is not providing servers, or the static configuration is empty. Fix the source, not the symlink.

Changes to resolved.conf have no effect

The most common cause is forgetting to restart the service. systemd-resolved reads its configuration at startup and does not monitor the file for changes:

Restart after configuration changes
sudo systemctl restart systemd-resolved

If the restart does not apply the changes, verify the configuration syntax. systemd-resolved silently ignores malformed lines — a typo in a DNS server address will not produce an error, it will simply be skipped.


Practical recommendations

For Debian 12 and Ubuntu 24.04 systems, the cleanest resolver configuration is:

  1. Let systemd-resolved manage /etc/resolv.conf as a stub symlink
  2. Configure your preferred upstream DNS servers in /etc/systemd/resolved.conf
  3. Use resolvectl for diagnostics, not cat /etc/resolv.conf
  4. Set DNSSEC=allow-downgrade unless you have a specific reason to enforce strict validation
  5. Use per-interface DNS routing for VPN and split-tunnel configurations

For server installations where systemd-resolved is not installed and you want a static file, that is a valid configuration — but commit to it. Do not install systemd-resolved later and expect the static file to survive.

For the DNS SRV record usage patterns that frequently arise alongside resolver configuration, the DNS SRV usage technical note covers the application-layer DNS features that depend on the resolver stack working correctly.