How rtpdns works
rtpdns is built from two widely used open-source DNS servers, wired together so that a change made in one place reaches every nameserver within seconds. Here's how the pieces fit, what we learned building it, and what it costs to run.
Two kinds of server
A hidden primary holds every zone. It's PowerDNS Authoritative with a Postgres database: it signs zones with DNSSEC, takes changes through its API, and copies in customers' zones for secondary DNS. It's hidden because no NS record points at it, so resolvers never query it.
Public nameservers answer every query. They run Knot DNS, developed by CZ.NIC, the registry for .cz domains. They have no configuration of their own for customer zones: they copy everything from the primary.
customer's DNS server (secondary DNS only)
│ zone transfer
▼
hidden primary PowerDNS + Postgres, signs zones
│ NOTIFY + zone transfers, signed with TSIG
├────────────► ns1.rtpdns.com Knot DNS
└────────────► ns2.rtpdns.com Knot DNS
▲
│ queries
resolvers on the internet
Zones appear everywhere on their own
Adding every new zone to every nameserver's configuration is the classic chore of running DNS. Instead, the primary publishes a catalog zone (RFC 9432): a special zone whose records list all the other zones. Each nameserver transfers the catalog and serves whatever it lists. Create a zone on the primary and it joins the catalog. The primary sends a NOTIFY, and within seconds every nameserver has transferred the new zone and is answering for it. Delete a zone, and it disappears everywhere the same way.
Two things tripped us up:
- A catalog's serial number must start as a Unix timestamp. PowerDNS sets the catalog's serial to the current time whenever its member list changes. A catalog created with a date-style serial such as 2026100501, which is the larger number, makes every later update look older, and the nameservers ignore it.
- PowerDNS's catalogs list the zones it's the primary for. To list the zones it copies from customers' servers too, we override two of the database queries it uses to build them.
Keys that can't collide, zones that can't hijack
Every zone transfer is signed with TSIG. The nameservers share one key with the primary. Secondary DNS customers bring their own, and because PowerDNS keeps all keys in one list, a customer's key must be named under their own zone, like xfr.example.com. Two customers can never pick the same name, so nobody can replace someone else's key.
The tool that creates zones also refuses any zone that could take over someone else's names: top-level domains, our own domain, and zones inside or above an existing zone. Every nameserver serves every zone and answers from the most specific one, so a stranger's www.example.com zone would otherwise hijack example.com's website.
A primary that only listens for NOTIFY
For secondary DNS, customers' servers have to reach the primary: they send it a NOTIFY when a zone changes, and allow it to transfer the zone. So the primary has its own public address, xfr.rtpdns.com. But PowerDNS has no response rate limiting, and an authoritative server that answers anyone can be used to amplify attacks on third parties. So from the internet, the firewall lets through exactly one kind of packet: a DNS NOTIFY over UDP. It reads the opcode straight out of the DNS header with the u32 match in iptables:
iptables -A rtpdns-notify-only -p udp \
-m u32 --u32 "0>>22&0x3C@8>>11&0xF=4" -j RETURN
Everything else from outside is dropped. The primary's own SOA checks and zone transfers leave from the same address, so customers only have to allow one IP.
DNSSEC
Managed zones are signed with ECDSA P-256 (algorithm 13) and NSEC3. Only the primary holds signing keys; the nameservers serve the signed zones as they receive them. Signatures are valid for three weeks and refreshed weekly, so signed zones would keep validating for at least a week even if the primary went down.
Counting queries
Pricing depends on zones and queries, so every nameserver counts queries per zone with Knot's statistics module. Response rate limiting (RRL) handles floods: in our lab, a burst of 1,000 identical queries from one address got about 120 full answers, a similar number of truncated answers telling the client to retry over TCP, and the rest were dropped. The counts include rate-limited queries, so attack traffic has to be filtered out before it reaches an invoice.
Nameservers anywhere
A nameserver at another provider doesn't need access to our private network. It connects out to the primary's server over WireGuard and reaches the primary through the tunnel, so it can sit behind NAT or change address. Each server makes its own key pair, only public keys are exchanged, and the tunnel carries nothing but DNS. That's how we'll add locations.
Testing
A Docker lab runs the same configuration as production: the primary, four nameservers (one on a separate network, reachable only through the WireGuard tunnel) and a stand-in customer DNS server. Fourteen end-to-end tests check, among other things, that new zones, changes and deletions reach every nameserver, that DNSSEC validates, that secondary DNS follows a customer's edits, and that query counts are exact. Further checks confirm that the primary answers only NOTIFY from untrusted addresses, that the tunnel carries only DNS, and that the nameservers keep answering when the primary is stopped.
What it costs
Right now, nothing. The two nameservers are Oracle Cloud Always Free virtual machines in Ashburn, Virginia, each with 1 GB of memory, and the primary shares the first one. This website is on GitHub Pages, and its contact form uses Web3Forms' free plan.
That's also the main limitation: both nameservers are in the same Oracle data center. A nameserver on another network costs about $4 to $6 a month, and with the tunnel above, adding one takes a few commands.