Docs / DNS & Domains / DNS over TLS with Unbound

DNS over TLS with Unbound

By Admin · Mar 15, 2026 · Updated Apr 23, 2026 · 328 views · 2 min read

DNS over TLS (DoT) encrypts DNS queries between your resolver and upstream DNS servers, preventing eavesdropping on DNS traffic. Unbound is a popular validating, recursive DNS resolver that supports DoT both as a client (forwarding encrypted queries upstream) and as a server (accepting encrypted queries from clients). This guide covers both configurations.

Installing Unbound

sudo apt install unbound unbound-anchor    # Ubuntu/Debian
sudo dnf install unbound                    # Rocky Linux

# Download root hints
sudo wget -O /etc/unbound/root.hints https://www.internic.net/domain/named.root

# Initialize DNSSEC trust anchor
sudo unbound-anchor -a /etc/unbound/root.key

Unbound as DoT Client (Forward to Encrypted Upstream)

# /etc/unbound/unbound.conf
server:
    interface: 127.0.0.1
    port: 53
    access-control: 127.0.0.0/8 allow
    access-control: 10.0.0.0/8 allow

    # DNSSEC validation
    auto-trust-anchor-file: /etc/unbound/root.key

    # Performance
    num-threads: 4
    msg-cache-size: 128m
    rrset-cache-size: 256m
    cache-min-ttl: 60
    cache-max-ttl: 86400
    prefetch: yes
    prefetch-key: yes

    # Privacy
    qname-minimisation: yes
    hide-identity: yes
    hide-version: yes

    # TLS settings for upstream
    tls-cert-bundle: /etc/ssl/certs/ca-certificates.crt

forward-zone:
    name: "."
    forward-tls-upstream: yes
    # Cloudflare DNS over TLS
    forward-addr: 1.1.1.1@853#cloudflare-dns.com
    forward-addr: 1.0.0.1@853#cloudflare-dns.com
    # Google DNS over TLS
    forward-addr: 8.8.8.8@853#dns.google
    forward-addr: 8.8.4.4@853#dns.google
# Test configuration and start
sudo unbound-checkconf
sudo systemctl enable --now unbound

# Verify DoT is working
dig @127.0.0.1 example.com +short

# Verify DNSSEC
dig @127.0.0.1 dnssec-failed.org A  # Should return SERVFAIL

Unbound as DoT Server (Accept Encrypted Queries)

server:
    # Regular DNS on localhost
    interface: 127.0.0.1@53

    # DoT listener on all interfaces
    interface: 0.0.0.0@853
    tls-port: 853
    tls-service-key: /etc/letsencrypt/live/dns.example.com/privkey.pem
    tls-service-pem: /etc/letsencrypt/live/dns.example.com/fullchain.pem

    access-control: 0.0.0.0/0 allow
    auto-trust-anchor-file: /etc/unbound/root.key

Client Testing

# Test DoT with kdig
kdig @dns.example.com +tls example.com A

# Test with knot-resolver
kresd -n --tls dns.example.com

# Android: Settings → Network → Private DNS → dns.example.com

Local Network Caching Resolver

# Serve your local network with encrypted upstream queries
server:
    interface: 0.0.0.0
    port: 53
    access-control: 192.168.0.0/16 allow
    access-control: 10.0.0.0/8 allow
    access-control: 127.0.0.0/8 allow
    access-control: 0.0.0.0/0 refuse

    # Aggressive caching
    prefetch: yes
    serve-expired: yes
    serve-expired-ttl: 86400

forward-zone:
    name: "."
    forward-tls-upstream: yes
    forward-addr: 1.1.1.1@853#cloudflare-dns.com
    forward-addr: 9.9.9.9@853#dns.quad9.net

Best Practices

  • Always enable DNSSEC validation alongside DoT for complete DNS security
  • Use qname-minimisation to minimize data sent to upstream resolvers
  • Enable prefetch to keep popular records in cache before they expire
  • Configure multiple upstream DoT servers for redundancy
  • Use serve-expired to return stale cache entries if upstream is temporarily unavailable
  • Verify TLS certificate validation is working by testing with an invalid certificate

Was this article helpful?