DNS over TLS (DoT) using systemd-resolved

Introduction

Previously, I had been using my ISP’s default DNS server. After encountering a page blocked by CUII, I decided to move to an alternative DNS server without censoring. This made it necessary to setup DNS over TLS (DoT, RFC7858).

As it was not easily done using the traditional /etc/resolv.conf and /etc/network/interfaces style configuration I decided to take this as an opportunity to move to systemd-networkd and systemd-resolved. Initially, this broke my AES 67 audio setup such that I subsequently needed to re-enable Avahi Daemon instead of the mDNS solution integrated into systemd-networkd.

This post summarizes the individual steps for my own reference and for everyone who may be interested in it.

Alternative DNS Server Providers

The first and most difficult step is to select a suitable replacement DNS provider. I went with the DNS by Digitalcourage e.V., but there are also other privacy-respecting and uncensored DNS providers:

Provider IPv4 IPv6 Link
AdGuard 94.140.14.140 2a10:50c0::1:ff https://adguard-dns.io/de/public-dns.html
  94.140.14.141 2a10:50c0::2:ff  
Artikel 10 e.V. 217.197.91.153 2001:67c:1401:2120::1 https://dns.artikel10.org/
Digitalcourage e.V. 5.9.164.112 2a01:4f8:251:554::2 https://digitalcourage.de/support/zensurfreier-dns-server
Digitale Gesellschaft 185.95.218.42 2a05:fc84::42 https://www.digitale-gesellschaft.ch/dns/
  185.95.218.43 2a05:fc84::43  
dns.watch 84.200.69.80 2001:1608:10:25::1c04:b12f https://dns.watch/
  84.200.70.40 2001:1608:10:25::9249:d69b  
Foundation for Applied Privacy 146.255.56.98 2a02:1b8:10:234::2 https://applied-privacy.net/services/dns/
Freie Netze München e.V. 5.1.66.255 2001:678:e68:f000:: https://ffmuc.net/wiki/doku.php?id=knb:dohdot
  185.150.99.255 2001:678:ed0:f000::  
Mullvad 194.242.2.2 2a07:e340::2 https://mullvad.net/en/help/dns-over-https-and-dns-over-tls
Njalla 95.215.19.53 2001:67c:2354:2::53 https://dns.njal.la/
Restena Foundation 158.64.1.29 2001:a18:1::29 https://www.restena.lu/en/document/190-configuring-your-server-public-dns-resolver
Quad9 9.9.9.9 2620:fe::fe https://quad9.net/service/service-addresses-and-features/
  149.112.112.112 2620:fe::9  
UncensoredDNS 91.239.100.100 2001:67c:28a4:: https://blog.uncensoreddns.org/dns-servers/

I mostly created above table from the sources linked from section See Also/Free Uncensored DNS Servers. This is obviously Europe-centric. If you are in a different part of the world, consider checking for regional alternatives if you trust them more.

DNS over TLS

Standard DNS runs over UDP and DNS requests are issued by the libraries used by the applications i. e. there is not necessarily a central daemon that emits them. The information about which DNS server to consult is typically retrieved from /etc/resolv.conf.

In a standard configuration, the values to put there are obtained from a DHCP server and typically point to the router on the network which in turn forwards the DNS requests to the DNS server provided by the ISP.

When using a DNS server which accepts requests via UDP, changing the DNS server of the system is as simple as replacing the entry in resolv.conf, e. g. by Google’s famous DNS 8.8.8.8 or Cloudflare’s just as easy to remember 1.1.1.1.

However, this time I wanted to setup a future-proof privacy-respecting choice of DNS server and many of them newly require DNS over TLS (DoT, or alternatively DNS over HTTPS, aka. DoT) and do not allow the unencrypted access via UDP anymore.

This is of course, good for security, but not all applications are individually capable of contacting a DoT or DoH-based DNS server directly. Hence for this use case, it is helpful to run a local unencrypted DNS server that translates the locally incoming UDP-based cleartext requests to TCP-based and encrypted DoT.

Such a local DNS server can be provided by systemd-resolved.

systemd-resolved

There are certainly other alternatives, but in recent times I tend to prefer the solutions offered by the systemd-related tools as sensible default choices.

Often, they implement only a subset of what is technically possible but this is often sufficient to solve my kind of use cases. Also, I like that the INI config file format and introspection features (*ctl-series of commands) are somewhat similar across the systemd tools, making them easy to get started with once one knows how any of the other tools work.

This case here is only the second time that the out-of-the-box feature set of the systemd tool was not coverying my use case 100% hence I had to substitute the integrated systemd-resolved feature for mDNS with the proper avahi-daemon (cf. Section Recovering avahi-daemon further down).

The configuration for systemd-resolved is a straight-forward INI file:

