Running Your Own DNS Server: BIND vs PowerDNS
4 min read
## Why Run Your Own DNS Server?
Most organizations delegate Authoritative DNS Server to a Managed DNS provider. The operational overhead is handled by someone else, the infrastructure is global, and the reliability is world-class. So why run your own?
Reasons include: strict data sovereignty requirements (no DNS (Domain Name System) query data touching third-party infrastructure), deep customization of response behavior, cost at very high query volumes, integration with internal systems that set DNS Zone File records programmatically, research, and education. If you are not sure you need it, you probably do not — and a managed provider is the correct choice.
If you do proceed, the two dominant options are BIND 9 and PowerDNS Authoritative Server.
## BIND 9: The Reference Implementation
BIND (Berkeley Internet Name Domain) is maintained by ISC and is the world's most widely deployed DNS server. It is the reference implementation against which the DNS RFCs were tested. BIND 9 handles authoritative service, recursive resolution, and can run both simultaneously (though this is discouraged for security reasons).
**Strengths**: Maximum RFC compliance, vast documentation, decades of production hardening, native DNSSEC with KASP (Key and Signing Policy automation), broad platform support.
**Weaknesses**: Configuration syntax is complex and idiosyncratic. Large zone sets require careful memory tuning. The monolithic architecture can be harder to operate at scale.
### Installing and Configuring BIND 9
```bash
# Ubuntu/Debian
sudo apt install bind9 bind9utils bind9-doc
# Check version
named -v
# Main config
sudo nano /etc/bind/named.conf.local
```
```
// /etc/bind/named.conf.local
zone "example.com" {
type primary;
file "/etc/bind/zones/example.com.zone";
allow-transfer { 203.0.113.54; }; // Secondary NS IP
notify yes;
};
```
```
// /etc/bind/named.conf.options
options {
directory "/var/cache/bind";
// Disable recursion for authoritative-only server
recursion no;
allow-recursion { none; };
// Disable version string disclosure
version "not disclosed";
// Listen on specific interface only
listen-on { 203.0.113.53; };
listen-on-v6 { none; };
// Rate limiting (basic DDoS protection)
rate-limit {
responses-per-second 20;
window 5;
};
};
```
The DNS Zone File lives at `/etc/bind/zones/example.com.zone` in standard RFC 1035 format (see Zone Files: Structure and Management for syntax).
```bash
# Validate zone before loading
named-checkzone example.com /etc/bind/zones/example.com.zone
# Reload after changes
sudo rndc reload example.com
# Check server status
sudo rndc status
```
### BIND DNSSEC with KASP
BIND 9.16+ supports automated key management via KASP:
```
// In named.conf.local, add:
zone "example.com" {
type primary;
file "/etc/bind/zones/example.com.zone";
dnssec-policy default; // Uses BIND's built-in default KASP
inline-signing yes;
key-directory "/etc/bind/keys";
};
```
With `inline-signing yes`, BIND signs the zone in memory without modifying your zone file. Key rollovers happen automatically according to the policy schedule. You still need to extract and submit DS records to your registrar; BIND generates them in `/etc/bind/keys/`.
## PowerDNS Authoritative Server
PowerDNS is developed by Open-Xchange and takes a database-centric approach. Zone data lives in a relational database (PostgreSQL, MySQL, SQLite, or others), not text files. The API-first design makes PowerDNS ideal for programmatic zone management and integration with provisioning systems.
**Strengths**: REST API, database backends (no zone file management at scale), modular backend architecture, excellent DNSSEC support, Lua scripting for custom record logic, active development.
**Weaknesses**: Requires a database; more moving parts. Zone files must be imported. Less familiarity in traditional sysadmin circles.
### Installing PowerDNS
```bash
# Ubuntu 22.04
sudo apt install pdns-server pdns-backend-pgsql
# Create database
sudo -u postgres createdb powerdns
sudo -u postgres psql powerdns < /usr/share/pdns-backend-pgsql/schema/schema.pgsql.sql
```
```
# /etc/powerdns/pdns.conf (excerpt)
launch=gpgsql
gpgsql-host=127.0.0.1
gpgsql-dbname=powerdns
gpgsql-user=pdns
gpgsql-password=secret
# Authoritative only
distributor-threads=3
receiver-threads=2
local-address=0.0.0.0
local-port=53
# Disable recursive queries
recursor=
# Enable API
api=yes
api-key=your-secret-api-key
webserver=yes
webserver-address=127.0.0.1
webserver-port=8081
```
### Managing Zones via PowerDNS API
```bash
# Create a zone
curl -X POST http://localhost:8081/api/v1/servers/localhost/zones \
-H "X-API-Key: your-secret-api-key" \
-H "Content-Type: application/json" \
-d '{
"name": "example.com.",
"kind": "Native",
"nameservers": ["ns1.example.com.", "ns2.example.com."]
}'
# Add a record
curl -X PATCH http://localhost:8081/api/v1/servers/localhost/zones/example.com. \
-H "X-API-Key: your-secret-api-key" \
-H "Content-Type: application/json" \
-d '{
"rrsets": [{
"name": "www.example.com.",
"type": "A",
"ttl": 300,
"changetype": "REPLACE",
"records": [{"content": "203.0.113.1", "disabled": false}]
}]
}'
```
### PowerDNS DNSSEC
```bash
# Enable DNSSEC for a zone
pdnsutil secure-zone example.com
# Generate DS records for submission to registrar
pdnsutil show-zone example.com
# Check DNSSEC state
pdnsutil check-zone example.com
```
## BIND vs PowerDNS: Decision Matrix
| Criterion | BIND 9 | PowerDNS |
|---|---|---|
| Zone storage | Text files | Database |
| API | `rndc` + zone files | Full REST API |
| DNSSEC automation | KASP (9.16+) | Excellent, built-in |
| Programmatic management | Difficult | Native |
| Operational simplicity | Moderate | Requires DB |
| Community documentation | Extensive | Good |
| Best for | Traditional ops, few zones | Many zones, DevOps integration |
## Hardening for Production
Both servers need hardening before production exposure:
```bash
# Run as non-root
# BIND creates 'bind' user; PowerDNS creates 'pdns' user automatically
# Firewall: allow DNS only from specific sources
ufw allow from any to any port 53 proto udp
ufw allow from any to any port 53 proto tcp
ufw allow from 203.0.113.54 to any port 53 proto tcp # Zone transfer from secondary only
# Disable AXFR from arbitrary hosts (BIND)
# allow-transfer { 203.0.113.54; }; # Only secondary NS
# Rate limiting to prevent amplification abuse
# (BIND rate-limit block shown above)
# Monitor with Prometheus
# bind_exporter or PowerDNS built-in Prometheus endpoint (:8082/metrics)
```
Running two nameservers (primary and secondary) with zone transfer is the minimum for reliability. The secondary pulls zone updates via AXFR/IXFR when the primary's SOA Record serial increments. For DNS Propagation and TTL (Time To Live) considerations with self-hosted nameservers, see DNS Performance Optimization at Scale. For adding Anycast DNS to your self-hosted setup, see Anycast DNS: How Global DNS Networks Work.
DNS Record Helper
Related Guides
Advanced DNS & Technical