What's Actually Happening

PowerDNS authoritative server or recursor fails to respond to DNS queries. Clients receive SERVFAIL or timeout errors.

The Error You'll See

```bash $ dig @localhost example.com

;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 12345 ```

Backend error:

bash
Backend error: Unable to connect to database

Refused query:

bash
status: REFUSED

Timeout:

bash
;; connection timed out; no servers could be reached

Why This Happens

  1. 1.Backend failure - Database connection lost
  2. 2.Zone not configured - Domain not in PowerDNS
  3. 3.DNSSEC errors - Signature validation failures
  4. 4.Recursion disabled - Server not configured for recursion
  5. 5.Firewall blocking - Port 53 not accessible
  6. 6.ACL restrictions - Query source not allowed
  7. 7.Resource limits - Memory or file descriptors exhausted

Step 1: Check PowerDNS Status

```bash # Check PowerDNS Authoritative: systemctl status pdns

# Check PowerDNS Recursor: systemctl status pdns-recursor

# Check processes: ps aux | grep pdns

# Check configuration: pdnsutil check-zone example.com

# Check listening ports: netstat -tlnp | grep pdns ss -tlnp | grep pdns

# DNS ports: # 53 - UDP and TCP

# Check logs: journalctl -u pdns -f tail -f /var/log/pdns.log

# Check version: pdns_server --version pdns_recursor --version

# Test config: pdns_server --config 2>&1 | head -20 ```

Step 2: Check Backend Connectivity

```bash # PowerDNS supports multiple backends: # - MySQL/MariaDB # - PostgreSQL # - SQLite # - LDAP # - BIND zone files

# Check configured backend: cat /etc/powerdns/pdns.conf | grep launch launch=gmysql

# MySQL backend: launch=gmysql gmysql-host=127.0.0.1 gmysql-user=pdns gmysql-password=password gmysql-dbname=pdns

# Test MySQL connection: mysql -u pdns -p pdns -h 127.0.0.1

# Check database tables: mysql -u pdns -p pdns -e "SHOW TABLES;"

# Required tables: # domains, records, supermasters, comments, etc.

# PostgreSQL backend: launch=gpgsql gpgsql-host=127.0.0.1 gpgsql-user=pdns gpgsql-password=password gpgsql-dbname=pdns

# Test PostgreSQL: psql -U pdns -h 127.0.0.1 pdns

# SQLite backend: launch=gsqlite3 gsqlite3-database=/var/lib/powerdns/pdns.sqlite3

# Test SQLite: sqlite3 /var/lib/powerdns/pdns.sqlite3 ".tables"

# Check backend logs: journalctl -u pdns | grep -i "backend|database|connection" ```

Step 3: Check Zone Configuration

```bash # List zones: pdnsutil list-all-zones

# Check specific zone: pdnsutil show-zone example.com

# Check zone integrity: pdnsutil check-zone example.com

# Add zone: pdnsutil create-zone example.com

# Add record: pdnsutil add-record example.com www A 192.168.1.10

# List records: pdnsutil list-zone example.com

# Check for zone errors: pdnsutil check-zone example.com 2>&1

# Zone in database: mysql -u pdns -p pdns -e "SELECT * FROM domains WHERE name='example.com';" mysql -u pdns -p pdns -e "SELECT * FROM records WHERE domain_id=1;"

# Native zone: pdnsutil create-zone example.com ns1.example.com

# Master zone: # In pdns.conf: master=yes

# Slave zone: slave=yes

# Check zone transfer: pdnsutil check-zone example.com

# Rectify zone (fixes ordering): pdnsutil rectify-zone example.com ```

Step 4: Check DNSSEC Configuration

```bash # Check DNSSEC status: pdnsutil show-zone example.com | grep DNSSEC

# Secure zone: pdnsutil secure-zone example.com

# Check DNSSEC keys: pdnsutil show-zone example.com

# List keys: pdnsutil list-keys example.com

# Generate new key: pdnsutil generate-zone-key example.com zsk pdnsutil generate-zone-key example.com ksk

# Sign zone: pdnsutil rectify-zone example.com

# Check for DNSSEC errors: pdnsutil check-zone example.com

# Disable DNSSEC for zone: pdnsutil disable-dnssec example.com

# DNSSEC in pdns.conf: dnssec=on

# Check signature validity: dig @localhost example.com DNSKEY +dnssec

# Export DS record: pdnsutil export-zone-dnskey example.com

# Check for expired signatures: pdnsutil check-zone example.com | grep -i expired

# Re-sign zone: pdnsutil rectify-zone example.com ```

