PowerDNS Authoritative Server Installation

PowerDNS Authoritative Server is a high-performance DNS server that stores zone data in relational databases like MySQL and PostgreSQL, making it ideal for dynamic DNS management with API access. This guide covers installing PowerDNS, configuring database backends, managing zones, enabling DNSSEC, and using the REST API.

Prerequisites

  • Ubuntu 22.04/Debian 12 or CentOS/Rocky 9
  • Root or sudo access
  • MySQL 8.0+ or PostgreSQL 14+ installed and running
  • Port 53 (TCP/UDP) open in firewall
  • Port 8081 open for the API (restrict to trusted IPs)

Install PowerDNS

# Ubuntu/Debian
sudo apt update
sudo apt install -y pdns-server pdns-backend-mysql
# Or for PostgreSQL:
# sudo apt install -y pdns-server pdns-backend-pgsql

# CentOS/Rocky - use the official PowerDNS repository
curl -o /etc/yum.repos.d/powerdns-auth-49.repo \
  https://repo.powerdns.com/repo-files/el-auth-49.repo
sudo dnf install -y pdns pdns-backend-mysql
# Or: sudo dnf install -y pdns pdns-backend-postgresql

# Stop the service before configuring
sudo systemctl stop pdns

# Check version
pdns_server --version

Disable systemd-resolved stub listener if it's occupying port 53:

sudo sed -i 's/#DNSStubListener=yes/DNSStubListener=no/' /etc/systemd/resolved.conf
sudo systemctl restart systemd-resolved

Configure the MySQL Backend

Create the PowerDNS database and user:

-- Run as MySQL root
CREATE DATABASE powerdns CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'pdns'@'localhost' IDENTIFIED BY 'StrongPDNSPassword123';
GRANT ALL PRIVILEGES ON powerdns.* TO 'pdns'@'localhost';
FLUSH PRIVILEGES;

Import the PowerDNS schema:

# Ubuntu/Debian schema location
mysql -u pdns -p powerdns < /usr/share/doc/pdns-backend-mysql/schema.mysql.sql

# CentOS/Rocky schema location
mysql -u pdns -p powerdns < /usr/share/doc/pdns/schema.mysql.sql

# Or download from GitHub
curl -s https://raw.githubusercontent.com/PowerDNS/pdns/master/modules/gmysqlbackend/schema.mysql.sql \
  | mysql -u pdns -p powerdns

Configure PowerDNS to use MySQL:

sudo tee /etc/powerdns/pdns.conf << 'EOF'
# Backend
launch=gmysql

gmysql-host=127.0.0.1
gmysql-port=3306
gmysql-dbname=powerdns
gmysql-user=pdns
gmysql-password=StrongPDNSPassword123
gmysql-dnssec=yes

# Server settings
local-address=0.0.0.0
local-port=53
daemon=no

# Performance
cache-ttl=20
negquery-cache-ttl=60
query-cache-ttl=20
max-tcp-connections=20

# Logging
log-dns-queries=no
loglevel=4
EOF

sudo systemctl start pdns
sudo systemctl enable pdns

Configure the PostgreSQL Backend

-- Run as PostgreSQL superuser
CREATE USER pdns WITH PASSWORD 'StrongPDNSPassword123';
CREATE DATABASE powerdns OWNER pdns;
# Import schema
psql -U pdns -d powerdns < /usr/share/doc/pdns-backend-pgsql/schema.pgsql.sql
sudo tee /etc/powerdns/pdns.conf << 'EOF'
launch=gpgsql

gpgsql-host=127.0.0.1
gpgsql-port=5432
gpgsql-dbname=powerdns
gpgsql-user=pdns
gpgsql-password=StrongPDNSPassword123
gpgsql-dnssec=yes

local-address=0.0.0.0
local-port=53
daemon=no
cache-ttl=20
EOF

Managing Zones and Records

PowerDNS stores zones in the database. Use pdnsutil for management:

# Create a new zone
sudo pdnsutil create-zone example.com ns1.example.com

# Add records using pdnsutil
sudo pdnsutil add-record example.com @ A 3600 203.0.113.10
sudo pdnsutil add-record example.com www A 3600 203.0.113.10
sudo pdnsutil add-record example.com mail A 3600 203.0.113.20
sudo pdnsutil add-record example.com @ MX "10 mail.example.com."
sudo pdnsutil add-record example.com @ TXT "v=spf1 mx -all"
sudo pdnsutil add-record example.com ns1 A 3600 192.168.1.10
sudo pdnsutil add-record example.com ns2 A 3600 192.168.1.11

# List zones
sudo pdnsutil list-all-zones

# List records in a zone
sudo pdnsutil list-zone example.com

# Rectify zone (update serial and NSEC records)
sudo pdnsutil rectify-zone example.com

# Check zone for errors
sudo pdnsutil check-zone example.com

# Delete a zone
sudo pdnsutil delete-zone example.com

You can also manage records directly in the database:

-- Insert a zone
INSERT INTO domains (name, type) VALUES ('example.com', 'NATIVE');

