Skip to main content
Plain DNS on port 53 is sent in clear text: anyone on the network can read it or change it. With a TLS certificate, DNZ also serves encrypted DNS to your devices, plus the dashboard over HTTPS. Encrypted queries go through the same path as UDP and TCP: local zones, views, blocklists, the cache, conditional forwarding and the query log.
This page is about DNZ serving encrypted DNS to clients. To have DNZ forward over DoT, DoH or DoQ to its upstreams, see Forwarding.

Turn it on

1

Get a certificate

The hostname (for example dns.example.com) must be a public name, and the certificate must come from a CA that devices trust, such as Let’s Encrypt. See Get a certificate.
2

Point DNZ at the certificate

/etc/dnz/dnz.env
The same settings live in the [tls] section of the config file (cert, key, dot_addr, https_addr, doq_addr, h3_addr). See Configuration.
3

Open the ports

Ports 853 and 443 are below 1024. The systemd unit already has CAP_NET_BIND_SERVICE. For Docker, publish the ports (TCP and UDP):
docker-compose.yml
4

Restart DNZ

The log shows dns-over-tls listening on …, dns-over-quic listening on …, dns-over-http/3 listening on … and https listening on …. Settings → General → Encrypted DNS shows which protocols are on.

Renewing the certificate

DNZ checks the certificate files every minute and reads them again when they change, so renewing the certificate doesn’t need a restart. If the new files are broken, the old certificate stays in use and the error appears under Settings → General → Encrypted DNS.

Ports for DoQ and DoH3

DoQ and DoH3 run over UDP on the same port numbers as DoT and HTTPS, so one certificate and one pair of port numbers cover everything. Set DNZ_DOQ_ADDR or DNZ_H3_ADDR to another address to move them, or to off to turn them off. If their UDP port is taken (by another HTTP/3 server, say), DNZ logs a warning and runs without them. Over HTTP/3, DNZ answers only /dns-query. The UI stays on HTTPS over TCP, and DNZ doesn’t send Alt-Svc, so browsers keep using HTTP/2. Clients reach DoH3 when they are configured for it, or by finding it in an HTTPS/SVCB record, for example:

DoH behind your own reverse proxy

/dns-query is also served on the plain HTTP port (DNZ_HTTP_ADDR), so DoH can sit behind your own HTTPS reverse proxy such as Caddy or nginx. Set DNZ_TRUSTED_PROXIES to the proxy’s address so the real client IP is still used for views, blocklists and logs. See HTTPS.

Get a certificate

When DNZ is the public nameserver for the hostname’s zone, use DNZ’s own ACME endpoint with lego. Create an API token with records.write for that zone, then:
lego stores certificates/dns.example.com.crt and .key under --path. Point DNZ_TLS_CERT and DNZ_TLS_KEY at those files. See ACME certificates.
With the systemd unit (DynamicUser), the service can’t read a root-owned key with mode 600. Give it access through the ssl-cert group: turn on SupplementaryGroups=ssl-cert in the unit, then after creating or renewing the certificate run:
This can go in lego’s --renew-hook.

Connect devices

Settings → General → Encrypted DNS shows the hostname, the DoH, DoQ and DoH3 addresses and the certificate’s expiry. Replace dns.example.com below with your hostname.
Android 9 and later: Settings → Network & internet → Private DNS → Private DNS provider hostname → dns.example.com.This uses DNS over TLS, so port 853 must be reachable.

Troubleshooting

Their UDP port is probably taken by another program, such as another HTTP/3 server. DNZ logs a warning and runs without them. Move them with DNZ_DOQ_ADDR / DNZ_H3_ADDR, or turn them off with off.
Look under Settings → General → Encrypted DNS for the error. If the new files are broken, DNZ keeps the old certificate. Under systemd, also check that the service can read the key (see the ssl-cert note above).
Android uses DNS over TLS on port 853. Make sure the port is open in the firewall and published in Docker, and that the hostname matches the certificate.