Configuring systemd-resolved: DNS Caching and Split DNS on Linux
AI generated
$
/etc
Linux
Configuring systemd-resolved
DNS caching and split DNS on Linux

On most current Linux distributions, DNS resolution today runs through systemd-resolved by default instead of directly through kernel or glibc anchored name resolution. For admins juggling internal VPN networks, Docker containers and the public internet, understanding this stub resolver is no longer optional, it is a prerequisite for DNS resolution to work reliably at all in mixed network environments.

10 min read Linux DNS Networking

1. How systemd-resolved works as a local stub resolver

systemd-resolved is a standalone system service acting as a local DNS stub resolver, listening on the loopback address 127.0.0.53. Applications on the system no longer send DNS queries directly to an external nameserver, but to this local stub, which accepts the query, answers it from cache if possible, and otherwise forwards it to the configured upstream resolver.

This design brings two decisive advantages: first, answers are cached system wide, so repeated queries for the same domain do not have to travel over the network again. Second, systemd-resolved can use different resolvers, and even different domains, per network interface, something a single global /etc/resolv.conf simply could not represent in the classic architecture.

2. Basic configuration through resolved.conf

The global configuration lives in /etc/systemd/resolved.conf, complemented by drop in files in /etc/systemd/resolved.conf.d/, which allow a cleaner separation between distribution defaults and your own customizations. The most important directives are DNS= for the primary resolvers, FallbackDNS= for resolvers that only kick in when none others are configured, and Domains= for the search list used with unqualified hostnames.

The DNSSEC= setting controls whether answers are cryptographically validated against tampering, while Cache= enables or disables system wide caching. In most production server environments, DNSSEC=no remains the pragmatic default, because many internal zones do not deliver valid DNSSEC signatures, and an overly strict mode would otherwise block legitimate internal resolution.


# /etc/systemd/resolved.conf.d/custom.conf
[Resolve]
DNS=1.1.1.1 9.9.9.9
FallbackDNS=8.8.8.8
Domains=~.
DNSSEC=no
Cache=yes
DNSStubListener=yes

3. Setting up split DNS for VPN scenarios

Split DNS means certain domains are resolved through a specific resolver, while all other queries take the regular path. That is typical for VPN connections: internal domains such as internal.mironsoft.local should go through the company internal DNS server, while resolution of public domains should still use the regular resolver instead of being routed unnecessarily through the VPN tunnel.

systemd-resolved implements this through so called routing domains, prefixed with a tilde. A domain entry like ~internal.mironsoft.local on a specific network interface ensures that only queries for that zone are routed through that interface's associated DNS servers, while the global search list stays untouched.


# Set split DNS for the VPN interface tun0
resolvectl dns tun0 10.8.0.1
resolvectl domain tun0 "~internal.mironsoft.local"

# Check the current mapping of domains to interfaces
resolvectl domain

4. Debugging with resolvectl

The command line tool resolvectl is the central place for everything related to systemd-resolved. resolvectl status shows the currently used DNS servers per interface, the configured domains and the active DNSSEC mode, which immediately reveals which setting is actually in effect when configuration comes from multiple conflicting sources, such as NetworkManager and a static resolved.conf.

For targeted troubleshooting, resolvectl query resolves a specific hostname and also shows which interface and resolver produced the answer, while resolvectl statistics reports cache hit ratio and transaction counts. For persistent problems, resolvectl flush-caches removes stale or faulty cached entries in a targeted way, without restarting the service entirely.


# Show the full DNS status for every interface
resolvectl status

# Targeted resolution showing which source answered
resolvectl query shop.mironsoft.de

# Flush the cache without restarting the service
resolvectl flush-caches

5. Interaction and conflicts with NetworkManager

NetworkManager can manage DNS settings in two fundamentally different ways: in dns=systemd-resolved mode, NetworkManager passes DNS servers obtained via DHCP or VPN straight through to systemd-resolved, which then handles all resolution. In dns=default mode, NetworkManager instead writes directly to /etc/resolv.conf, bypassing systemd-resolved entirely and simply losing split DNS functionality.

The most common configuration mistake happens when both services try to manage /etc/resolv.conf at the same time. This shows up as seemingly random DNS behavior changing after every connection switch, because depending on service ordering, different configurations win. The reliable fix is to pick exactly one manager and pin it explicitly in /etc/NetworkManager/NetworkManager.conf.


# /etc/NetworkManager/conf.d/dns.conf
[main]
dns=systemd-resolved

systemd-resolved provides several variants of resolv.conf under /run/systemd/resolve/. stub-resolv.conf points to the local stub resolver at 127.0.0.53 and is the recommended default, while resolv.conf directly lists the actual configured upstream servers, bypassing the local cache and split DNS logic.

A common mistake is a manually created, static /etc/resolv.conf that overwrites the expected symlink. Resolution keeps working at first, but any split DNS configuration and the local cache are completely bypassed, without that being obvious at first glance. A quick ls -la /etc/resolv.conf immediately shows whether the expected symlink is still in place.


# Check whether /etc/resolv.conf still correctly points to systemd-resolved
ls -la /etc/resolv.conf

# Reset the symlink to the recommended stub resolver
ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf

7. DNSSEC validation and DNS over TLS

Beyond classic name resolution, systemd-resolved supports both DNSSEC validation and DNS over TLS for encrypted transport to the upstream resolver. The setting DNSOverTLS=opportunistic first tries an encrypted connection and automatically falls back to unencrypted DNS if the resolver lacks support, striking a sensible balance between compatibility and security.

