Comprehensive configuration guidance for PowerDNS Recursor with best practices for production environments. The Recursor is a recursive DNS resolver - it does not serve authoritative zones.
| File | Purpose | Location |
|---|---|---|
| Main config | Recursor settings | /etc/powerdns/recursor.conf |
| Additional configs | Modular configs | /etc/powerdns/recursor.d/ |
| Lua scripts | Custom logic | /etc/powerdns/recursor.lua |
| RPZ files | Response Policy Zones | /etc/powerdns/rpz/ |
| Runtime | PID and sockets | /var/run/powerdns/ |
| Logs | Query logs | /var/log/powerdns/ |
# Listen addresses (IPv4 and IPv6)
local-address=0.0.0.0,::
local-port=53
# User and group to run as
setuid=pdns-recursor
setgid=pdns-recursor
# Daemon mode
daemon=yes
# Runtime directory
socket-dir=/var/run/powerdns
Prevent open resolver abuse:
# Allow queries only from trusted networks
allow-from=127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
# For public resolver (use with rate limiting!)
# allow-from=0.0.0.0/0,::/0
# Maximum subnet size for allow-from
allow-from-subnet-size=24
Option 1: Full recursion (default)
# No forwarding - full recursive resolution
# forward-zones=
Option 2: Forward all queries
# Forward everything to upstream resolvers
forward-zones=.=223.5.5.5,1.0.0.1,8.8.8.8
Option 3: Split forwarding
# Forward specific zones
forward-zones=example.com.=10.0.0.10,internal.local.=10.0.0.20
# Forward everything else with recursion
forward-zones-recurse=.=223.5.5.5,1.0.0.1
Option 4: Forward from file
# Load forward zones from file
forward-zones-file=/etc/powerdns/forward-zones.conf
Example forward-zones.conf:
# Format: domain=IP1,IP2,IP3
example.com=10.0.0.10,10.0.0.11
internal.local=10.0.0.20
.=223.5.5.5,1.0.0.1
# Enable DNSSEC validation (recommended)
dnssec=validate
# Other options:
# dnssec=process - Process DNSSEC but don't require
# dnssec=log-fail - Log validation failures
# dnssec=off - Disable DNSSEC
# Trust anchor file (optional, uses built-in by default)
# trustanchor.file=/etc/powerdns/trustanchor.file
# Negative trust anchors (for broken domains)
# negtrustanchor=example.com
# Number of worker threads (default: 2, increase for high load)
threads=4
# Cache settings
max-cache-entries=1000000
packetcache-max-size=500000
max-cache-ttl=86400
max-negative-ttl=3600
# Query settings
max-qperq=50
max-mthreads=2000
max-udp-packets-per-fragment=10
# TCP settings
max-tcp-clients=128
max-tcp-per-client=0
max-tcp-queries-per-connection=100
# Log level (0-9, higher = more verbose)
loglevel=6
# Logging facility (syslog)
logging-facility=1
# Log specific events
log-common-troubles=yes
log-rpzs=yes
log-spoofing=yes
# Quiet mode (reduces logging)
# quiet=yes
# Log file (leave empty for syslog)
# log-file=/var/log/powerdns/recursor.log
# Enable web server for API and statistics
webserver=yes
webserver-address=127.0.0.1
webserver-port=8082
webserver-password=your_secure_password_here
# Allow from specific networks
webserver-allow-from=127.0.0.1,10.0.0.0/8
# API key (alternative to password)
# api-key=your_api_key_here
# Log level for web server
webserver-loglevel=normal
# Rate limiting (queries per second)
# Note: Full rate limiting requires Lua scripting
# Basic protections
max-mthreads=2000
max-qperq=50
# TCP connection limits
max-tcp-clients=128
max-tcp-queries-per-connection=100
Enable Lua scripting for advanced query handling:
# Enable Lua configuration file
lua-config-file=/etc/powerdns/recursor.lua
Example recursor.lua - Block specific domains:
-- Block malicious domains
function nodata(dq)
if dq.qname:match('malware%.example%.com$') then
dq:addAnswer(pdns.CNAME, '.')
return true
end
return false
end
-- Log specific queries
function preresolve(dq)
if dq.qname:match('sensitive%.example%.com$') then
pdnslog('Query for sensitive domain: ' .. dq.qname .. ' from ' .. dq.remoteaddr)
end
return false
end
Configure Response Policy Zones for threat blocking:
# RPZ file path
rpz-file=/etc/powerdns/rpz/blocklist.txt
# RPZ action override
rpz-action-override=Drop
# RPZ IP override (for A/AAAA records)
rpz-ip-override=0.0.0.0
# RPZ NXDOMAIN action
rpz-nxdomain-action=NXDOMAIN
Example blocklist.txt:
; Malware domains
malware-site.com CNAME .
bad-domain.net CNAME .
; Ad servers
ads.example.com CNAME .
; Tracking domains
tracker.example.org CNAME .
; IP-based blocking
192.0.2.1 CNAME .
# Route queries based on source IP
# This requires Lua scripting
# Example: Different forwarders for different subnets
-- In recursor.lua
function preresolve(dq)
if dq.remoteaddr:match('^10%.0%.0%.') then
dq:setVariable('forward-to', '10.0.0.1')
end
return false
end
# Enable ECS (EDNS Client Subnet)
use-client-subnet=yes
# ECS scope handling
ecs-scope-zero-address=0.0.0.0/0
# Basic home resolver configuration
local-address=127.0.0.1
local-port=53
allow-from=127.0.0.0/8,192.168.0.0/16,10.0.0.0/8
# Forward to public resolvers
forward-zones=.=223.5.5.5,1.0.0.1
# DNSSEC validation
dnssec=validate
# Basic logging
loglevel=5
log-common-troubles=yes
# Web server for stats
webserver=yes
webserver-address=127.0.0.1
webserver-port=8082
webserver-password=home_password
# Enterprise internal resolver
local-address=0.0.0.0,::
local-port=53
# Allow internal networks only
allow-from=10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
# Forward internal zones to internal DNS
forward-zones=internal.example.com.=10.0.1.10,10.0.1.11
forward-zones=corp.local.=10.0.2.10
# Forward external with recursion
forward-zones-recurse=.=223.5.5.5,1.0.0.1,8.8.8.8
# DNSSEC validation
dnssec=validate
# Performance tuning
threads=8
max-cache-entries=2000000
max-mthreads=5000
# Logging
loglevel=6
log-file=/var/log/powerdns/recursor.log
# Web server (internal only)
webserver=yes
webserver-address=10.0.0.1
webserver-port=8082
webserver-password=enterprise_password
webserver-allow-from=10.0.0.0/8
# Public resolver configuration
local-address=0.0.0.0,::
local-port=53
# Allow from anywhere (use with Lua rate limiting!)
allow-from=0.0.0.0/0,::/0
# Full recursion
# forward-zones=
# DNSSEC validation
dnssec=validate
# Performance
threads=16
max-cache-entries=5000000
max-mthreads=10000
# TCP limits
max-tcp-clients=500
max-tcp-queries-per-connection=100
# Logging
loglevel=5
log-common-troubles=yes
# Lua rate limiting (required for public resolvers)
lua-config-file=/etc/powerdns/ratelimit.lua
# Check configuration syntax
pdns_recursor --config
# Test configuration without starting
pdns_recursor --config-dir=/etc/powerdns --daemon=no
# Verify service status
systemctl status pdns-recursor
# Test resolution
dig @localhost example.com
# Test DNSSEC
dig @localhost dnssec-failed.org
# Should return SERVFAIL if validation working
# Reload configuration (some changes require restart)
systemctl reload pdns-recursor
# Full restart (required for some changes)
systemctl restart pdns-recursor
# Check status
systemctl status pdns-recursor
# Monitor logs
journalctl -u pdns-recursor -f
# Get server statistics
curl -H "X-API-Key: your_password" \
http://localhost:8082/api/v1/servers/localhost
# Get detailed statistics
curl -H "X-API-Key: your_password" \
http://localhost:8082/api/v1/servers/localhost/statistics
# Get config values
curl -H "X-API-Key: your_password" \
http://localhost:8082/api/v1/servers/localhost/config
Enable metrics endpoint at /metrics:
# Access metrics
curl http://localhost:8082/metrics
Key metrics:
powerdns_recursor_questions_total - Total queriespowerdns_recursor_cache_entries - Cache sizepowerdns_recursor_dnssec_validations_total - DNSSEC validationsNo DNSSEC validation:
dig @localhost dnssec-failed.org
# Should return SERVFAIL
Forwarding not working:
# Check forward-zones configuration
pdns_recursor --config | grep forward
High memory usage:
# Reduce cache size
max-cache-entries=500000
Slow queries:
# Increase threads
threads=8
# Check upstream latency
# Run in foreground with debug logging
pdns_recursor --daemon=no --loglevel=9 --disable-syslog
Running PowerDNS Recursor in regulated environments? We assist with:
Secure your deployment: office@linux-server-admin.com | Contact Page