DNS over TLS (DoT) encrypts your DNS queries, making it much harder for anyone on the network to snoop on which websites you’re visiting.
Let’s see how this actually works. Imagine you’re running a small web server and want to ensure your DNS lookups are private. We’ll configure a local DNS resolver, unbound, to use DoT.
First, we need to install unbound if you don’t have it already:
sudo apt update && sudo apt install unbound
Now, let’s create a configuration file for unbound. We’ll call it /etc/unbound/unbound.conf.d/dot-resolver.conf.
server:
interface: 127.0.0.1
port: 53
access-control: 127.0.0.0/8 allow
do-ip4: yes
do-ip6: no
do-udp: yes
do-tcp: yes
root-hints: "/usr/share/dns/root.hints"
# Enable DNS over TLS
ssl-upstream: yes
ssl-port: 853
# Specify the upstream DoT servers
# For Google's DNS:
forward-zone:
name: "."
forward-addr: 8.8.8.8@853#dns.google
forward-addr: 8.8.4.4@853#dns.google
# For Cloudflare's DNS:
# forward-zone:
# name: "."
# forward-addr: 1.1.1.1@853#cloudflare-dns.com
# forward-addr: 1.0.0.1@853#cloudflare-dns.com
In this configuration:
interface: 127.0.0.1andport: 53tellunboundto listen for DNS requests on the local machine on the standard DNS port.access-control: 127.0.0.0/8 allowpermits requests from the local network.ssl-upstream: yesis the key directive that enables DoT.ssl-port: 853specifies the standard port for DNS over TLS.forward-zonewithforward-addrpointing to an IP address and ahostname#portcombination is how we tellunboundwhere to send its queries using TLS. The#dns.googlepart is crucial; it’s the hostname thatunboundwill use to verify the TLS certificate of the upstream server.
After saving this file, you need to restart the unbound service:
sudo systemctl restart unbound
Now, to make your system actually use this local unbound resolver, you need to update your /etc/resolv.conf. For most systems using systemd-resolved, you’ll want to edit /etc/systemd/resolved.conf:
[Resolve]
DNS=127.0.0.1
#FallbackDNS=8.8.8.8 1.1.1.1
#DNSSEC=yes
#DNSOverTLS=yes
And then restart systemd-resolved:
sudo systemctl restart systemd-resolved
If you’re not using systemd-resolved and have a static /etc/resolv.conf, you’d directly edit it:
nameserver 127.0.0.1
Let’s test it. You can use dig to query your local unbound resolver and see the response time. A typical query for google.com from your local resolver would look like this:
dig @127.0.0.1 google.com
The dig output will show you the answer. To verify that it’s actually using TLS, you can check unbound’s logs (if configured to be verbose) or observe network traffic with tcpdump filtering for port 853. For instance, to see traffic going to Google’s DoT server:
sudo tcpdump -i any port 853 and host 8.8.8.8
You’ll see TLS handshake and encrypted DNS packets.
The most surprising thing about DoT is that it doesn’t make DNS queries significantly slower for the end-user, despite the added encryption overhead. This is because the TLS handshake is typically done once per session with the resolver, and subsequent DNS queries are multiplexed over that established connection. The latency increase is often negligible compared to the base DNS lookup time, and the privacy gains are substantial.
The forward-addr directive is very flexible. You can specify multiple upstream servers, and unbound will load balance and rotate through them. The format ip_address@port#hostname is critical for certificate validation; if the hostname doesn’t match the certificate presented by the server at that IP and port, the connection will fail. This prevents man-in-the-middle attacks where an attacker might try to impersonate an upstream DNS server.
The root-hints file is a list of the IP addresses of the root DNS servers. unbound uses this to bootstrap its DNSSEC validation and to find authoritative servers if it’s not forwarding all queries. In a purely forwarding setup like the one above, it’s less critical but still good practice to have.
The unbound service might fail to start if the configuration file has syntax errors, or if another service is already using port 53. You can check unbound’s status with sudo systemctl status unbound and its logs with sudo journalctl -u unbound. Common issues include incorrect forward-addr syntax or missing /usr/share/dns/root.hints.
After configuring your system to use 127.0.0.1 as its DNS server, you’ll next want to explore DNSSEC validation to ensure the integrity of the DNS responses you receive.