The HTTP API
PowerDNS's HTTP API is how most real deployments manage zones: from automation, a web interface, or tools like Terraform and certbot's DNS plugins. It's a REST API on the built-in web server, authenticated with the X-API-Key header.
In the lab, a single PATCH request adds printer.internal.example.net. Three things are worth noticing in the output:
- The change is live at once. No restart and no cache to wait for, unlike
pdnsutil. - The serial went up by itself, from 2026102101 to 2026102102, because the zone has
SOA-EDIT-APIset toDEFAULT. Without that, API changes leave the serial alone, and secondaries never notice them. - A zone created through the API is served straight away, and gets the correct SOA from
default-soa-content.
Zone kinds: Native, Primary and Secondary
Every zone has a kind, and pdnsutil creates zones as Native by default. Native means "replication is the database's job": PowerDNS sends no NOTIFY and expects every server to share or replicate the same database. That suits a MySQL or PostgreSQL cluster. For DNS-native replication to a secondary with its own database, which is what this lab does, the zones must be Primary on ns1. pdnsutil set-kind changes it.
root@ns1:~# curl -s -X PATCH -H "X-API-Key: lab-only-api-key" -H 'Content-Type: application/json' http://127.0.0.1:8081/api/v1/servers/localhost/zones/internal.example.net. -d '{"rrsets":[{"name":"printer.internal.example.net.","type":"A","ttl":3600,"changetype":"REPLACE","records":[{"content":"10.10.0.30","disabled":false}]}]}' -w 'HTTP %{http_code}\n'
HTTP 204
root@ns1:~# dig @203.0.113.53 printer.internal.example.net +short; dig @203.0.113.53 internal.example.net SOA +short
10.10.0.30
ns1.example.net. hostmaster.example.net. 2026102102 10800 3600 604800 3600
root@ns1:~# curl -s -X POST -H "X-API-Key: lab-only-api-key" -H 'Content-Type: application/json' http://127.0.0.1:8081/api/v1/servers/localhost/zones -d '{"name":"lab.example.net.","kind":"Native","nameservers":["ns1.example.net.","ns2.example.net."],"soa_edit_api":"DEFAULT"}' -o /dev/null -w 'HTTP %{http_code}\n'
HTTP 201
root@ns1:~# dig @203.0.113.53 lab.example.net SOA +short
ns1.example.net. hostmaster.lab.example.net. 2026102101 10800 3600 604800 3600
root@ns1:~# curl -s -H "X-API-Key: lab-only-api-key" http://127.0.0.1:8081/api/v1/servers/localhost/zones | grep -oE '"(name|kind)": *"[^"]+"|"serial": *[0-9]+' | paste - - -
"kind": "Native" "name": "example.net." "serial": 2026102101
"kind": "Native" "name": "internal.example.net." "serial": 2026102102
"kind": "Native" "name": "0.10.10.in-addr.arpa." "serial": 2026102101
"kind": "Native" "name": "lab.example.net." "serial": 2026102101
root@ns1:~# curl -s -X DELETE -H "X-API-Key: lab-only-api-key" http://127.0.0.1:8081/api/v1/servers/localhost/zones/lab.example.net. -w 'HTTP %{http_code}\n'
HTTP 204
root@ns1:~# for z in example.net internal.example.net 0.10.10.in-addr.arpa; do pdnsutil set-kind $z primary; done; pdnsutil list-all-zones primary
example.net
internal.example.net
0.10.10.in-addr.arpa- Lines 1–2: Add a record: HTTP 204, done.
- Lines 5–6: Live at once, and the serial went up by itself (SOA-EDIT-API).
- Lines 8–12: A whole new zone, served immediately, with a correct SOA.
- Lines 15–18: Every zone so far is kind Native.
- Lines 20–21: The test zone removed again.
- Lines 23–26: The real zones become Primary, so ns1 will send NOTIFY for them.