Step 5: Check Recursor Configuration

```bash # For PowerDNS Recursor:

# Check recursor config: cat /etc/powerdns/recursor.conf

# Allow recursion: allow-from=0.0.0.0/0 # Or restrict: allow-from=192.168.0.0/16,10.0.0.0/8

# Forward zones: forward-zones=example.com=127.0.0.1:5300

# Forward all: forward-zones-recurse=.=8.8.8.8,8.8.4.4

# Local address: local-address=0.0.0.0 local-port=53

# Check recursion works: dig @localhost google.com

# Test forwarding: dig @localhost example.com

# Check recursor logs: journalctl -u pdns-recursor -f

# Recursor API: # Enable API: webserver=yes webserver-address=127.0.0.1 webserver-port=8081 api-key=secret

# Check API: curl -H "X-API-Key: secret" http://localhost:8081/api/v1/servers/localhost

# DNS64: dns64-prefix=64:ff9b::/96 ```

Step 6: Check ACL and Security

```bash # Check allow-from: grep allow-from /etc/powerdns/pdns.conf

# Allow queries from anywhere: allow-from=0.0.0.0/0,::/0

# Or specific networks: allow-from=192.168.0.0/16,10.0.0.0/8,127.0.0.0/8

# Check allow-axfer-ips: allow-axfer-ips=192.168.1.100,192.168.1.101

# For zone transfers

# Check allow-recursion: allow-recursion=192.168.0.0/16,10.0.0.0/8

# Check trusted-notification-proxy: # For slave zones

# Query local address: local-address=0.0.0.0 local-ipv6=::

# Check query-local-address: query-local-address=0.0.0.0

# Test ACL: dig @server-ip example.com # From allowed and disallowed sources

# Check for ACL rejections: journalctl -u pdns | grep -i "denied|rejected|refused" ```

Step 7: Check Firewall

```bash # Check firewall rules: iptables -L -n | grep 53

# Allow DNS: iptables -I INPUT -p udp --dport 53 -j ACCEPT iptables -I INPUT -p tcp --dport 53 -j ACCEPT

# Using ufw: ufw allow 53/udp ufw allow 53/tcp

# Using firewalld: firewall-cmd --add-service=dns --permanent firewall-cmd --reload

# Test port: nc -zuv localhost 53

# Check if listening: ss -ulnp | grep :53 ss -tlnp | grep :53

# Test from client: dig @server-ip example.com

# Check for packet drops: tcpdump -i any udp port 53

# Allow AXFR (zone transfer): iptables -I INPUT -p tcp --dport 53 -s slave-ip -j ACCEPT ```

Step 8: Check Resource Limits

```bash # Check file descriptors: ulimit -n

# Increase file descriptors: ulimit -n 65535

# In systemd service: [Service] LimitNOFILE=65535

# Check memory: free -m

# Check PowerDNS memory: ps aux | grep pdns | awk '{sum+=$6} END {print sum/1024 " MB"}'

# Check cache size (recursor): # In recursor.conf: max-cache-entries=1000000

# Check distributor-threads: distributor-threads=4 receiver-threads=4

# Check timeout settings: query-local-timeout=20 tcp-timeout=10

# Check for OOM: dmesg | grep -i "out of memory"

# Check CPU usage: top -p $(pgrep pdns)

# Adjust threads for load: threads=4 ```

Step 9: Debug Query Issues

```bash # Enable trace logging: # In pdns.conf: loglevel=6 query-logging=yes

# Check logs: journalctl -u pdns -f

# Query with trace: dig @localhost example.com +trace

# Check specific record type: dig @localhost example.com A dig @localhost example.com MX dig @localhost example.com SOA

# Check authoritative: dig @localhost example.com +nssearch

# Force TCP: dig @localhost example.com +tcp

# Check with DNSSEC: dig @localhost example.com +dnssec

# Test backend query: # Direct database check: mysql -u pdns -p pdns -e "SELECT * FROM records WHERE name='www.example.com';"

# Check query stats: # Using API: curl -H "X-API-Key: secret" http://localhost:8081/api/v1/servers/localhost/statistics

# Check ringbuffer: # In recursor.conf: stats-ringbuffer-entries=10000 ```

Step 10: PowerDNS Verification Script