-- Insert records (get domain ID first)
SET @domain_id = LAST_INSERT_ID();

INSERT INTO records (domain_id, name, type, content, ttl, prio)
VALUES
  (@domain_id, 'example.com', 'SOA', 'ns1.example.com admin.example.com 2026040401 10800 3600 604800 300', 300, 0),
  (@domain_id, 'example.com', 'NS', 'ns1.example.com', 3600, 0),
  (@domain_id, 'example.com', 'NS', 'ns2.example.com', 3600, 0),
  (@domain_id, 'example.com', 'A', '203.0.113.10', 3600, 0),
  (@domain_id, 'www.example.com', 'A', '203.0.113.10', 3600, 0);

Enable the REST API

PowerDNS has a built-in REST API for zone and record management:

# Add to /etc/powerdns/pdns.conf
sudo tee -a /etc/powerdns/pdns.conf << 'EOF'

# REST API
api=yes
api-key=YourSecureAPIKey123ChangeThis
webserver=yes
webserver-address=127.0.0.1
webserver-port=8081
webserver-allow-from=127.0.0.1,192.168.1.0/24
EOF

sudo systemctl restart pdns

Use the API to manage DNS:

# List zones
curl -s -H "X-API-Key: YourSecureAPIKey123ChangeThis" \
  http://localhost:8081/api/v1/servers/localhost/zones | python3 -m json.tool

# Create a zone
curl -s -X POST -H "X-API-Key: YourSecureAPIKey123ChangeThis" \
  -H "Content-Type: application/json" \
  http://localhost:8081/api/v1/servers/localhost/zones \
  -d '{
    "name": "newdomain.com.",
    "kind": "Native",
    "nameservers": ["ns1.example.com.", "ns2.example.com."]
  }'

# Add a record to an existing zone
curl -s -X PATCH -H "X-API-Key: YourSecureAPIKey123ChangeThis" \
  -H "Content-Type: application/json" \
  "http://localhost:8081/api/v1/servers/localhost/zones/newdomain.com." \
  -d '{
    "rrsets": [{
      "name": "www.newdomain.com.",
      "type": "A",
      "ttl": 3600,
      "changetype": "REPLACE",
      "records": [{"content": "203.0.113.50", "disabled": false}]
    }]
  }'

# Delete a record
curl -s -X PATCH -H "X-API-Key: YourSecureAPIKey123ChangeThis" \
  -H "Content-Type: application/json" \
  "http://localhost:8081/api/v1/servers/localhost/zones/newdomain.com." \
  -d '{"rrsets": [{"name": "www.newdomain.com.", "type": "A", "changetype": "DELETE"}]}'

DNSSEC Configuration

# Enable DNSSEC for a zone
sudo pdnsutil secure-zone example.com

# Check DNSSEC status
sudo pdnsutil show-zone example.com

# Generate a Zone Signing Key (ZSK) and Key Signing Key (KSK)
sudo pdnsutil add-zone-key example.com zsk active 2048 rsasha256
sudo pdnsutil add-zone-key example.com ksk active 2048 rsasha256

# Get DS records to submit to your registrar
sudo pdnsutil export-zone-dnskey example.com

# Rectify the zone after enabling DNSSEC
sudo pdnsutil rectify-zone example.com

# Verify DNSSEC is working
dig +dnssec example.com SOA @localhost

Performance Tuning

# Add to pdns.conf for higher throughput
sudo tee -a /etc/powerdns/pdns.conf << 'EOF'

# Thread and connection pool settings
receiver-threads=4
distributor-threads=4

# Cache tuning (increase for large zones)
cache-ttl=60
negquery-cache-ttl=60
query-cache-ttl=20
max-cache-entries=2000000

# Rate limiting
max-qps-limit=10000
EOF

sudo systemctl restart pdns

Monitor performance:

# Check statistics
pdns_control show '*'

# Key metrics
pdns_control show cache-hits
pdns_control show cache-misses
pdns_control show queries

Troubleshooting

PowerDNS fails to start:

# Check for configuration errors
sudo pdns_server --config-check

# Check logs
journalctl -u pdns -n 100 --no-pager

# Verify port 53 is free
sudo ss -tlnup | grep :53

Database connection failures:

# Test MySQL connection
mysql -u pdns -p -h 127.0.0.1 powerdns -e "SELECT COUNT(*) FROM domains;"

# Check gmysql settings in pdns.conf
sudo pdns_server --config | grep gmysql

Queries returning SERVFAIL:

# Check zone rectify
sudo pdnsutil check-all-zones

# Enable query logging temporarily
pdns_control set log-dns-queries=yes
# Send a query, then:
journalctl -u pdns -n 20 --no-pager
pdns_control set log-dns-queries=no

Conclusion

PowerDNS Authoritative Server provides a database-backed DNS solution that enables programmatic zone management through SQL or a REST API, making it ideal for hosting providers and environments where DNS records change frequently. The DNSSEC support ensures zone integrity, while the flexible backend system allows scaling from single MySQL instances to distributed database clusters. Always use the pdnsutil rectify-zone command after bulk record changes to keep zone metadata consistent.