The strict mode DNSOverTLS=yes instead refuses any unencrypted connection, which can lead to complete resolution failures in environments where an individual resolver's DNS over TLS support is unreliable. For production servers it is therefore recommended to use the opportunistic mode, or to deliberately disable DNS over TLS entirely, rather than risking unexpected outages in strict mode.


[Resolve]
DNSOverTLS=opportunistic
DNSSEC=allow-downgrade

8. Understanding multiple DNS servers and fallback behavior

When several servers are listed under DNS=, systemd-resolved does not stubbornly stick to the first entry by default, but continuously tests which of the configured servers actually answers most reliably and fastest, then prefers that one for subsequent queries. This so called feature level probing also remembers whether a server supports certain capabilities such as EDNS0, to avoid unnecessary fallback attempts on every single query.

If the currently preferred server stops answering for an extended period, systemd-resolved automatically switches to the next configured server in the list, without requiring manual intervention. For production setups it is therefore recommended to list at least two independent resolvers under DNS=, so that a single failed server does not block the system's entire name resolution.


# Show the currently preferred server per interface
resolvectl status | grep -A2 "Current DNS Server"

9. Troubleshooting common problems

A classic symptom is resolution timeouts even though resolvectl status shows correct resolvers. In many cases the cause is a firewall blocking outbound queries on port 53, or a VPN client that activates the network interface but never provides valid DNS servers via DHCP or its own configuration channel.

Another common problem involves Docker containers: if the host runs systemd-resolved, /etc/resolv.conf inside the container points to 127.0.0.53, an address unreachable from within the isolated container network. Docker normally handles this automatically by inserting the actual upstream servers from /run/systemd/resolve/resolv.conf when the container is created, which should be explicitly verified for custom network setups using network_mode: host.

File Points To Uses Local Cache Recommended For
stub-resolv.conf 127.0.0.53, local stub Yes Standard setup with full feature set
resolv.conf (resolved) Actual upstream servers No Applications without systemd-resolved support
Static /etc/resolv.conf Manually entered servers No Not recommended, bypasses split DNS
uplink-resolv.conf Upstream servers for other resolved instances No Containers running their own systemd-resolved

Mironsoft

Server administration, Docker hosts, and performance tuning

Linux servers nobody on the team really understands anymore?

We handle setup, hardening, and performance tuning of Linux servers and Docker hosts for Magento deployments, documented and traceable instead of grown and unclear.

Server Audit

Review the existing server configuration for security gaps and performance bottlenecks.

Docker Host Setup

Set up and secure production-ready Docker environments for Magento cleanly.

Monitoring & Tuning

Measure resource usage and tune systemd, kernel, and services with purpose.

10. Summary

systemd-resolved

Stub address

127.0.0.53, local DNS cache per system

Core command

resolvectl status, query and flush-caches

Split DNS

Routing domains with tilde prefix per interface

Most common mistake

Static resolv.conf overwrites the expected symlink

11. FAQ: systemd-resolved

1What is the difference between stub-resolv.conf and resolv.conf?
stub-resolv.conf points to the local cache at 127.0.0.53 and is the recommended default. resolv.conf lists the actual upstream servers directly, so the local cache and split DNS logic are not used.
2Why does DNS not work the same way inside a Docker container as on the host?
If the host uses systemd-resolved, 127.0.0.53 is only reachable on the host, not inside the isolated container network. Docker therefore automatically inserts the actual upstream servers, which should be explicitly checked for custom network setups.
3How do I set up split DNS for a VPN connection?
Using resolvectl domain, a routing domain with a tilde prefix is set on the VPN interface, such as ~internal.example.com. Only queries for that zone then travel through the VPN DNS server, while all other queries keep taking the regular path.
4How do I flush the DNS cache of systemd-resolved?
The resolvectl flush-caches command removes all cached entries without restarting the service. This is especially helpful for stale entries after DNS changes, when a domain briefly still returns the old answer.
5Why should DNSSEC often stay disabled on servers?
Many internal zones and some public domains do not deliver valid DNSSEC signatures. A strictly enabled DNSSEC mode can therefore block legitimate resolution, which is why DNSSEC=no or allow-downgrade is often the more pragmatic choice in mixed environments.
6How do I tell whether NetworkManager or systemd-resolved controls the DNS configuration?
Running ls -la on /etc/resolv.conf shows whether it is the expected symlink to stub-resolv.conf. If the file instead shows static content, NetworkManager in dns=default mode has likely overwritten the symlink.
7What does DNSOverTLS=opportunistic do compared to DNSOverTLS=yes?
Opportunistic mode tries an encrypted connection and automatically falls back to unencrypted DNS if the resolver lacks support. Strict mode refuses unencrypted connections entirely, which can cause outages with incompatible resolvers.
8Can I set different DNS servers per network interface?
Yes, that is one of the core advantages of systemd-resolved. Using resolvectl dns, custom resolvers can be set for each interface, independent of the globally configured default servers.
9Where do I find systemd-resolved logs when troubleshooting?
The service logs through the systemd journal, accessible with journalctl -u systemd-resolved. For more detailed output, the log level can be temporarily raised with systemctl service-log-level systemd-resolved debug.
10Do I need to install resolvectl separately?
No, resolvectl is part of the systemd package and already present on any system with systemd-resolved active. It replaces older, distribution specific tools such as systemd-resolve, which on newer versions is redirected as an alias to resolvectl.