Wait, I can manage DNS config without losing hair??

In my unending quest to move the configuration for everything I manage into version control, I was always annoyed with configuring DNS providers. I run or use some services for domains that often require adding various records beyond the "standard" ones. Some examples are using DNS-01 for ACME stuff, configuring mail handling / verification, adding sub-domains and redirects, and so on. And backing this config up was always a chore. Put your hand up if you've thought to back this up before.

I should say at this point that I tend to pay for DNS providers to implement stuff for me. I assume that I don't have time to run an authoritative server, but I could be wrong...

So... the amount of DNS configuration I end up managing is more than I'd like. Not to mention that entering and saving config from DNS providers is absolutely crap. For the few I've used in the past, they all had different web forms for adding/managing records, and the forms all had limitations around acceptable input and how they were presented. I remember one that required splitting the record into two because of some arbitrary limit on number of characters for an input field. Fun, right?

This led me on a massive hunt a few years ago to find something better. That's when I discovered LuaDNS. This is a DNS provider that load configuration, written in Lua, from your git repository. Do I really need to say more??

Ok fine, here are some reasons why I like it:

Example: Configuring a domain for email service

This is an example of configuring a domain to use migadu for email service:


-- migadu email setup
-- https://admin.migadu.com/domains/109975/dns/instructions
txt(_a, 'hosted-email-verify=aaaaaaaa')
txt(_a, 'v=spf1 include:spf.migadu.com -all')
txt('_dmarc.' .. _a, 'v=DMARC1; p=quarantine;')

mx(_a, 'aspmx1.migadu.com', 0)
mx(_a, 'aspmx2.migadu.com.', 1)

cname('key1._domainkey.' .. _a, 'key1.' .. _a .. '._domainkey.migadu.com.')
cname('key2._domainkey.' .. _a, 'key2.' .. _a .. '._domainkey.migadu.com.')
cname('key3._domainkey.' .. _a, 'key3.' .. _a .. '._domainkey.migadu.com.')

srv('_submissions._tcp.' .. _a, 'smtp.migadu.com', 465)
srv('_imaps._tcp.' .. _a, 'imap.migadu.com', 993)

The set_defaults(_a) at the top is a template I created. It does some configuration that is applicable for all domains, like setting 'www' CNAME <domain>. _a is a placeholder for the domain.

Yes, this config snippet could itself be a template, which I could then use like this in a domain config file: set_migadu(_a).

Version control

LuaDNS can be triggered by a webhook to load configuration from a git repo. This means that all of the Lua config written can be in git, with branches, or whatever you want. And pushed anywhere, triggering automation at LuaDNS to pull and deploy it.


This provider has an API, that many ACME clients support for DNS-01, but I use it too for updating records with changed IP addresses. Yeah, I know many providers have this too, but none(?) of them have the other benefits above.

Oh, there is one more benefit: I can't afford to lose any more hair :D

For anyone (rightfully) wondering: I wasn't paid or anything by LuaDNS for this, I'm just a happy paying customer.