PowerDNS Cheat Sheet
Overview
PowerDNS is a high-performance authoritative DNS server that supports multiple database backends including PostgreSQL, MySQL/MariaDB, SQLite, LDAP, and BIND zone files. Unlike traditional DNS servers that read static zone files, PowerDNS can serve DNS records directly from a database, enabling dynamic DNS updates through its REST API or direct database manipulation. It supports DNSSEC out of the box with automatic key management, ALIAS records for zone apex CNAMEs, and Lua scripting for advanced record manipulation.
The PowerDNS ecosystem consists of three components: the Authoritative Server (pdns) for hosting authoritative zones, the Recursor (pdns-recursor) for recursive resolution, and dnsdist for DNS load balancing and traffic management. The authoritative server’s API allows full programmatic control over zones and records, making it ideal for automation, hosting platforms, and infrastructure-as-code workflows. PowerDNS handles billions of DNS queries per day in production at major hosting providers and DNS service operators worldwide.
Installation
Package Installation
# Ubuntu/Debian
sudo apt install pdns-server pdns-backend-pgsql
# Or with MySQL backend
sudo apt install pdns-server pdns-backend-mysql
# RHEL/CentOS
sudo dnf install pdns pdns-backend-postgresql
# Install PowerDNS tools
sudo apt install pdns-tools pdnsutil
# Start service
sudo systemctl enable pdns
sudo systemctl start pdns
Docker
docker run -d --name pdns \
-p 53:53/tcp -p 53:53/udp -p 8081:8081 \
-e PDNS_api=yes \
-e PDNS_api_key=changeme \
-e PDNS_webserver=yes \
-e PDNS_webserver_address=0.0.0.0 \
-e PDNS_webserver_port=8081 \
-e PDNS_launch=gsqlite3 \
-e PDNS_gsqlite3_database=/var/lib/powerdns/pdns.sqlite3 \
powerdns/pdns-auth-48
Configuration
Main Config (/etc/powerdns/pdns.conf)
# General settings
daemon=yes
guardian=yes
setuid=pdns
setgid=pdns
local-address=0.0.0.0
local-port=53
# Backend
launch=gpgsql
# PostgreSQL backend
gpgsql-host=localhost
gpgsql-port=5432
gpgsql-dbname=powerdns
gpgsql-user=pdns
gpgsql-password=changeme
# API
api=yes
api-key=your-api-key-here
webserver=yes
webserver-address=0.0.0.0
webserver-port=8081
webserver-allow-from=127.0.0.1,10.0.0.0/8
# DNSSEC
default-soa-content=ns1.example.com hostmaster.@ 0 10800 3600 604800 3600
# Logging
loglevel=4
log-dns-queries=no
log-dns-details=no
# Performance
receiver-threads=4
distributor-threads=4
cache-ttl=60
query-cache-ttl=20
negquery-cache-ttl=60
Database Setup (PostgreSQL)
# Create database
sudo -u postgres createuser pdns
sudo -u postgres createdb -O pdns powerdns
# Initialize schema
sudo -u postgres psql powerdns < /usr/share/doc/pdns-backend-pgsql/schema.pgsql.sql
# Or for MySQL
mysql -u root -p -e "CREATE DATABASE powerdns; GRANT ALL ON powerdns.* TO 'pdns'@'localhost' IDENTIFIED BY 'changeme';"
mysql -u root -p powerdns < /usr/share/doc/pdns-backend-mysql/schema.mysql.sql
Core Commands (pdnsutil)
| Command | Description |
|---|---|
pdnsutil create-zone <domain> | Create a new zone |
pdnsutil delete-zone <domain> | Delete a zone |
pdnsutil list-all-zones | List all zones |
pdnsutil list-zone <domain> | List records in a zone |
pdnsutil add-record <domain> <name> <type> <content> | Add a record |
pdnsutil delete-rrset <domain> <name> <type> | Delete a record set |
pdnsutil edit-zone <domain> | Interactive zone editor |
pdnsutil check-zone <domain> | Validate zone |
pdnsutil check-all-zones | Validate all zones |
pdnsutil rectify-zone <domain> | Fix zone metadata |
pdnsutil secure-zone <domain> | Enable DNSSEC |
pdnsutil show-zone <domain> | Show zone metadata and keys |
pdnsutil increase-serial <domain> | Increment SOA serial |
Zone Management
# Create a zone
pdnsutil create-zone example.com ns1.example.com
# Add records
pdnsutil add-record example.com . A 3600 203.0.113.10
pdnsutil add-record example.com . MX "3600 10 mail.example.com"
pdnsutil add-record example.com www A 3600 203.0.113.10
pdnsutil add-record example.com mail A 3600 203.0.113.20
pdnsutil add-record example.com . NS 3600 ns1.example.com
pdnsutil add-record example.com . NS 3600 ns2.example.com
pdnsutil add-record example.com ns1 A 3600 203.0.113.1
pdnsutil add-record example.com ns2 A 3600 203.0.113.2
pdnsutil add-record example.com . TXT '3600 "v=spf1 include:_spf.google.com ~all"'
pdnsutil add-record example.com _dmarc TXT '3600 "v=DMARC1; p=reject; rua=mailto:dmarc@example.com"'
# Add AAAA record
pdnsutil add-record example.com www AAAA 3600 2001:db8::1
# Delete a record
pdnsutil delete-rrset example.com www A
# Replace all records of a type
pdnsutil replace-rrset example.com www A 3600 203.0.113.11
# List zone contents
pdnsutil list-zone example.com
# Check zone validity
pdnsutil check-zone example.com
REST API
export PDNS_URL="http://localhost:8081"
export PDNS_API_KEY="your-api-key-here"
# List all zones
curl -s -H "X-API-Key: $PDNS_API_KEY" \
"$PDNS_URL/api/v1/servers/localhost/zones" | jq '.[].name'
# Get zone details
curl -s -H "X-API-Key: $PDNS_API_KEY" \
"$PDNS_URL/api/v1/servers/localhost/zones/example.com." | jq
# Create a zone
curl -X POST -H "X-API-Key: $PDNS_API_KEY" \
-H "Content-Type: application/json" \
"$PDNS_URL/api/v1/servers/localhost/zones" \
-d '{
"name": "newdomain.com.",
"kind": "Native",
"nameservers": ["ns1.example.com.", "ns2.example.com."]
}'
# Add/Update records (PATCH)
curl -X PATCH -H "X-API-Key: $PDNS_API_KEY" \
-H "Content-Type: application/json" \
"$PDNS_URL/api/v1/servers/localhost/zones/example.com." \
-d '{
"rrsets": [
{
"name": "app.example.com.",
"type": "A",
"ttl": 300,
"changetype": "REPLACE",
"records": [
{"content": "10.0.1.10", "disabled": false},
{"content": "10.0.1.11", "disabled": false}
]
}
]
}'
# Delete a record set
curl -X PATCH -H "X-API-Key: $PDNS_API_KEY" \
-H "Content-Type: application/json" \
"$PDNS_URL/api/v1/servers/localhost/zones/example.com." \
-d '{
"rrsets": [
{
"name": "old.example.com.",
"type": "A",
"changetype": "DELETE"
}
]
}'
# Notify secondaries
curl -X PUT -H "X-API-Key: $PDNS_API_KEY" \
"$PDNS_URL/api/v1/servers/localhost/zones/example.com./notify"
DNSSEC
# Secure a zone
pdnsutil secure-zone example.com
# Show DNSSEC keys
pdnsutil show-zone example.com
# Export DS records (for registrar)
pdnsutil export-zone-ds example.com
# Key rollover
pdnsutil add-zone-key example.com ksk active 2048
pdnsutil deactivate-zone-key example.com <old-key-id>
# Rectify zone (fix NSEC/NSEC3 chains)
pdnsutil rectify-zone example.com
# Set NSEC3
pdnsutil set-nsec3 example.com '1 0 10 abcd' narrow
# Unset NSEC3 (use NSEC)
pdnsutil unset-nsec3 example.com
Advanced Usage
Zone Transfer (AXFR/IXFR)
# pdns.conf - allow zone transfers
allow-axfr-ips=10.0.0.0/8
also-notify=10.0.0.2,10.0.0.3
# Per-zone metadata
pdnsutil set-meta example.com ALLOW-AXFR-FROM 10.0.0.2/32
pdnsutil set-meta example.com ALSO-NOTIFY 10.0.0.2:53
Lua Records (Dynamic DNS)
# Add Lua record for geographic load balancing
pdnsutil add-record example.com www LUA "A \"ifportup(443, {'10.0.1.1','10.0.2.1','10.0.3.1'})\""
# Weighted responses
pdnsutil add-record example.com www LUA "A \"pickwhashed({10, '10.0.1.1', 5, '10.0.2.1'})\""
# Health-checked failover
pdnsutil add-record example.com api LUA "A \"ifurlup('https://10.0.1.1/health', {'10.0.1.1','10.0.2.1'})\""
Secondary (Slave) Zone
# Create secondary zone
pdnsutil create-secondary-zone example.org 10.0.0.100
# Or via API
curl -X POST -H "X-API-Key: $PDNS_API_KEY" \
-H "Content-Type: application/json" \
"$PDNS_URL/api/v1/servers/localhost/zones" \
-d '{
"name": "example.org.",
"kind": "Slave",
"masters": ["10.0.0.100"]
}'
Troubleshooting
| Issue | Solution |
|---|---|
Unable to launch backend | Check database connection settings in pdns.conf; verify schema is initialized |
| SERVFAIL on queries | Run pdnsutil check-zone <domain>; ensure SOA and NS records exist |
| API returns 401 | Verify api-key in pdns.conf matches your request header |
| DNSSEC validation failing | Run pdnsutil rectify-zone <domain>; verify DS records at registrar |
| Zone transfer not working | Check allow-axfr-ips setting; verify NOTIFY is configured |
| Slow query performance | Enable query cache; check database indexes; increase receiver-threads |
| Lua records not resolving | Ensure Lua backend is compiled in; check Lua syntax in record |