Managing DNS with DNSControl
Learn how to manage your DNS zones and records as code using DNSControl with the DNScale provider. Define your entire DNS configuration in JavaScript.
DNSControl lets you manage DNS with a JavaScript-based configuration file. You define your zones and records in dnsconfig.js, preview changes with dnscontrol preview, and apply them with dnscontrol push. The DNScale provider integrates directly with the DNScale API, giving you full control over your DNS infrastructure as code.
Prerequisites
Before you begin, ensure you have:
- DNSControl installed (version 4.0 or later) - Install DNSControl
- A DNScale account with an API key - Get your API key
- Basic familiarity with JavaScript syntax
Setup
DNSControl uses a creds.json file for provider credentials. Create this file in your project directory:
{
"dnscale": {
"TYPE": "DNSCALE",
"api_key": "your-api-key-here"
}
}The provider connects to https://api.dnscale.eu/v1 by default. To use a custom API endpoint, add api_url:
{
"dnscale": {
"TYPE": "DNSCALE",
"api_key": "your-api-key-here",
"api_url": "https://custom-api.example.com/v1"
}
}Keep creds.json out of version control. Add it to your .gitignore:
echo "creds.json" >> .gitignoreCreating Your First Zone
Create a dnsconfig.js file with a basic zone definition:
var REG_NONE = NewRegistrar("none");
var DSP_DNSCALE = NewDnsProvider("dnscale");
D("example.com", REG_NONE, DnsProvider(DSP_DNSCALE),
A("@", "192.0.2.1"),
A("www", "192.0.2.1")
);This configuration:
- Declares the DNScale provider using credentials from
creds.json - Creates a zone for
example.com - Adds two A records: one at the apex (
@) and one forwww
When you run dnscontrol push, DNScale automatically creates the zone if it doesn't exist yet.
Managing DNS Records
A Records
Point hostnames to IPv4 addresses:
D("example.com", REG_NONE, DnsProvider(DSP_DNSCALE),
A("@", "192.0.2.1"),
A("www", "192.0.2.1"),
A("api", "192.0.2.10", TTL(300))
);AAAA Records
Add IPv6 support:
D("example.com", REG_NONE, DnsProvider(DSP_DNSCALE),
AAAA("@", "2001:db8::1"),
AAAA("www", "2001:db8::1")
);CNAME Records
Create aliases for subdomains:
D("example.com", REG_NONE, DnsProvider(DSP_DNSCALE),
CNAME("blog", "example.github.io."),
CNAME("docs", "readthedocs.io.")
);ALIAS Records
Use ALIAS for apex-level CNAME-like behavior:
D("example.com", REG_NONE, DnsProvider(DSP_DNSCALE),
ALIAS("@", "loadbalancer.example.net.")
);MX Records
Configure email delivery:
D("example.com", REG_NONE, DnsProvider(DSP_DNSCALE),
MX("@", 10, "mail.example.com."),
MX("@", 20, "mail2.example.com.")
);TXT Records
Add SPF, DKIM, or verification records:
D("example.com", REG_NONE, DnsProvider(DSP_DNSCALE),
TXT("@", "v=spf1 include:_spf.google.com ~all"),
TXT("@", "google-site-verification=abc123"),
TXT("_dmarc", "v=DMARC1; p=reject; rua=mailto:dmarc@example.com")
);CAA Records
Control which certificate authorities can issue certificates for your domain:
D("example.com", REG_NONE, DnsProvider(DSP_DNSCALE),
CAA("@", "issue", "letsencrypt.org"),
CAA("@", "issuewild", "letsencrypt.org"),
CAA("@", "iodef", "mailto:security@example.com")
);SRV Records
Define service locations:
D("example.com", REG_NONE, DnsProvider(DSP_DNSCALE),
SRV("_sip._tcp", 10, 60, 5060, "sip.example.com.")
);PTR Records
Create reverse DNS entries:
D("example.com", REG_NONE, DnsProvider(DSP_DNSCALE),
PTR("1", "server.example.com.")
);SSHFP Records
Publish SSH host key fingerprints:
D("example.com", REG_NONE, DnsProvider(DSP_DNSCALE),
SSHFP("@", 1, 1, "d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3")
);TLSA Records
Associate TLS certificates with domain names:
D("example.com", REG_NONE, DnsProvider(DSP_DNSCALE),
TLSA("_443._tcp", 3, 1, 1, "abc123def456...")
);HTTPS and SVCB Records
Configure service binding for modern HTTPS connections:
D("example.com", REG_NONE, DnsProvider(DSP_DNSCALE),
HTTPS("@", 1, ".", "alpn=h2,h3"),
SVCB("_dns.resolver", 1, "dns.example.com.", "alpn=dot")
);Advanced Configuration
Multiple Domains
Manage several domains in one configuration file:
var REG_NONE = NewRegistrar("none");
var DSP_DNSCALE = NewDnsProvider("dnscale");
D("example.com", REG_NONE, DnsProvider(DSP_DNSCALE),
A("@", "192.0.2.1"),
A("www", "192.0.2.1"),
MX("@", 10, "mail.example.com.")
);
D("example.org", REG_NONE, DnsProvider(DSP_DNSCALE),
A("@", "192.0.2.2"),
CNAME("www", "example.org.")
);
D("example.net", REG_NONE, DnsProvider(DSP_DNSCALE),
A("@", "192.0.2.3"),
CNAME("www", "example.net.")
);Using Variables
Since dnsconfig.js is JavaScript, you can use variables to reduce repetition:
var REG_NONE = NewRegistrar("none");
var DSP_DNSCALE = NewDnsProvider("dnscale");
var WEBSERVER_IP = "192.0.2.1";
var MAIL_SERVERS = [
MX("@", 10, "mx1.mailprovider.com."),
MX("@", 20, "mx2.mailprovider.com.")
];
var SPF = TXT("@", "v=spf1 include:_spf.mailprovider.com ~all");
D("example.com", REG_NONE, DnsProvider(DSP_DNSCALE),
A("@", WEBSERVER_IP),
A("www", WEBSERVER_IP),
MAIL_SERVERS,
SPF
);
D("example.org", REG_NONE, DnsProvider(DSP_DNSCALE),
A("@", WEBSERVER_IP),
A("www", WEBSERVER_IP),
MAIL_SERVERS,
SPF
);Macros with Functions
Create reusable record patterns using JavaScript functions:
function WEBSITE(ip) {
return [
A("@", ip),
A("www", ip),
AAAA("@", "2001:db8::1"),
AAAA("www", "2001:db8::1")
];
}
function GOOGLE_WORKSPACE() {
return [
MX("@", 1, "aspmx.l.google.com."),
MX("@", 5, "alt1.aspmx.l.google.com."),
MX("@", 5, "alt2.aspmx.l.google.com."),
TXT("@", "v=spf1 include:_spf.google.com ~all")
];
}
D("example.com", REG_NONE, DnsProvider(DSP_DNSCALE),
WEBSITE("192.0.2.1"),
GOOGLE_WORKSPACE()
);Preview and Push Workflow
DNSControl uses a two-step workflow: preview your changes first, then apply them.
Preview Changes
dnscontrol previewThis connects to the DNScale API, compares your dnsconfig.js with the current live records, and shows what would change:
******************** Domain: example.com
1 correction (dnscale)
#1: CREATE A www.example.com 192.0.2.1 ttl=3600Apply Changes
Once you're satisfied with the preview, apply the changes:
dnscontrol push******************** Domain: example.com
1 correction (dnscale)
#1: CREATE A www.example.com 192.0.2.1 ttl=3600
Done. 1 correction.Best Practices
Version Control Your Configuration
Keep dnsconfig.js in Git to track every DNS change:
git init
echo "creds.json" >> .gitignore
git add dnsconfig.js .gitignore
git commit -m "Initial DNS configuration"Always Preview Before Pushing
Run dnscontrol preview before every dnscontrol push. This catches mistakes before they affect live DNS.
Use CI/CD for Automated Deployments
Add DNSControl to your CI/CD pipeline. Run preview on pull requests and push on merge to main:
# .github/workflows/dns.yml
name: DNS
on:
pull_request:
paths: ["dnsconfig.js"]
push:
branches: [main]
paths: ["dnsconfig.js"]
jobs:
dns:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install DNSControl
run: |
curl -sL https://github.com/StackExchange/dnscontrol/releases/latest/download/dnscontrol-Linux -o dnscontrol
chmod +x dnscontrol
- name: Preview
if: github.event_name == 'pull_request'
run: ./dnscontrol preview
env:
DNSCALE_API_KEY: ${{ secrets.DNSCALE_API_KEY }}
- name: Push
if: github.ref == 'refs/heads/main'
run: ./dnscontrol push
env:
DNSCALE_API_KEY: ${{ secrets.DNSCALE_API_KEY }}Keep Credentials Secure
Never commit creds.json. Use environment variables or a secrets manager in CI/CD.
Complete Example
Here's a full dnsconfig.js for a typical website with email:
var REG_NONE = NewRegistrar("none");
var DSP_DNSCALE = NewDnsProvider("dnscale");
var SERVER_IP = "203.0.113.50";
D("mywebsite.com", REG_NONE, DnsProvider(DSP_DNSCALE),
// Website
A("@", SERVER_IP),
AAAA("@", "2001:db8::50"),
CNAME("www", "mywebsite.com."),
// API subdomain
A("api", "203.0.113.51", TTL(300)),
// Email (Google Workspace)
MX("@", 1, "aspmx.l.google.com."),
MX("@", 5, "alt1.aspmx.l.google.com."),
MX("@", 5, "alt2.aspmx.l.google.com."),
MX("@", 10, "alt3.aspmx.l.google.com."),
MX("@", 10, "alt4.aspmx.l.google.com."),
// Email security
TXT("@", "v=spf1 include:_spf.google.com ~all"),
TXT("_dmarc", "v=DMARC1; p=reject; rua=mailto:dmarc@mywebsite.com"),
// Certificate authority authorization
CAA("@", "issue", "letsencrypt.org"),
CAA("@", "issuewild", "letsencrypt.org"),
// HTTPS service binding
HTTPS("@", 1, ".", "alpn=h2,h3")
);Next Steps
- Read the DNSControl documentation for the full configuration reference
- Learn about DNS Record Types supported by DNScale
- Understand DNS Zones and how they work
- Explore Zone Import Methods to migrate existing DNS
- Set up Email Security with SPF, DKIM, and DMARC
Conclusion
DNSControl brings DNS-as-code to your workflow with a straightforward JavaScript configuration. Combined with the DNScale provider, you get version-controlled DNS changes, safe preview-before-push workflows, and the ability to manage multiple domains from a single file. Whether you're managing one domain or hundreds, DNSControl provides the tooling to do it reliably and at scale.
dnscontrol push your DNS today.