# /etc/systemd/resolved.conf.d/20-digitalcourage-dot.conf
[Resolve]
DNS=5.9.164.112#dns3.digitalcourage.de 2a01:4f8:251:554::2#dns3.digitalcourage.de
DNSOverTLS=yes
Domains=~.
# disable multicast DNS because we _must_ use avahi-daemon for AES67!
MulticastDNS=no

The DNS servers to use are configured in line DNS and DoT is enabled by setting DNSOverTLS=yes.

If I understood it correctly, for systemd-resolved to run correctly, the network interface must be known by systemd-networkd. I used the opportunity to configure systemd-networkd for my primary network interface eno1 but it would possibly be enough to only have the [Match] and Name=eno1 lines if the interface was managed by /etc/network/interfaces.

# /etc/systemd/network/20-masysma-main.network
[Match]
Name=eno1

[Link]
RequiredForOnline=routable

[Network]
DHCP=ipv6
Address=192.168.1.16/24
Gateway=192.168.1.1

For my use of systemd-networkd, I removed the interface from /etc/network/interfaces, keeping only the loopback interface there:

# /etc/network/interfaces
auto lo
iface lo inet loopback

Ensure both of the required systemd services are running:

# systemctl enable --now systemd-networkd.service
# systemctl enable --now systemd-resolved.service

Then inspect the status of systemd-resolved with the resolvectl command:

# resolvectl
Global
         Protocols: +LLMNR +mDNS +DNSOverTLS DNSSEC=no/unsupported
  resolv.conf mode: stub
Current DNS Server: 5.9.164.112#dns3.digitalcourage.de
       DNS Servers: 5.9.164.112#dns3.digitalcourage.de
                    2a01:4f8:251:554::2#dns3.digitalcourage.de
        DNS Domain: ~.

Link 2 [...]

Link 5 (eno1)
    Current Scopes: DNS LLMNR/IPv4 LLMNR/IPv6
         Protocols: +DefaultRoute +LLMNR -mDNS +DNSOverTLS DNSSEC=no/unsupported
Current DNS Server: fe80::1
       DNS Servers: fe80::1
     Default Route: yes

Link 6 [...]

Note the -mDNS and +mDNS in the output: If you wanted to setup systemd-resolved with mDNS support but without avahi-daemon, you could set the variable MulticastDNS=yes in the network interface configuration and the systemd-resolved configuration to enable its integrated mDNS support.

Check that systemd-resolved works by itself:

# resolvectl query masysma.net
masysma.net: 2a01:239:241:bf00::1              -- link: eno1
             87.106.179.152                    -- link: eno1

-- Information acquired via protocol DNS in 936us.
-- Data is authenticated: no; Data was acquired via local or encrypted transport: yes
-- Data from: cache

Once it works OK there, replace /etc/resolv.conf by a configuration that points to the locally running server provided by systemd-resolved:

# mv /etc/resolv.conf /etc/resolv.conf.bak
# ln -s /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf

Check the content of the effective resolv.conf:

$ grep -vE '^#' /etc/resolv.conf

nameserver 127.0.0.53
options edns0 trust-ad
search .

Confirm that DNS works by running host:

$ host masysma.net
masysma.net has address 87.106.179.152
masysma.net has IPv6 address 2a01:239:241:bf00::1
masysma.net mail is handled by 5 smtpin.rzone.de. 

Recovering avahi-daemon

After initially setting up systemd-resolved as a replacement also for avahi-daemon, I noticed that the audio streams configured in aes67-linux-daemon cf. aes67_audio_notes(37) would no longer be received on the Audio Interface. This was due to the streams not being announced with mDNS anymore.

In general, it seems the aes67-linux-daemon requires avahi-daemon for this task. Hence I disabled the mDNS support in systemd-resolved and instead re-enabled the avahi-daemon.service.

After restarting aes67-linux-daemon the audio streams were again discovered by the interface.

The configuration in the previous section already accounts for this change. If you don’t run AES67 you might get away successfully with setting MulticastDNS=yes in /etc/systemd/resolved.conf.d and /etc/systemd/network and could then probably do without the separately running avahi-daemon.service

See Also

About CUII

Free Uncensored DNS Servers

DNS-over-TLS with systemd-resolved


Ma_Sys.ma Website 5 (1.0.2) – no Flash, no JavaScript, no Webfont, no Copy Protection, no Mobile First. No bullshit. No GUI needed. Works with any browser.

Created: 2026/01/01 19:36:19 | Revised: 2026/01/04 21:42:50 | Tags: kb, systemd, avahi, dot, dns, linux, aes67 | Version: 1.0.0 | SRC (Pandoc MD) | GPL

(c) 2026 Ma_Sys.ma info@masysma.net.

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.