```bash # Create verification script: cat << 'EOF' > /usr/local/bin/check-powerdns.sh #!/bin/bash

echo "=== PowerDNS Authoritative ===" systemctl status pdns 2>/dev/null | head -5 || echo "pdns service not found"

echo "" echo "=== PowerDNS Recursor ===" systemctl status pdns-recursor 2>/dev/null | head -5 || echo "pdns-recursor service not found"

echo "" echo "=== Processes ===" ps aux | grep pdns | grep -v grep || echo "No PowerDNS processes"

echo "" echo "=== Listening Ports ===" ss -ulnp 2>/dev/null | grep :53 || netstat -ulnp | grep :53 || echo "Not listening on UDP 53" ss -tlnp 2>/dev/null | grep :53 || netstat -tlnp | grep :53 || echo "Not listening on TCP 53"

echo "" echo "=== Configuration ===" cat /etc/powerdns/pdns.conf 2>/dev/null | grep -E "^launch|^gmysql|^gpgsql|^master|^slave" | head -10 || echo "No pdns.conf"

echo "" echo "=== Backend Connection ===" backend=$(grep "^launch" /etc/powerdns/pdns.conf 2>/dev/null | cut -d= -f2) case $backend in gmysql) host=$(grep "^gmysql-host" /etc/powerdns/pdns.conf 2>/dev/null | cut -d= -f2) db=$(grep "^gmysql-dbname" /etc/powerdns/pdns.conf 2>/dev/null | cut -d= -f2) echo "MySQL backend: $host/$db" mysql -h $host -u pdns -p -e "SELECT 1" 2>/dev/null && echo "Connection OK" || echo "Connection FAILED" ;; gpgsql) echo "PostgreSQL backend" ;; gsqlite3) db=$(grep "^gsqlite3-database" /etc/powerdns/pdns.conf 2>/dev/null | cut -d= -f2) echo "SQLite backend: $db" [ -f "$db" ] && echo "Database exists" || echo "Database NOT FOUND" ;; esac

echo "" echo "=== Zones ===" pdnsutil list-all-zones 2>/dev/null | head -10 || echo "Cannot list zones"

echo "" echo "=== Zone Check ===" for zone in $(pdnsutil list-all-zones 2>/dev/null | head -5); do echo "Checking $zone:" pdnsutil check-zone $zone 2>&1 | tail -2 done

echo "" echo "=== DNSSEC Status ===" pdnsutil list-keys 2>/dev/null | head -5 || echo "No DNSSEC keys"

echo "" echo "=== Recursor Test ===" dig @localhost google.com +short 2>/dev/null | head -3 || echo "Recursion test failed"

echo "" echo "=== Authoritative Test ===" dig @localhost localhost +short 2>/dev/null || echo "Local zone test failed"

echo "" echo "=== Firewall ===" iptables -L -n 2>/dev/null | grep 53 || ufw status 2>/dev/null | grep 53 || echo "Check firewall manually"

echo "" echo "=== Recent Logs ===" journalctl -u pdns --no-pager -n 10 2>/dev/null || echo "No systemd logs"

echo "" echo "=== Recommendations ===" echo "1. Verify backend database is running and accessible" echo "2. Check zone integrity with pdnsutil check-zone" echo "3. Ensure DNSSEC keys are valid if enabled" echo "4. Configure allow-from ACL properly" echo "5. Allow port 53 UDP/TCP in firewall" echo "6. Check recursion settings for recursor" echo "7. Review logs for specific errors" EOF

chmod +x /usr/local/bin/check-powerdns.sh

# Usage: /usr/local/bin/check-powerdns.sh ```

PowerDNS Query Checklist

CheckExpected
Service runningpdns process active
Backend connectedDatabase accessible
Zones configuredDomains in PowerDNS
DNSSEC validKeys not expired
ACL configuredallow-from set correctly
Firewall openPort 53 UDP/TCP allowed
RecursionEnabled for recursor

Verify the Fix

```bash # After fixing PowerDNS query

# 1. Check service systemctl status pdns // Active running

# 2. Check zones pdnsutil list-all-zones // Zones listed

# 3. Test zone dig @localhost example.com // Returns A record

# 4. Check recursion dig @localhost google.com // Returns answer

# 5. Check DNSSEC dig @localhost example.com DNSKEY // Returns DNSKEY

# 6. Monitor logs journalctl -u pdns -f // No errors ```

  • [Fix BIND DNS Resolution Failed](/articles/fix-bind-dns-resolution-failed)
  • [Fix DNS Server Not Responding](/articles/fix-dns-server-not-responding)
  • [Fix DNS Propagation Issues](/articles/fix-dns-propagation-issues)