PowerDNS: Self-Host Authoritative DNS with an API and DNSSEC
Nobody thinks about DNS until they need to automate it. Then you open your registrar's control panel, look for an API, and find either nothing, a rate limit that makes your certificate renewals flaky, or a "premium" tier that costs more than the domain.
That's usually the moment self-hosting authoritative DNS starts to look reasonable. PowerDNS is the mature answer: GPL, written in C++, in production at ISPs and registries for two decades, and built around a database backend and a real HTTP API rather than a text file you edit by hand.
First, Know Which PowerDNS You Want
This trips people up constantly, because the project ships three separate daemons and they do completely different jobs.
| Daemon | What it does |
|---|---|
| Authoritative Server | Answers queries for zones you own. This is the one that replaces your registrar's DNS hosting. |
| Recursor | Resolves queries on behalf of clients. This is Pi-hole and unbound territory, for your LAN. |
| dnsdist | A DNS-aware load balancer that sits in front of either. |
Running your own DNS for domains you own means the Authoritative Server. Everything below is about that.
Version 5.1 landed in June 2026, adding structured logging and RRset comments for the LMDB backend.
Why Bother
Three reasons hold up in practice.
A real API. Records become something your automation writes, not something a human clicks. This is the whole reason most people end up here.
Wildcard certificates that renew themselves. ACME DNS-01 challenges need to write a TXT record on demand. If your registrar has no API, you're doing that by hand every 90 days, which means eventually you forget and something expires on a Sunday.
Split-horizon DNS. Return internal addresses to internal clients and public ones to everyone else, from one zone definition. Useful the moment you have a VPN and internal services behind it.
Turning On the API
The API is off by default, which is the correct default. Three directives in pdns.conf enable it:
api=yes
api-key=your-long-random-key-here
webserver=yes
The built-in webserver binds to 127.0.0.1 on port 8081 unless you change it with webserver-address and webserver-port. Leave that default alone unless you have a specific reason, and use webserver-allow-from to control who can reach it.
Authentication is a static key in an X-API-Key header. Listing your zones:
curl -s -H 'X-API-Key: your-long-random-key-here' \
http://127.0.0.1:8081/api/v1/servers/localhost/zones | jq .
The basepath is /api/v1, and localhost there is PowerDNS's own server ID, not a hostname. Every record operation runs through this endpoint, which is what certbot DNS plugins and Terraform providers talk to.
Do not expose 8081 to the internet. An API that rewrites your DNS is an API that can quietly redirect your mail and reissue your certificates. Keep it on localhost or behind your VPN.
DNSSEC Is One Command, But Check Your Version
PowerDNS has the best DNSSEC story of any self-hostable nameserver. Signing a zone is a single command, and since 4.0 the default is a single ECDSA key (algorithm 13, ECDSAP256SHA256) used as a Combined Signing Key, which is a sensible modern choice you don't have to think about.
Here's the trap. PowerDNS 5.0 restructured pdnsutil from flat commands to subcommands, and most tutorials online still show the old ones:
| Before 5.0 | 5.0 and later |
|---|---|
pdnsutil secure-zone |
pdnsutil zone secure |
pdnsutil show-zone |
pdnsutil zone show |
pdnsutil rectify-all-zones |
pdnsutil zone rectify-all |
After signing, you still have to take the DS record from pdnsutil zone show and paste it into your registrar's panel. That step is outside PowerDNS, and it's where the chain of trust actually gets established. Skip it and you've signed a zone nobody validates.
The Part Everyone Gets Wrong
One nameserver is not enough. DNS expects at least two, and putting both on the same VM gives you the appearance of redundancy with none of the substance. If that box reboots, every domain you host goes dark, not just slow. Run a secondary somewhere else, ideally on a different provider.
Glue records. If your nameservers live inside the zone they serve, ns1.example.com answering for example.com, resolvers hit a chicken-and-egg problem. Your registrar has to publish glue records (the IPs of those nameservers) in the parent zone. Register them at your registrar or nothing resolves.
Port 53 needs TCP too. Plenty of people open UDP and stop. DNSSEC responses are large, and anything that doesn't fit falls back to TCP. Zone transfers to your secondary also use TCP. Open both.
Troubleshooting
dig @your-server example.com returns REFUSED. The zone isn't loaded, or the backend can't be reached. Check pdnsutil zone list-all and your database connection settings.
Works locally, fails from outside. Almost always the firewall or a provider security group missing 53/tcp. Test with dig +tcp explicitly.
DNSSEC validation fails. The DS record at your registrar doesn't match. Regenerate with pdnsutil zone show and compare carefully, including the key tag.
Records change in the database but queries return old data. That's the packet cache. Either wait it out or purge it with pdns_control purge.
Running It
DNS is the definition of infrastructure that has to stay up, and it needs a stable public IP. PowerDNS on Elestio starts at $11/month fully managed, with the MySQL backend, SSL, automated backups, monitoring and updates handled, across Hetzner, DigitalOcean, Vultr, Linode, Scaleway, Netcup, AWS or your own VM. Run your secondary on a different provider and you have real redundancy for less than most registrars charge for API access alone.
Thanks for reading ❤️ See you in the next one 👋