Over the years, I’ve inevitably grown my collection of unneeded domains, domains which I really don’t have much use for, but also don’t have the heart to abandon and let squatters do their thing.
Management has not necessarily become more complex, never quite needing to change anything after initial setup, nor needing anything more complex than an A or MX record. Rather, I’m practically OCD and want a nice birds-eye-view of everything.
To solve this problem, there were a few choices:
terraform/opentofupulumioctodnsdnscontrol
First of all, terraform/opentofu/pulumi were very tempting choices because they meant I could manage my full Cloudflare infrastructure, not only DNS records. However, one downside for a simple setup is that they aren’t stateless, what you see in your configs might not represent what is actually rolled out in the real world.
- All of these need some storage backend either an encrypted state committed to your repo (not the best practice, with the risk of passphrase leaks) or an
S3compatible storage bucket and credentials for that (Cloudflare’sR2was a tempting choice with its generous 10GB free tier. Not to forget mentioning the UI is just way more pretty thanS3which pleases me somewhat every time I need to look at it). - Furthermore, being stateful means its easy for these tools to go out of sync if you don’t respect the “no manual configuration after setting this up” rule. While you can configure it to ignore parts of your infra, I’m one person and I don’t want to spend time resolving these kinds of issues if in the future I lack the needed self-control.
Now on to the stateless tools. The advantage of these is what you see is what you get, there are no unexpected differences or chances for divergence. When these tools run the state is diff’d and synchronised with the configuration.
octodnswas the main contender, but was also quickly rejected after a few local experiments. I am no expert, but while seemingly fully capable, it also at the time seemed that its Cloudflare support was not as well refined. So that left me with only one reasonable choice.
Initial setup of dnscontrol was easy. Assuming you’re wanting to manage DNS records for cloudflare, just create a new CLOUDFLARE_API_TOKEN and a corresponding creds.json to define reading this from the environment, finally create your configuration with dnsconfig.js and run dnscontrol preview
-
Please be careful when you run the following.
dnscontrolassumes it has FULL ownership of your dns management. If any records differ from your current domain configuration, they will be updated or removed when you rundnscontrol push
// creds.json
{
"cloudflare": {
"TYPE": "CLOUDFLAREAPI",
"apitoken": "$CLOUDFLARE_API_TOKEN"
}
}
// dnsconfig.js
var DSP_CLOUDFLARE = NewDnsProvider("cloudflare");
// * "none" means only DNS management, not domain registration.
// * aliases to "none" are for documentation purposes only
var REG_NONE = NewRegistrar("none");
var REG_CLOUDFLARE = REG_NONE;
// configure your domain(s)
D("my.cool.domain", REG_CLOUDFLARE,
DnsProvider(DSP_CLOUDFLARE),
CNAME("www", "michlo.dev.", CF_PROXY_ON),
END);
The last thing you need to do to make this process painless is setup your CI to automate the check/preview/push process. I’m using github actions, so here’s that config. Don’t forget to set your secret for CLOUDFLARE_API_TOKEN in your repo.
name: dnscontrol
on:
pull_request:
push:
branches: [main]
jobs:
dnscontrol:
runs-on: ubuntu-latest
container:
image: ghcr.io/stackexchange/dnscontrol:latest
steps:
- uses: actions/checkout@v6
- run: dnscontrol --version
- env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
run: dnscontrol check
- env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
run: dnscontrol ${{ (github.ref == 'refs/heads/main' && github.event_name == 'push') && 'push' || 'preview' }}
Now you’re all set! Open a new PR with your changes, preview them, merge, and watch it all roll out!