DNS over TLS: Setup Guides for Routers, Servers, and Clients

DNS over TLS: Setup Guides for Routers, Servers, and Clients

Encrypted DNS changes how your network handles name resolution by moving queries from UDP/53 to TLS on TCP/853 with certificate validation and persistent connections.

We’ll show reliable ways to enable DNS over TLS (DoT) on routers, server-side resolvers, and end-user clients, plus commands to verify strict TLS, detect leaks, and avoid common breakage.

The flow is straightforward: your stub or caching resolver validates a server certificate for a published authentication name (for example one.one.one.one, dns.google, or dns.quad9.net) and then transports DNS messages inside that encrypted session; strict profiles refuse cleartext or invalid certificates so you don’t silently downgrade.

Because DoT changes transport semantics, you’ll want to tune caches, MTU-sensitive options, and firewall rules so reliability and latency stay predictable under load.

Why DNS Over TLS Matters

Plain DNS is readable and malleable in transit; DoT hides queries from passive observers and blocks simple on-path tampering, while DNSSEC (if enabled) validates answer integrity at the data layer; middleboxes that relied on plaintext inspection won’t see your queries anymore.

Prerequisites and Core Concepts

Pick resolvers that publish a TLS authentication domain and certificate policy (one.one.one.one for 1.1.1.1, dns.google for 8.8.8.8, dns.quad9.net for 9.9.9.9); correct time is mandatory because TLS rejects skewed clocks; prefer strict DoT with verification tied to an auth name over opportunistic modes that might fall back to UDP/53 without notice.

Router Setup (OpenWrt-Style, Embedded Linux)

Most routers ship with dnsmasq for DHCP and DNS; to add DoT, run a local caching forwarder that speaks plain DNS to LAN clients but forwards upstream via TLS; Unbound or Stubby are proven options on OpenWrt-like systems.

Option A: Unbound as a DoT Forwarder

Install Unbound and a CA bundle, enable the service, then define a forward-zone with TLS and pinned authentication names; Unbound listens on 53 for the LAN and connects out on 853.

# Router shell
opkg update
opkg install unbound ca-bundle ca-certificates
# /etc/unbound/unbound.conf.d/dot-forward.conf
server:
  cache-min-ttl: 120
  cache-max-ttl: 86400
  edns-buffer-size: 1232
  prefetch: yes
  qname-minimisation: yes
  tls-cert-bundle: "/etc/ssl/certs/ca-certificates.crt"
forward-zone:
  name: "."
  forward-tls-upstream: yes
  forward-addr: 1.1.1.1@853#one.one.one.one
  forward-addr: 9.9.9.9@853#dns.quad9.net
# enable and start
/etc/init.d/unbound enable
/etc/init.d/unbound restart

Point dnsmasq (or your DHCP server) at 127.0.0.1 for DNS, or advertise the router’s LAN IP; remove any WAN DNS overrides so only Unbound goes upstream.

Option B: Stubby (getdns) as a DoT Stub

Stubby is a small DoT stub that listens locally and forwards to providers with strict verification; you can pair it with dnsmasq for caching or let Unbound cache while Stubby handles upstream TLS.

opkg update
opkg install stubby ca-bundle ca-certificates
# /etc/stubby/stubby.yml (strict profile)
resolution_type: GETDNS_RESOLUTION_STUB
dns_transport_list:
  - GETDNS_TRANSPORT_TLS
tls_authentication: GETDNS_AUTHENTICATION_REQUIRED
round_robin_upstreams: 1
idle_timeout: 6000
listen_addresses:
  - 127.0.0.1@5453
  - ::1@5453
upstream_recursive_servers:
  - address_data: 1.1.1.1
    tls_port: 853
    tls_auth_name: "one.one.one.one"
  - address_data: 9.9.9.9
    tls_port: 853
    tls_auth_name: "dns.quad9.net"
/etc/init.d/stubby enable
/etc/init.d/stubby restart

Configure dnsmasq to forward to 127.0.0.1#5453 (and ::1#5453 for IPv6) and confirm that WAN egress is TCP/853 only.

Server Setup (Expose Your Own DoT Resolver)

Hosting a resolver with inbound DoT gives you fleet-wide policy and a single encrypted endpoint; you’ll need a hostname with a valid certificate, firewall rules for TCP/853, and a resolver that accepts TLS.

Unbound: Inbound DoT

Enable a TLS listener on 853 with your key and certificate, bind on public addresses, and keep any legacy plain-DNS on a separate interface if you still serve internal clients; enable DNSSEC validation to catch tampering upstream.

# /etc/unbound/unbound.conf.d/dot-inbound.conf
server:
  interface: 0.0.0.0@853
  interface: ::0@853
  tls-service-key: "/etc/letsencrypt/live/dns.example.com/privkey.pem"
  tls-service-pem: "/etc/letsencrypt/live/dns.example.com/fullchain.pem"
  hide-identity: yes
  hide-version: yes
  rrset-roundrobin: yes
  qname-minimisation: yes
  harden-dnssec-stripped: yes

If you front the resolver with a TCP/TLS stream proxy, preserve client addresses only if both sides support PROXY protocol v2; otherwise terminate TLS on Unbound to keep logs and rate limits accurate.

Knot Resolver: Inbound and Outbound DoT

