Configure systemd-resolved Safely with a Drop-in File

A stray systemd-resolved restart can wipe out DNS settings you set by hand, so put them in a drop-in file instead. You will create a local systemd-resolved drop-in, set explicit DNS servers, and confirm which configuration the resolver is using. The same workflow applies to search domains, DNSSEC, DNS-over-TLS, caching and the local stub listener. Allow about 10 minutes, including a connectivity check.

Before you change anything

This guide matches the installed systemd-resolved package, version 255.4-1ubuntu8.17, whose manpage identifies the format as systemd 255. You need root access and an active systemd-resolved service. The examples use documentation addresses, so replace them with DNS servers supplied by your network administrator or provider.

First record the current state. This command is ordinary, read-only work:

resolvectl status

Look for the current DNS servers, per-link settings and the resolv.conf mode. Save the output if you may need to compare or recover later.

Checkpoint 1: create the local drop-in

Use a file below /etc/systemd/resolved.conf.d/. Files in this directory override the main configuration and are read in lexicographic filename order. A two-digit prefix makes the intended priority visible. Creating this file needs elevated privileges:

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

The addresses above are reserved documentation addresses and will not resolve names. Substitute real addresses before applying this to a working machine. The two entries in Domains= demonstrate different jobs: example.internal is a search suffix for single-label names, while ~example.internal is a route-only domain for directing matching queries to suitable per-link DNS servers. A route-only domain has no effect unless per-link DNS servers are known.

DNSSEC=allow-downgrade attempts validation but can disable it when the server cannot support it. Use DNSSEC=yes only when your DNS service supports DNSSEC correctly and your trust-anchor and system updates are maintained. DNSOverTLS=no is explicit here, not a claim that ordinary DNS is private. Setting it to opportunistic permits fallback and does not authenticate the server; setting it to yes requires a DNS-over-TLS server with a valid certificate. If you use encrypted DNS, specify a server name in DNS=, such as 192.0.2.53#dns.example, when that name is the certificate identity.

Checkpoint 2: inspect the merged configuration

Before restarting anything, ask systemd to display the main file and drop-ins as one configuration. This is read-only:

systemd-analyze cat-config systemd/resolved.conf

Confirm that your file appears under /etc/systemd/resolved.conf.d/ and that the final value for each single-value option is the one you expect. Drop-ins are sorted by filename across their configuration directories. For a single-value option, the last assignment wins. List-valued options can accumulate entries, so do not assume that adding a second file removes an earlier list.

A vendor drop-in in /usr/lib/systemd/resolved.conf.d/ can still affect the result. The local /etc directory has higher precedence. To disable one vendor file, the manpage recommends a same-named symlink to /dev/null in the local directory. Do that only after identifying the exact filename, because it is a deliberate package override.

Checkpoint 3: apply and verify

systemd-resolved cannot reload this configuration in place on the installed system, so restarting it is service-disrupting. Existing lookups may briefly fail. Schedule this for a suitable moment and keep a second shell or console available:

sudo systemctl restart systemd-resolved
resolvectl status

Check that the global and link sections show the expected protocols and DNS servers. Then test a name that should be available through the configured service:

resolvectl query example.com

The command should return an answer rather than a resolver error. A successful query alone does not prove DNSSEC validation or DNS-over-TLS authentication. Use resolvectl status to check the resolver's reported protocol state, and test an internal name separately if you configured a search or route-only domain.

Common traps and recovery

An empty DNS= setting does not necessarily mean that no DNS servers are used. When it is not specified, resolved can use servers in /etc/resolv.conf; per-link servers supplied by network configuration and runtime applications also matter. FallbackDNS= is only used when no other DNS information is known, and an omitted value uses a compiled-in list.

Do not enable ResolveUnicastSingleLabel=yes casually. It forwards single-label names to global DNS servers even without search domains, which can disclose internal-looking names to servers outside your control. Also remember that LLMNR= and MulticastDNS= require the corresponding per-link setting as well as the global setting.

If name resolution breaks, undo the local file and restart the service. This removes only the file created in this guide:

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

The removal is reversible only if you kept the file contents, so copy the file to a protected note before editing it. Do not delete other files from the directory while troubleshooting.

Done means