DNSControl Compiler

Separate your DNSControl configuration into multiple files (e.g. DNS zone files) for readability.

This year, Stack Exchange released DNSControl, written in GoLang, which they describe as, “DNS as Code”. More than that, they’ve also provided a Docker-based version which I use, and have built a tiny wrapper around, DNSControl Compiler.

From the times that I’ve used Portainer for quickly starting and stopping containers, it comes very close to what I’m looking for, but still lacking some granular control that I’m used to. So, I tend to opt for 1 of 2 ways:

  1. Bash script as a wrapper – that I usually save as run.bash
  2. Docker Compose

This little project started out with my standard fashion of creating run.bash, which is the few lines of the Docker command that you see in the script itself. Where I went off the beaten path was trying to figure out how to create separate DNS zone files for DNSControl, and after finding this issue in particular, realizing that it’s not really geared for a conf.d/ setup and/or include / import syntax (yet?).




These commands will set everything up for you in ~/src/devOps/dnscontrol/


mkdir -pv ~/src/devOps/dnscontrol/domains/{business,personal,projects,servers};

touch ~/src/devOps/dnscontrol/{.gitignore,creds.json,global.js};

cat << EOF > ~/src/devOps/dnscontrol/.gitignore

cat << EOF > ~/src/devOps/dnscontrol/global.js
// Registrars
var REG_NONE		=	NewRegistrar('none', 'NONE');	// Null registrar.

// Providers


If you haven’t read DNSControl’s Getting Started, you should. After you understand the basic structure, global.js is how you’ll start the whole process. I’ve used Linode and Digital Ocean as VPS providers for quite some time, and using their DNS solutions were easy to setup. Under the Providers section, I have 2 lines:

  1. var DIGITALOCEAN = NewDnsProvider(“digitalocean”, “DIGITALOCEAN”);
  2. var LINODE = NewDnsProvider(“linode”, “LINODE”);


Using the aforementioned setup, I would have domains/personal/getterman.js, and a basic structure such as:

// Domain :
	"", REG_NONE,
	DnsProvider( DIGITALOCEAN ),
	DnsProvider( LINODE ),

	// Google Suite
	MX( "@", 1, "ASPMX.L.GOOGLE.COM.", TTL(86400) ),
	MX( "@", 5, "ALT1.ASPMX.L.GOOGLE.COM.", TTL(86400) ),
	MX( "@", 5, "ALT2.ASPMX.L.GOOGLE.COM.", TTL(86400) ),
	MX( "@", 10, "ALT3.ASPMX.L.GOOGLE.COM.", TTL(86400) ),
	MX( "@", 10, "ALT4.ASPMX.L.GOOGLE.COM.", TTL(86400) ),
	CNAME( "mail", "", TTL(86400) ),

	// Blog
	CNAME( "thad", "@", TTL(86400) ),

	// FIN
	CNAME( "www", "@", TTL(86400) )

The reason that I have a FIN section, is because I need a closing line without a comma, and www is a common entry for DNS.


Right-click to save dnscc.bash from my Snippets folder at GitLab.


dnscc.bash preview is the default behavior when you run dnscc.bash without any additional arguments.

When you wish to save changes? Execute dnscc.bash push


  1. Using this technique, you can organize your DNS zones into separate paths and/or Git submodules for collaborative teamwork that you can tie to Travis CI.
  2. I’m working on a setup for DDNS (Dynamic DNS) and ideally using RFC 2136, and will cover that when I have it working the way that I’d like. What’s making this take a pinch longer is that I need DDNS client support for:
    • pfSense
    • Ubuntu Server

Did this article save you time or money? I'd love a coffee!

Did you find this useful?
Please share with those who you believe would find this useful too!

Leave a Reply

Your email address will not be published. Required fields are marked *