Knot Resolver can accept client DoT and forward upstream via DoT with hostname authentication; enable TLS listeners, load certificates, then define a TLS forward policy for upstreams.

-- /etc/knot-resolver/kresd.conf
net.listen('0.0.0.0', 853, { kind = 'tls' })
net.listen('::', 853, { kind = 'tls' })
net.tls('/etc/knot-resolver/server-cert.pem', '/etc/knot-resolver/server-key.pem')
cache.size = 100 * MB
modules.load('policy')
policy.add(policy.all(policy.TLS_FORWARD({
  {'1.1.1.1', hostname='one.one.one.one'},
  {'9.9.9.9', hostname='dns.quad9.net'}
})))

Recent BIND 9.18+ can also serve DoT via tls and listen-on tls blocks, and modern PowerDNS Recursor supports outbound DoT; verify your exact version and feature flags before production.

Client Setup

Clients can speak DoT directly to a public resolver or to your router/server; always prefer strict validation so you don’t silently fall back to UDP/53.

Android (Private DNS)

Android 9+ supports system-wide DoT; go to Settings → Network & Internet → Private DNS, choose “Private DNS provider hostname,” and enter one.one.one.one, dns.google, or dns.quad9.net; IPs alone won’t work because Android validates the certificate against the hostname.

Linux with systemd-resolved

systemd-resolved enforces strict DoT to named servers using the “IP#hostname” form; point resolv.conf at the stub and enable TLS.

# /etc/systemd/resolved.conf.d/dot.conf
[Resolve]
DNS=1.1.1.1#one.one.one.one 9.9.9.9#dns.quad9.net
DNSOverTLS=yes
# activate
sudo mkdir -p /etc/systemd/resolved.conf.d
sudo systemctl restart systemd-resolved
sudo ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf

Use resolvectl status to confirm “DNS Over TLS: yes (strict)” and that servers show the expected hostnames.

iOS and macOS

Create and install a DNS Settings configuration profile (via MDM or manually) that specifies DNS over TLS and the resolver hostname; this applies system-wide on Wi-Fi and cellular; third-party stubs remain optional.

Windows Note

Windows 11 includes native DNS-over-HTTPS for the system resolver; system-wide DoT isn’t generally available in stable releases, so if you need DoT specifically, run a local stub (Stubby or Unbound) on 127.0.0.1 and point Windows at it over classic DNS while the stub upgrades upstream to DoT.

Testing and Proving It Works

To test a provider directly with strict TLS and hostname validation, use kdig which speaks DoT natively and verifies against your CA store; to inspect only TLS, you can use openssl with SNI set.

# strict DoT with hostname validation
kdig @1.1.1.1 +tls-ca +tls-hostname=one.one.one.one example.com A
# TLS handshake and certificate peek
openssl s_client -connect 1.1.1.1:853 -servername one.one.one.one -quiet

To verify your router or stub is the only upstream path, capture traffic while generating lookups; you should see TCP/853 to your chosen providers and no UDP/53 to the internet.

# on the router or host
tcpdump -ni any 'port 53 or port 853'
# generate some load through your local resolver
for i in {1..5}; do dig +short example${i}.com @127.0.0.1; done
# on systemd-resolved hosts
resolvectl status | grep -i "DNS Over TLS"

Avoiding Leaks and Breakage

Keep time correct (enable NTP) so TLS validation succeeds; pin hostnames, not just IPs, because SNI and certificate checks use names; prefer strict modes over opportunistic to avoid silent downgrade; set EDNS buffer near 1232 bytes to reduce fragmentation; use long-lived connections and prefetch to cut handshake overhead; for captive portals, DoT will fail until you complete sign-on, so allow clear DNS only on a quarantined onboarding network.

Operational Tips

Configure at least two upstream DoT providers and enable health checks; export metrics for query rates, TLS failures, and fallbacks; record provider hostnames in configuration management so audits can confirm that strict validation is enabled end to end.

DNS over TLS Setup Guides (FAQ)

Run a short capture on the WAN and ensure you see outbound TCP connections to port 853 and no UDP/53; to identify which resolver answered a test name during the capture, issue a quick DNS Lookup.

Enter the provider’s hostname such as one.one.one.one, dns.google, or dns.quad9.net; IPs are rejected because Android validates the certificate against the hostname rather than an address, and you can verify your public path separately with What Is My IP Address.

Functionally it’s the same, but confirm reachability and firewall rules for TCP/853 over IPv6; if you aren’t sure your network supports it, run an IPv6 Test before switching to strict mode.

Set DNSOverTLS=yes and use the “IP#hostname” form in DNS= so SNI and certificate checks use the provider’s name; resolvectl status should then report “DNS Over TLS: yes (strict).”

Yes, serve plain DNS on the LAN side from a local cache that forwards upstream via DoT, then block direct UDP/53 and TCP/53 egress so clients must use your resolver.

Yes; DoT protects the transport channel, while DNSSEC validates the authenticity of the data; combining both gives privacy in transit and verifiable answers.

Captive portals, legacy firewalls that block TCP/853, and tools that relied on plaintext DNS visibility; pilot carefully, document fallbacks, and monitor TLS errors per site.

Use kdig with +tls-ca and +tls-hostname, for example kdig @1.1.1.1 +tls-ca +tls-hostname=one.one.one.one example.com A, which proves encryption and hostname verification in one step.