Last updated: 2026-02-11
PowerDNS DNSSEC & DNS over TLS Configuration Guide
This guide provides recommended DNSSEC and DNS over TLS (DoT) settings for PowerDNS Authoritative Server and PowerDNS Recursor. PowerDNS provides built-in DNSSEC support with straightforward key management through the pdnsutil command-line tool. For DoT, dnsdist (covered in a separate guide) serves as the TLS frontend for the Authoritative Server, while the Recursor supports outgoing DoT natively.
Prerequisites
- PowerDNS Authoritative Server 4.7 or later
- PowerDNS Recursor 4.7 or later (for validation and outgoing DoT)
- A backend that supports DNSSEC (e.g.,
gmysql,gpgsql,lmdb,bind) - A valid TLS certificate and private key (for DoT via dnsdist)
Authoritative Server DNSSEC
Securing a Zone
Enable DNSSEC for an existing zone with a single command:
pdnsutil secure-zone example.com
This generates a KSK and ZSK using the default algorithm (ECDSAP256SHA256) and activates them immediately.
Algorithm Selection
Specify the algorithm when securing a zone:
pdnsutil secure-zone example.com ecdsap256sha256
| Algorithm | Recommendation |
|---|---|
rsasha256 |
Acceptable (2048-bit minimum) |
rsasha512 |
Acceptable |
ecdsap256sha256 |
Recommended |
ecdsap384sha384 |
Acceptable |
ed25519 |
Good (limited resolver support) |
Rectifying the Zone
After securing a zone, rectify it to generate NSEC/NSEC3 records and calculate ordername hashes:
pdnsutil rectify-zone example.com
Key Management
List all keys for a zone:
pdnsutil show-zone example.com
Add a new key:
# Add a new ZSK
pdnsutil add-zone-key example.com zsk active ecdsap256sha256
# Add a new KSK
pdnsutil add-zone-key example.com ksk active ecdsap256sha256
Rotate a ZSK:
# Add a new ZSK (active)
pdnsutil add-zone-key example.com zsk active ecdsap256sha256
# Deactivate the old ZSK (by key ID)
pdnsutil deactivate-zone-key example.com OLD_KEY_ID
# After TTL expiry, remove the old key
pdnsutil remove-zone-key example.com OLD_KEY_ID
DS Record Publication
Extract the DS record for publication at the parent zone:
pdnsutil show-zone example.com
This shows DS records in multiple digest formats. Publish the SHA-256 digest (type 2) with your registrar:
pdnsutil export-zone-ds example.com
You can also extract specific formats:
pdnsutil hash-zone-record example.com example.com
NSEC vs NSEC3
By default, PowerDNS uses NSEC. To switch to NSEC3 for authenticated denial of existence without zone enumeration:
pdnsutil set-nsec3 example.com '1 0 0 -'
pdnsutil rectify-zone example.com
The parameters '1 0 0 -' mean:
1-- NSEC3 algorithm (SHA-1, the only defined algorithm)0-- Flags (0 = no opt-out)0-- Iterations (0 is recommended per RFC 9276)--- Empty salt (no salt, recommended per RFC 9276)
To revert to NSEC:
pdnsutil unset-nsec3 example.com
pdnsutil rectify-zone example.com
Authoritative Server Configuration
Enable DNSSEC in pdns.conf:
# /etc/pdns/pdns.conf
# Backend (example: PostgreSQL)
launch=gpgsql
gpgsql-host=127.0.0.1
gpgsql-dbname=pdns
gpgsql-user=pdns
gpgsql-password=secret
gpgsql-dnssec=yes
# Enable DNSSEC
# (Key storage is handled by the backend)
# Bind to all interfaces
local-address=0.0.0.0, ::
local-port=53
# Authoritative only (recursion is disabled by default in 4.5+)
# AXFR restrictions
disable-axfr=yes
allow-axfr-ips=192.0.2.1/32,198.51.100.1/32
# Logging
loglevel=4
log-dns-queries=no
Recursor DNSSEC Validation
PowerDNS Recursor validates DNSSEC-signed responses automatically when configured:
# /etc/pdns-recursor/recursor.conf
# Enable DNSSEC validation
dnssec=validate
# Log DNSSEC validation failures
dnssec-log-bogus=yes
# Local address
local-address=127.0.0.1, ::1
# Allow queries from local networks
allow-from=127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, ::1/128, fe80::/10
# Forward to authoritative servers (optional)
# forward-zones=example.com=192.0.2.1
# Root hints
hint-file=/usr/share/dns/root.hints # RHEL: /var/named/named.ca; or omit (built-in hints used by default)
DNSSEC validation modes:
| Mode | Description |
|---|---|
off |
No DNSSEC processing |
process-no-validate |
Set DO bit, pass records through, no validation |
process |
Set DO bit, set AD flag where applicable |
log-fail |
Validate, log failures, but serve bogus answers |
validate |
Full validation, return SERVFAIL for bogus answers |
Complete Configuration
Authoritative Server
# /etc/pdns/pdns.conf
# Backend
launch=gpgsql
gpgsql-host=127.0.0.1
gpgsql-dbname=pdns
gpgsql-user=pdns
gpgsql-password=secret
gpgsql-dnssec=yes
# Network
local-address=0.0.0.0, ::
local-port=53
# Security (recursion is disabled by default in 4.5+)
disable-axfr=yes
allow-axfr-ips=192.0.2.1/32,198.51.100.1/32
version-string=anonymous
# Performance
cache-ttl=60
negquery-cache-ttl=60
query-cache-ttl=20 # Renamed to zone-cache-refresh-interval in Auth 4.8+
# Logging
loglevel=4
log-dns-queries=no
After starting the server, secure your zones:
pdnsutil secure-zone example.com
pdnsutil set-nsec3 example.com '1 0 0 -'
pdnsutil rectify-zone example.com
pdnsutil export-zone-ds example.com
Validating Recursor
# /etc/pdns-recursor/recursor.conf
# Network
local-address=127.0.0.1, ::1
local-port=53
# Access control
allow-from=127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, ::1/128, fe80::/10
# DNSSEC
dnssec=validate
dnssec-log-bogus=yes
# Root hints
hint-file=/usr/share/dns/root.hints # RHEL: /var/named/named.ca; or omit (built-in hints used by default)
# Performance
threads=4
max-cache-entries=1000000
max-packetcache-entries=500000
# Security
version-string=anonymous
DNS over TLS (DoT)
Authoritative Server with dnsdist
PowerDNS Authoritative Server does not natively support DNS over TLS. Use dnsdist as a TLS-terminating frontend (see the dnsdist guide for full configuration):
-- /etc/dnsdist/dnsdist.conf
-- DNS over TLS frontend
addTLSLocal('0.0.0.0:853', '/etc/dnsdist/ssl/fullchain.pem', '/etc/dnsdist/ssl/privkey.pem', {
minTLSVersion='tls1.2'
})
-- Forward to local PowerDNS Authoritative Server
newServer({address='127.0.0.1:53', name='pdns-auth'})
Recursor Outgoing DoT
PowerDNS Recursor 4.7+ supports forwarding queries to upstream resolvers over TLS. Specify the TLS port and server name for certificate verification:
# /etc/pdns-recursor/recursor.conf
# Forward to upstream over DoT
forward-zones-recurse=.=9.9.9.9:853#dns.quad9.net;149.112.112.112:853#dns.quad9.net
The #hostname suffix after the address enables TLS and verifies the server certificate against the specified hostname.
Version note: The
#hostnamesyntax for outgoing DoT requires PowerDNS Recursor 4.6+. On Debian 11 (Recursor 4.5), this syntax is not available and outgoing DoT is not supported natively.
Recursor DoT Configuration
# /etc/pdns-recursor/recursor.conf
# Network
local-address=127.0.0.1, ::1
local-port=53
# Access control
allow-from=127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, ::1/128, fe80::/10
# DNSSEC
dnssec=validate
dnssec-log-bogus=yes
# Forward all queries over DoT
forward-zones-recurse=.=9.9.9.9:853#dns.quad9.net;149.112.112.112:853#dns.quad9.net;1.1.1.1:853#cloudflare-dns.com;1.0.0.1:853#cloudflare-dns.com
# Performance
threads=4
max-cache-entries=1000000
max-packetcache-entries=500000
# Security
version-string=anonymous
Verification
Check that a zone is properly signed:
pdnsutil check-zone example.com
Verify DNSSEC keys and DS records:
pdnsutil show-zone example.com
Test with dig:
dig @localhost example.com DNSKEY +dnssec +multiline
dig @localhost example.com A +dnssec
Verify the ad (Authenticated Data) flag is set when querying through the recursor:
dig @127.0.0.1 example.com A +dnssec
Check NSEC3 records:
dig @localhost example.com NSEC3PARAM
Use delv for detailed validation:
delv @localhost example.com A +rtrace
Test the full chain of trust from an external resolver:
dig example.com A +dnssec +trace