Cases / Public sector
DNS cluster migration
A public-sector organization's BIND-based split-horizon DNS cluster had grown to the point where keeping the internal and external views consistent was an ongoing manual burden. Rebuilt it from scratch on PowerDNS and dnsdist with a hidden primary, a shared SQL backend, and view-aware query routing. Internal-view edits now propagate to the base view automatically.
The problem
The customer is a public-sector organization with a few dozen DNS zones across internal and external views, in a classic split-horizon setup where the same name can resolve differently depending on who is asking.
The existing cluster was BIND-based, with separate server instances for each view. Zone files lived on those servers individually. As the estate grew, the operational burden of keeping the views consistent grew with it. Records added to one view often had to be mirrored into the other; when that step was missed (and it sometimes was), the views drifted apart. There was no single place to look at “what does this zone contain”, and change review was ad-hoc.
Two things had to be true of any replacement:
- One source of truth for shared records. Operators should be able to edit the internal view without having to remember which records also need duplicating into the base or external view.
- A view-aware query path. Clients on different networks should still see the answer appropriate to them. Split-horizon was a real requirement, not something to abandon.
What I did
I designed and built a new DNS cluster from scratch, on PowerDNS and dnsdist with a shared SQL backend.
The shape:
- A shared SQL backend holds all zone data: internal, base, and external views, each as its own zone within the same database.
- A hidden primary PowerDNS instance is the only one exposing the admin API. All zone edits happen there; it never serves queries from the network.
- Multiple PowerDNS instances run on the serving nodes, each bound to a different port, each authoritative for one view. They run against the same shared backend, so anything the primary commits is immediately visible to them.
- Records added to the internal view are projected into the base view within the database, so the base zone contains all the records without an operator having to maintain duplicates. The external view picks up its shared records from the base.
- dnsdist sits in front as the single public-facing endpoint. It inspects each query, primarily by source, and forwards it to the PowerDNS instance on the port for the right view. The PowerDNS instances themselves aren’t network-reachable from clients; only dnsdist is.
Cutover was done as a parallel run. The new cluster was brought up alongside the existing BIND setup, populated with the zone data, and validated against what BIND was serving for an extended period. Once the views matched, traffic was switched over. The old cluster stayed available for rollback until I was confident.
The outcome
Editing DNS records is now a single-place operation. An operator changes a record in the view it belongs to; if it’s shared, propagation into the base view (and through to the external view) is automatic. The drift problem the BIND setup quietly created over years is gone, because the data structure no longer requires duplicate edits to stay consistent.
dnsdist also gives the team room it didn’t have before: routing rules, traffic shaping, rate limiting, and a single front-end logging point are now configuration changes rather than architecture changes.
Tools
PowerDNS · dnsdist · BIND