Configure and Check systemd-resolved Without Losing DNS

Edit /etc/resolv.conf directly on a system running systemd-resolved and your change vanishes at the next boot: that file is generated. This guide identifies the resolver actually handling lookups, sets explicit DNS servers in a local drop-in, and proves the running service is using the result. Allow about 10 minutes. The examples target the systemd 255 behaviour documented by the Ubuntu package systemd-resolved 255.4-1ubuntu8.17.

Before you start

You need a shell, an active network connection, and sudo access. Use two DNS server addresses supplied by your network administrator. The addresses in the examples are documentation placeholders, not servers you should copy blindly. Expect a brief interruption to name resolution when the service is restarted.

These instructions change only a file under /etc/systemd/resolved.conf.d/. Do not edit /run/systemd/resolve/stub-resolv.conf: systemd-resolved maintains that file. The service normally exposes its full resolver at 127.0.0.53, and the recommended compatibility arrangement is for /etc/resolv.conf to point to that generated stub file.

1. Check the current path

Start with read-only checks. They show whether the service is running, which interfaces have DNS scopes, and whether local applications are directed at the stub.

systemctl is-enabled systemd-resolved.service
systemctl is-active systemd-resolved.service
resolvectl status
readlink -f /etc/resolv.conf
resolvectl query example.org

A healthy result includes active, a Global or link-specific DNS scope, and a successful answer for example.org. The resolved path is commonly /run/systemd/resolve/stub-resolv.conf. If the service is inactive, fix that separately before changing configuration:

sudo systemctl start systemd-resolved.service

Checkpoint: Do not continue until you can tell which command currently supplies DNS servers. DHCP, per-link network configuration, resolvectl, and /etc/resolv.conf can all contribute settings.

2. Add a local DNS drop-in

Drop-ins are easier to audit and survive package updates better than edits to a vendor file. Files in /etc/systemd/resolved.conf.d/ take precedence over vendor configuration, and are applied in lexicographic filename order. Create one clearly named file with a late-enough prefix:

sudo install -d -m 0755 /etc/systemd/resolved.conf.d
sudo tee /etc/systemd/resolved.conf.d/90-local-dns.conf >/dev/null <<'EOF'
[Resolve]
DNS=192.0.2.53 2001:db8::53
Domains=~.
DNSSEC=allow-downgrade
DNSOverTLS=no
EOF

Replace both documentation addresses. DNS= sets global servers; per-link servers learned from network configuration can still participate. Domains=~. is a route-only root domain: it prefers these global servers for names that do not match a more specific routing domain. It is not a search suffix, so it does not turn printer into printer.example.

DNSSEC=allow-downgrade attempts validation but can fall back when the upstream cannot support it. Set DNSSEC=yes only when you know the configured servers support DNSSEC correctly and the host receives regular trust-anchor updates. DNSOverTLS=no makes the transport choice explicit; opportunistic TLS does not authenticate the server and strict TLS will make all lookups fail if the server or certificate is unsuitable.

The setting DNS= accepts IPv4 or IPv6 addresses, with optional ports, interface names, and certificate names. For a DNS-over-TLS server whose certificate name is not its address, the documented form is like 203.0.113.53#dns.example. Do not add that suffix unless you are also configuring and verifying TLS.

3. Apply the file and inspect the live configuration

Restarting the service is a privileged, service-disrupting action. Save the file first, and keep another shell available if this is a remote machine.

sudo systemctl restart systemd-resolved.service
systemctl is-active systemd-resolved.service
resolvectl status

The status output should show the service as active and list the replacement servers. It may also show per-link servers: global DNS= does not erase information supplied by DHCP or another network manager. If the output still reflects an old configuration, check for a later drop-in with a conflicting option:

ls -la /usr/lib/systemd/resolved.conf.d /usr/local/lib/systemd/resolved.conf.d /etc/systemd/resolved.conf.d 2>/dev/null
grep -R --line-number --fixed-strings 'DNS=' /usr/lib/systemd/resolved.conf.d /usr/local/lib/systemd/resolved.conf.d /etc/systemd/resolved.conf.d 2>/dev/null

4. Test real lookups and routing

Use resolvectl query rather than assuming that a changed file proves the result. Test a public name, a local synthetic name, and a name in any private zone you deliberately configured.

resolvectl query example.org
resolvectl query localhost
resolvectl flush-caches
resolvectl query example.org

localhost is answered locally and does not prove that an upstream server works. The public query should return one or more addresses. Flushing caches is normally unnecessary because network changes flush them automatically, but the command is useful while testing. For a more detailed service-level view, inspect recent messages:

sudo journalctl -u systemd-resolved.service -b --no-pager -n 80

Remember that a single-label name is not normally sent to global unicast DNS. It needs a search domain, or the explicit ResolveUnicastSingleLabel=yes setting. Enabling that option can disclose internal-looking names to DNS servers outside your control, so prefer a correctly scoped search domain.

5. Recover if lookups fail

First distinguish a bad resolver configuration from a broken link:

systemctl is-active systemd-resolved.service
resolvectl status
resolvectl query localhost
resolvectl query example.org

If localhost works but the public query fails, check the server addresses, routing, firewall rules, DNSSEC mode, and any conflicting drop-ins. Strict DNSSEC can reject every answer from a server that does not support it correctly. Strict DNS-over-TLS can do the same when the certificate or server name is wrong.

To undo this guide's change, remove only the file created above and restart the service. This is destructive to that one configuration file, so confirm the path before pressing Enter:

sudo rm -- /etc/systemd/resolved.conf.d/90-local-dns.conf
sudo systemctl restart systemd-resolved.service
resolvectl status

After removal, the resolver returns to the remaining configuration sources. If the old settings do not return, inspect the drop-in directories and the network manager that supplies per-link DNS data. Do not replace /etc/resolv.conf with a hand-written file until you understand which component owns it.

Done means