Skip to main content

Documenting my DNS records

I had to change some of my DNS records recently, a phrase which strikes fear into the heart of sysadmins everywhere. It all went fine, but I definitely felt like I was playing with fire.

My current domain registrar is Hover, and the only way I can manage my domains is through a web dashboard on their website:

Screenshot of a web dashboard. There's a table of records, one record per row, and an edit button on the right-hand side.

It’s easy to make a one-off change in this dashboard, but it’s harder to manage a set of DNS records over a long period. There are two big things it’s missing:

One way to get both of these would be to use an infrastructure-as-code (IaC) tool to manage my DNS records, which is how I’ve managed DNS records at multiple jobs. I could define my DNS records in code, add inline comments, and track changes in Git.

Unfortunately there are no IaC tools for Hover – it doesn’t even have a public API – so that approach is out. (If I was starting from scratch, one of the reasons I’d pick a different domain registrar is so I could use a proper IaC tool.) I could migrate my domains to another service, but that’s a big change and I’m a bit nervous doing that without any sort of safety net.

However, I’ve still found a way to add documentation and change history to my existing setup. This adds a safety net that makes me feel more comfortable making changes, and opens the door to me moving my domains elsewhere.

Getting a snapshot of my existing DNS records

This project started when I learnt about Alex Dalitz’s gem dnsruby, which lets you list DNS records in Ruby. Here’s a simple example:

require 'dnsruby'  # dnsruby (1.72.1)

dns =
records = dns.getresources('', 'TXT')

# "v=spf1 ?all"
# "ahrefs-site-verification_c8470a858a715b78845c1b81e2dc2f7aa8b367ced4cd8d342a3986a33a03b84c"
# "google-site-verification=o3zoiEGC6aLEgPMKiyWHZcRZrutF6wHQjKqhkRvgWiQ"

You have to know exactly which domain name and record type you want to query – I don’t think there’s an easy way to get all the DNS records for a particular domain, especially if you want to include all the subdomains. This is a limitation of DNS, not the dnsruby gem.

But that’s not an issue for me, because I know what subdomains and record types I’m using – I can read them out of my web dashboard. By iterating over the possible domains and record types, I wrote a script that gets all my DNS records and saves them to a YAML file:

require 'date'
require 'yaml'

require 'dnsruby'

def get_dns_records(domain, record_type)
  dns =
  records = dns.getresources(domain, record_type)

domains_to_check = {
  ''        => ['NS', 'MX', 'A', 'TXT'],
  ''  => ['CNAME'],
  '' => ['CNAME'],
  # ...and several other domains and subdomains

dns_records =
    .flat_map do |domain, record_types| do |rt|
        [domain, rt, get_dns_records(domain, rt)]

now ='%Y-%m-%d.%H-%M-%S')
    .to_h { |domain, rt, resources| ["#{domain} #{rt}", resources] }

puts "dns_records.#{now}.yml"

Here’s a little snippet of the YAML it produces:

--- NS:
- MX:
- 20
- 10 A:

This is already an improvement on what I had before – if I run this script on a schedule, I’ll have snapshots of what my DNS looked like on a particular date. I could use that to construct an edit history, and it would make it easier for me to revert a bad change. If I make a change and break something, I can look at a previous snapshot to see what working configuration I should re-apply.

And now I have my DNS records in a plaintext file, I can add comments.

Adding documentation to my DNS snapshots

I started rearranging one of these YAML snapshots, grouping similar records from different domains and adding comments to explain what they’re for. For example, I can add a comment to remind me where the IP address comes from:

# == Netlify DNS records ==
# These are DNS records that allow me to use my own domains with my sites
# hosted on Netlify.
# See A:   [ "", ] A:   [ "", ] A: [ "", ]

I went through the snapshot and added a comment for every DNS record – now I know why created each record. It took a while, but now I have a much better understanding of what my DNS is doing, and what’s safe to change in the future. You can read the fully-commented file on GitHub. This file is now the canonical statement of what my DNS records should be.

I wrote a second script that can compare two YAML snapshots: do my live DNS records match this canonical statement?

require 'yaml'

expected_records = YAML.load_file(ARGV[0])
actual_records   = YAML.load_file(ARGV[1])

if expected_records == actual_records
  puts 'The DNS records match 🥳'
  exit 0
  puts "The DNS records don't match! 😱"

  (expected_records.keys + actual_records.keys).uniq.each do |k|
    next unless expected_records[k] != actual_records[k]

    puts "#{k}:"
    puts " - expected: #{expected_records[k].inspect}"
    puts " - actual:   #{actual_records[k].inspect}"

  exit 1

Here’s the output:

$ ruby compare_dns_records.rb dns_records.yml dns_records.good.yml
The DNS records match 🥳

$ ruby compare_dns_records.rb dns_records.yml dns_records.bad.yml
The DNS records don't match! 😱 A:
 - expected: [""]
 - actual:   [""]

In the first case, all my DNS records are configured correctly. In the second case, I’ve typo’d 75 as 57 – now I know that I need to go and fix something in my Hover dashboard.

It can’t actually fix the mistake, only tell me that something’s wrong – but this is much better than what I had before.

An infrastructure-as-code future

I’m going to leave my DNS records in Hover for now, but these scripts have also give me ideas for how I might migrate out of Hover, if I ever decide to do so. One of the tricky parts is replicating all my existing DNS records in a new service – how do I know I’ve done that correctly?

Fortunately, dnsruby is very flexible. Currently the nameserver for is at Hover, and their nameserver is When you do a DNS lookup for my domain, it asks for the DNS records.

But I can tell dnsruby to ignore that, and to ask Linode’s nameserver instead:

require 'dnsruby'

dns ={:nameserver => [""]})
records = dns.getresources('', 'TXT')
# []

I feel like this could give me more reassurance when I copy DNS records between providers. First, I copy my existing DNS records into the new provider. Then, I use dnsruby to get snapshots of the DNS records being served by my old/new provider’s nameservers. Finally, I compare the two snapshots to check they match.

Crucially, I could do this before I switch the domain to the new provider’s nameservers. This gives me time to test, to iterate, to fix silly mistakes, and I can do so at a relaxed pace without worrying if my site/email are down.


You can see the complete code on GitHub.

These two scripts allow me to do regular checks of my DNS. I have them set to run as a daily job in GitHub Actions. First, I create a snapshot of my live DNS records. Then, I compare those records to the canonical statement of what I expect my DNS to be. If the two have diverged, the job will fail and I’ll get an alert, and I’ll go to investigate.

I can also run the check on demand, if I’m actively making changes.

This doesn’t change anything in Hover or the way I manage my DNS records, but it’s done wonders for my peace of mind. I now have some written documentation about all of my DNS records are for, and I have an edit history so I can easily revert any breaking changes.