By default, DNS queries on Linux go to your ISP’s resolver in plaintext on UDP port 53. Anyone between you and that resolver — your ISP, your network admin, a rogue router — can log every domain you visit. DNS over HTTPS (DoH) encrypts those queries inside standard HTTPS traffic on port 443.
This guide covers three approaches: the built-in systemd-resolved (no extra software), dnscrypt-proxy (flexible, supports filters), and AdGuard Home (self-hosted with a web UI).
Method 1: systemd-resolved with DoH
Most modern Debian/Ubuntu and Fedora systems already use systemd-resolved. Check:
systemctl status systemd-resolved
resolvectl status | head -20
Enable DNS over HTTPS in systemd-resolved
Edit the resolved configuration:
sudo nano /etc/systemd/resolved.conf
Add or modify:
[Resolve]
DNS=1.1.1.1#cloudflare-dns.com 9.9.9.9#dns.quad9.net
FallbackDNS=8.8.8.8#dns.google
DNSSEC=yes
DNSOverTLS=yes
DNS= accepts IP#hostname format. The #hostname part is used to verify the TLS certificate.
Restart resolved:
sudo systemctl restart systemd-resolved
Verify DoH is active:
resolvectl status | grep -E "(DNS Server|DNS over TLS|DNSSEC)"
Expected output:
Current DNS Server: 1.1.1.1#cloudflare-dns.com
DNS over TLS setting: yes
DNSSEC setting: yes
Test a lookup and confirm it works:
resolvectl query example.com
Confirm No Plaintext DNS Leaks
Capture traffic to check that port 53 traffic is gone:
sudo tcpdump -i any port 53 -c 20 &
dig example.com
You should see no port 53 traffic if DoH is working correctly. All queries should appear on port 443.
Method 2: dnscrypt-proxy
dnscrypt-proxy supports DoH, DNSCrypt, and ODoH (Oblivious DoH). It runs as a local resolver on 127.0.0.1:53 and proxies queries through encrypted channels.
Install dnscrypt-proxy
# Debian/Ubuntu
sudo apt install dnscrypt-proxy
# Fedora
sudo dnf install dnscrypt-proxy
# Arch
sudo pacman -S dnscrypt-proxy
Configure dnscrypt-proxy
sudo nano /etc/dnscrypt-proxy/dnscrypt-proxy.toml
Key settings to configure:
# Listen on localhost port 53
listen_addresses = ['127.0.0.1:53', '[::1]:53']
# Use only DoH servers (not plain DNS)
server_names = ['cloudflare', 'quad9-doh-ip4-filter-pri', 'nextdns']
# Filter out servers without DNSSEC
require_dnssec = true
# Filter out servers that log queries
require_nolog = true
# Filter out servers that don't require NOFILTER
require_nofilter = false
# Block ads and trackers with built-in blocklists
[sources]
[sources.public-resolvers]
urls = ['https://raw.githubusercontent.com/DNSCrypt/dnscrypt-resolvers/master/v3/public-resolvers.md']
cache_file = '/var/cache/dnscrypt-proxy/public-resolvers.md'
minisign_key = 'RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3'
refresh_delay = 72
# Enable CLOAKING for local overrides (optional)
# [cloaking]
# cloaking_rules = '/etc/dnscrypt-proxy/cloaking-rules.txt'
# Block known malware/ad domains
[blocked_names]
blocked_names_file = '/etc/dnscrypt-proxy/blocked-names.txt'
Start and enable the service:
sudo systemctl enable --now dnscrypt-proxy
Point systemd-resolved at dnscrypt-proxy
Edit resolved.conf to use the local proxy:
sudo nano /etc/systemd/resolved.conf
[Resolve]
DNS=127.0.0.1
DNSOverTLS=no
DNSSEC=no
(dnscrypt-proxy handles DNSSEC and encryption itself.)
Restart both services:
sudo systemctl restart dnscrypt-proxy systemd-resolved
Test:
dig @127.0.0.1 example.com
Check which server is actually being used:
dnscrypt-proxy -resolve example.com
Method 3: AdGuard Home (Self-Hosted)
AdGuard Home is a DNS server with DoH support, ad blocking, and a web interface. Good for households or small networks.
Install AdGuard Home
curl -s -S -L https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scripts/install.sh | sh -s -- -v
The installer places the binary in /opt/AdGuardHome/. Access the setup wizard at http://localhost:3000.
During setup:
- Set the DNS server to listen on port 53
- Set the admin interface to port 3000 (or another port)
- Choose upstream DNS servers with DoH enabled
Configure DoH Upstream in AdGuard Home
In the web UI, go to Settings > DNS settings > Upstream DNS servers:
https://dns.cloudflare.com/dns-query
https://dns.quad9.net/dns-query
https://dns.adguard-dns.com/dns-query
Enable Parallel requests for faster lookups.
Set your system DNS to 127.0.0.1:
sudo resolvectl dns eth0 127.0.0.1
Or edit /etc/resolv.conf directly (if not managed by systemd-resolved):
echo "nameserver 127.0.0.1" | sudo tee /etc/resolv.conf
Lock the file against NetworkManager overwrites:
sudo chattr +i /etc/resolv.conf
Choosing a DoH Provider
| Provider | Logs | DNSSEC | Filtering | Notes |
|---|---|---|---|---|
| Cloudflare (1.1.1.1) | No query logs | Yes | Optional | Fast, widely audited |
| Quad9 (9.9.9.9) | No PII | Yes | Malware blocking | Swiss nonprofit |
| NextDNS | Configurable | Yes | Highly configurable | Free tier available |
| AdGuard DNS | Minimal | Yes | Ad/tracker blocking | Self-hostable |
Avoid using your ISP’s or Google’s DNS for privacy — they log queries.
Verify the Full Setup
Check DNS resolution is working:
nslookup example.com
curl -s "https://www.dnsleaktest.com/results.json" | python3 -m json.tool | grep name
Run a DNS leak test at https://dnsleaktest.com — all results should show your chosen provider, not your ISP.
Confirm DNSSEC validation:
dig +dnssec sigfail.verteiltesysteme.net
# Should return SERVFAIL if DNSSEC validation is working
Troubleshooting Common Issues
DNS Resolution Fails After Configuration
If you lose DNS resolution after enabling DoH, the most likely cause is a configuration syntax error:
# Temporary fix: use a direct DNS server
echo "nameserver 1.1.1.1" | sudo tee /etc/resolv.conf
# Then review your configuration files for typos
sudo journalctl -u systemd-resolved --no-pager -n 50
Slow DNS Lookups
DoH adds TLS overhead compared to plain DNS. If lookups feel slow, check latency:
time dig @1.1.1.1 example.com
time dig @9.9.9.9 example.com
time dig @8.8.8.8 example.com
Choose the provider with the lowest latency from your location. Cloudflare consistently provides the fastest response times due to its CDN presence.
NetworkManager Overwriting resolv.conf
NetworkManager frequently overwrites /etc/resolv.conf. Prevent this:
# /etc/NetworkManager/conf.d/dns.conf
[main]
dns=systemd-resolved
Then restart: sudo systemctl restart NetworkManager
Performance Comparison: DoH vs Standard DNS
| Metric | Standard DNS (UDP 53) | DNS over TLS | DNS over HTTPS |
|---|---|---|---|
| First lookup latency | ~20ms | ~80ms | ~100ms |
| Cached lookup latency | ~1ms | ~1ms | ~1ms |
| Privacy from ISP | None | Full | Full |
| Resistance to blocking | None | Moderate (port 853) | High (port 443) |
| CPU overhead | Minimal | Low | Low-Medium |
The first lookup is slower with DoH due to the TLS handshake, but subsequent lookups use cached connections and perform comparably.
Related Articles
- How to Set Up Encrypted DNS-over-HTTPS (DoH) on All Devices
- How to Configure DNS over HTTPS Inside a VPN Tunnel
- Linux Mint Privacy Setup Guide for Beginners
- Linux Secure Boot Setup with Custom Keys for Preventing.
- Proton Drive Linux Client Setup Guide 2026
Built by theluckystrike — More at zovo.one