Managing DNS Zones as Code
Manual DNS edits leave no history and no easy undo. dnscontrol treats zones as code: declarative config, and a dry-run before every change. git brings the history revision.

If you run more than a handful of domains, you know the drill: log into a web console, find the right zone, add a record, hope you didn’t typo a hostname. There’s no history of who changed what, no review before it goes live, and no undo if it breaks production.
That’s exactly the gap dnscontrol and git close.
dnscontrol solves this by treating DNS zones as code: you describe your records declaratively in JavaScript configuration files, commit them to git, and let a tool compute and apply the diff against your actual DNS provider(s). It’s the same idea as infrastructure-as-code applied to DNS specifically.
The dnscontrol model
At the core, dnscontrol works with two commands:
dnscontrol preview
dnscontrol push
dnscontrol preview computes the difference between what’s declared in your config and what’s actually live at the provider, and prints it - without touching anything. dnscontrol push applies that same diff for real. This separation means you always get a dry-run: you can read exactly which records will be added, changed, or removed before anything happens.
Importing an existing zone
Bringing an existing domain under dnscontrol doesn’t mean hand-transcribing every record. dnscontrol can import a zone straight from the current provider and generate the equivalent configuration file automatically, ready to review and commit as the new source of truth.
Reusing repetitive records with macros
Because a dnscontrol config is just JavaScript, you’re not stuck writing every record out inline for every domain. Anything that repeats across zones - nameservers, CAA policy, mail server MX/SRV records, SPF/DMARC boilerplate - can be factored into a plain variable or function, defined once, and reused everywhere.
In this repo’s dnsconfig.js, shared record sets are declared as arrays up top:
var CAA_5OK_DEFAULT = [
CAA_BUILDER({
label: "@",
iodef: "hostmaster@5ok.sk",
issue: ["letsencrypt.org"],
issuewild: ["letsencrypt.org"],
}),
];
var MX_5OK_GW02 = [
MX("@", 5, "mail-gw02.5ok.network."),
MX("@", 10, "fire.5ok.network."),
];
and then dropped straight into a D(...)/D_EXTEND(...) call as if they were individual records, e.g. in config/zones/sk.5ok.js:
D("5ok.sk", REG_NONE,
DnsProvider(DNS_BIND),
DefaultTTL(600),
NS_5OK_HE,
CAA_5OK_DEFAULT,
MX_5OK_GW02,
...
);
The payoff shows up the first time one of these needs to change. When the mail gateway migrates, the CAA-authorized issuer changes, or an SPF/DMARC rule needs tightening, it’s a one-line edit to the shared variable or function - every domain referencing it picks up the change on the next dnscontrol push. Compare that to hunting down and editing the same boilerplate copy-pasted across a dozen zone files, and inevitably missing one.
Why this beats manual edits
A few concrete benefits fall out of putting DNS in git and behind dnscontrol:
- Full change history. Every record change is a git commit.
git logandgit blametell you exactly who changed what, when, and - if commit messages are used well - why. Try getting that out of a typical DNS provider’s web UI. - Review and approval workflows. Because changes are just commits, they can go through the same pull-request review your team already uses for application code. A second person can look at a diff like
+ TXT("@", "v=spf1 ...")and catch a mistake before it’s live, instead of after customers start reporting broken email. - Safe, easy rollback. If a change turns out to be wrong,
git revertgets you back to a known-good state, anddnscontrol pushapplies it. No need to remember what the old value was or dig through provider audit logs (if they even exist). - A diffable dry-run before every change.
dnscontrol previewshows precisely what’s about to change, in a readable format, beforednscontrol pushmakes it real. Manual UI edits give you no equivalent safety net. - Multiple people, one source of truth. Several people can propose DNS changes concurrently via branches/PRs without stepping on each other or silently overwriting one another’s edits in a shared web console.
Conclusion
None of this is DNS-specific magic - it’s the same discipline that makes infrastructure-as-code valuable everywhere else: a single declarative source of truth, versioned history, and a dry-run before every apply. dnscontrol just brings that discipline to a part of the stack that, for a lot of teams, still lives entirely in a web browser tab.