Debian Local DNS: Deciding Between systemd-resolved and dnsmasq
A practical decision guide for Debian engineers choosing between systemd-resolved and dnsmasq for local DNS resolution, covering constraints, trade-offs, and validation.
06 Nov 2025, 13:04 UTC

The Local DNS Decision on Debian
On Debian, local DNS resolution is rarely a single-service decision. If you run containers, virtual machines, or custom internal hostnames, you need a resolver that answers for local zones and forwards everything else to an upstream provider. The choice usually narrows to systemd-resolved—bundled with the systemd stack—or dnsmasq, a lightweight standalone forwarder and DHCP server. Picking the wrong one can cause duplicate queries, broken NetworkManager integration, or applications that fail because /etc/resolv.conf is a symbolic link (a pointer file) to a stub file they do not recognize.
The practical takeaway: if you want tight integration with systemd and NetworkManager and minimal moving parts, start with systemd-resolved. If you need conditional forwarding, per-interface upstream servers, or DHCP alongside DNS, dnsmasq is usually the safer base.
Decision Constraints
Before comparing features, answer these constraints for your environment:
- Network management: Is NetworkManager controlling your interfaces? If so, it may overwrite
/etc/resolv.confunless configured to defer to another resolver. - Application expectations: Do any applications require
/etc/resolv.confto be a regular writable file rather than a symlink? - Zone complexity: Do you need to forward
.internalor.corpdomains to different upstream servers? - Service scope: Is this a desktop, a server, or a network gateway? DHCP requirements change the calculus.
- Validation needs: Do you require DNSSEC validation on client queries?
Supported Options Compared
Both packages are available in Debian repositories and maintained, but they solve different layers of the problem.
| Capability | systemd-resolved | dnsmasq |
|---|---|---|
| Packaging | Part of the systemd stack; enabled by default on many Debian installations | Standalone package; must be installed and enabled explicitly |
| Client configuration | /etc/resolv.conf symlinks to /run/systemd/resolve/stub-resolv.conf, listening on 127.0.0.53 | /etc/resolv.conf points to 127.0.0.1 or another configured listen-address |
| Caching | Yes, with per-link and global caches | Yes, in-memory cache |
| DNSSEC | Built-in validation support | Not built-in; requires upstream validation or external tooling |
| Conditional forwarding | Limited; per-link DNS and routing domains | Strong; server=/domain/upstream syntax in /etc/dnsmasq.d/*.conf |
| DHCP | No | Yes, integrated |
| NetworkManager | Native integration, but may require DNS management to be disabled to avoid conflicts | Coexists as a local DNS server; NetworkManager can point clients to it |
| Local hostnames | /etc/hosts and per-link configuration | /etc/hosts and /etc/dnsmasq.d/*.conf |
Trade-offs in Practice
systemd-resolved simplifies the client side. The stub resolver on 127.0.0.53—a minimal local listener that forwards queries to the full resolver service—means applications do not need to know which upstream DNS server is active. However, that same stub can break applications that hard-code /etc/resolv.conf or expect a traditional resolver interface. On Debian systems using NetworkManager, the resolver can also be bypassed if NetworkManager continues to write its own nameserver entries. Disabling NetworkManager’s DNS management leaves systemd-resolved in control.
dnsmasq gives you granular control. You can define per-interface upstreams, split DNS for internal domains, and serve DHCP from the same daemon. The cost is ownership: you manage /etc/resolv.conf directly, and a misconfigured listen-address or missing bind-interfaces can expose local hostnames to the wider network. Running dnsmasq and systemd-resolved simultaneously without isolation often produces duplicate queries and inconsistent answers because both attempt to bind port 53.
Coexistence is possible but fragile. A common pattern uses systemd-resolved for global resolution and dnsmasq only for local hostnames via /etc/hosts or drop-in files. This requires careful port management and is generally recommended only when you must migrate gradually.
Concrete Validation: Confirming systemd-resolved
If you decide to use systemd-resolved as the primary resolver, validate the integration before relying on it. Run these commands as root or via sudo.
1. Verify the service and boot state
systemctl is-enabled systemd-resolved
systemctl status systemd-resolved
Expected checks: The first command should print enabled. The second should show active (running). If the service is masked or disabled, enable and start it with systemctl enable --now systemd-resolved.
2. Confirm the resolv.conf symlink
ls -l /etc/resolv.conf
Expected checks: The output should show a symlink pointing to /run/systemd/resolve/stub-resolv.conf. If /etc/resolv.conf is a regular file, another service is managing DNS. Back it up before replacing it with the symlink.
3. Check DNSSEC and link state
resolvectl status
Expected checks: Look for DNSSEC enabled: yes on the global section or the relevant link. If DNSSEC is disabled, edit /etc/systemd/resolved.conf and set DNSSEC=yes, then restart the service.
4. Test resolution through the stub
dig @127.0.0.53 example.com
Expected checks: The response should contain an ANSWER SECTION with an IP address. If DNSSEC validation succeeds for a signed domain, the header may include the ad (Authenticated Data) flag. Absence of the ad flag does not always indicate failure; it depends on the domain’s DNSSEC status and your configuration.
5. Isolate NetworkManager
If NetworkManager is installed, check whether it is still writing to /etc/resolv.conf. On recent NetworkManager versions, disabling its DNS management is often done by setting dns=none in the [main] section of /etc/NetworkManager/NetworkManager.conf:
[main]
dns=none
Restart NetworkManager with systemctl restart NetworkManager. Risk: if systemd-resolved is not yet fully configured, you may temporarily lose DNS resolution. Validate step 4 immediately after the restart. To revert, remove that setting and restart NetworkManager; confirm systemd-resolved remains active before relying on the old behavior.
Limitations and a Practical Check
Neither option is universally superior. systemd-resolved’s stub mode may not be compatible with applications that hard-code /etc/resolv.conf or expect a traditional resolver interface. dnsmasq can inadvertently expose local hostnames if listen-address or bind-interfaces are misconfigured, and its default configuration may conflict with DHCP services if not carefully isolated.
After any change, run a single practical check to confirm the system is coherent:
ss -tulnp | grep ':53'
This lists processes listening on port 53. You should see exactly one resolver—either systemd-resolved bound to 127.0.0.53 or dnsmasq bound to its configured address. If both appear, stop one service before proceeding. Then resolve a known local name and an external name to confirm the split behavior matches your intent.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.