# Enable DoH and DoT Besides plain DNS on port 53, nxdns can answer encrypted queries from your devices: DNS over HTTPS (DoH) on port 443 and DNS over TLS (DoT) on port 853. Both are off by default, and both need a certificate and its private key, as PEM files. ## 1. Get a certificate Devices check the certificate against the name you give them, so use a certificate for a name you control, from an ACME client such as certbot, lego or Caddy. Android's Private DNS setting, for example, takes a hostname and needs a certificate a phone trusts. To try it out first, a self-signed certificate is enough: ```sh openssl req -x509 -newkey rsa:2048 -nodes \ -keyout /etc/nxdns/key.pem -out /etc/nxdns/cert.pem -days 365 \ -subj "/CN=nxdns.lan" \ -addext "subjectAltName=DNS:nxdns.lan,IP:192.168.1.10" ``` Replace `nxdns.lan` and `192.168.1.10` with the name and address your devices will use. The key must be readable by the user nxdns runs as, and by nobody else. Under systemd that is the `nxdns` user: ```sh chown nxdns:nxdns /etc/nxdns/cert.pem /etc/nxdns/key.pem chmod 0644 /etc/nxdns/cert.pem chmod 0600 /etc/nxdns/key.pem ``` Under Docker, put both files in `etc-nxdns/` next to `compose.yaml` and give them to uid 65532 the same way, since the container cannot change the read-only mount itself. ## 2. Turn the listeners on In file mode, add both sections to the configuration file: ```zon .doh_server = .{ .enabled = true, .cert_path = "/etc/nxdns/cert.pem", .key_path = "/etc/nxdns/key.pem", }, .dot_server = .{ .enabled = true, .cert_path = "/etc/nxdns/cert.pem", .key_path = "/etc/nxdns/key.pem", }, ``` In database mode, open **System** in the web interface, tick **enabled** under **DoH Server** and **DoT Server**, check the certificate and key paths, and save. Both listeners bind every address on their standard ports by default; `bind` and `port` change that. The packaged systemd unit already allows binding ports below 1024. Under Docker, uncomment the `443` and `853` lines under `ports:` in `compose.yaml`. ## 3. Check and restart In file mode, `check` loads the certificate and key the same way the listeners will: ```sh nxdns check --config /etc/nxdns/config.zon ``` It fails when a file is unreadable or when the key does not belong to the certificate, and warns when the key is readable by others: ``` FAIL doh_server.key_path: '/etc/nxdns/other.pem': private key does not belong to the certificate WARN dot_server.key_path: '/etc/nxdns/key.pem' is mode 644; a TLS key must be readable by its owner only ``` Then restart nxdns (`systemctl restart nxdns`, or `docker compose restart`). The log names both listeners: ``` info(nxdns): doh listener on 0.0.0.0:443 info(nxdns): dot listener on 0.0.0.0:853 ``` If a certificate cannot be loaded, nxdns refuses to start and exits 2 rather than run without the listener you asked for: ``` doh_server: '/etc/nxdns/cert.pem' + '/etc/nxdns/key.pem': private key file is not readable nxdns run failed: BadCertificate ``` ## 4. Test them DoT, with a `dig` from BIND 9.18 or newer: ```sh dig @192.168.1.10 +tls example.com A +short ``` DoH, using curl's own DoH resolver to look up `example.com`: ```sh curl -sS --doh-url https://nxdns.lan/dns-query -o /dev/null \ -w 'resolved to %{remote_ip}\n' https://example.com ``` Add `--doh-insecure` to the curl command while you test with a self-signed certificate. DoH is served at `/dns-query` only, over HTTP/1.1, as both GET and POST. ## Renewing the certificate nxdns checks both files every 30 seconds and switches to a renewed pair on its own; connections already open finish on the old one. There is nothing to restart. Each reload is logged: ``` info(cert_store): certificate reloaded from /etc/nxdns/cert.pem ``` To load it at once instead, press **Reload certificates** on the **System** page. If a reload fails, nxdns keeps serving the certificate it has and says why.