Multi-Provider DNS with Terraform & DNSControl
Deploy DNS zones across DNScale and Hetzner DNS for redundancy using Terraform or DNSControl.
Answer snapshot
Multi-provider DNS reduces the chance that one authoritative DNS provider outage makes your zone unreachable. Define the zone once in Terraform or DNSControl, push identical records to two independent providers (DNScale + Hetzner DNS, for example), then list both providers' nameservers at your registrar. Resolvers can query either provider and usually retry another nameserver on timeout. Cost is one extra provider subscription; benefit is reduced provider-level risk when records, DNSSEC, and monitoring stay in sync.
What you'll learn
- Understand which DNS single points of failure multi-provider DNS reduces
- Deploy identical DNS zones to DNScale and Hetzner DNS using Terraform or DNSControl
- Automate record synchronization through CI/CD pipelines with drift detection
- Configure NS delegation at your registrar for dual-provider resolution
Running your DNS through a single provider leaves you exposed to that provider's authoritative network and control-plane failures. If that provider goes down — whether from an outage, a DDoS attack, or a misconfiguration — your domains can become unreachable. Multi-provider DNS reduces that risk by serving the same zone from two independent providers simultaneously.
This guide walks through setting up DNScale and Hetzner DNS as dual providers using either Terraform or DNSControl. Both tools let you define records once and push them to both providers, keeping everything in sync without manual duplication.
Why Multi-Provider DNS
DNS is the foundation of every internet service you run. A provider outage doesn't just mean your website is unreachable — it means email stops flowing, MX records become unresolvable, APIs become inaccessible, and services that depend on SRV or TXT records break.
Multi-provider DNS works by delegating your domain to nameservers from both providers. Resolvers worldwide can query either provider, and many will retry another listed nameserver when one times out. To understand how resolvers choose between nameservers, see What is DNS?.
When does multi-provider make sense?
- Production domains where downtime has real cost
- Compliance environments that require redundancy
- Global services where provider coverage varies by region — see Global DNS Resolution Balancing
When is it overkill? For development domains, internal tooling, or domains where a few minutes of downtime is acceptable, a single provider with good uptime is usually enough.
How It Works
The concept is straightforward:
- Define your records once in Terraform or DNSControl
- Push identical records to both DNScale and Hetzner DNS
- Set NS records at your registrar pointing to nameservers from both providers
- Resolvers query either provider — resolver-specific nameserver selection decides which answer is used
┌──────────────┐
│ Registrar │
│ │
│ NS records: │
│ ns1.dnscale │
│ ns2.dnscale │
│ hydrogen.ns │ ← Hetzner
│ oxygen.ns │ ← Hetzner
└──────┬───────┘
│
▼
┌──────────────┐ ┌──────────────┐
│ DNScale │ │ Hetzner DNS │
│ │ │ │
│ example.com │ │ example.com │
│ A, MX, TXT │ │ A, MX, TXT │
│ (identical) │ │ (identical) │
└──────────────┘ └──────────────┘
▲ ▲
│ ┌──────────┐ │
└────│ Resolver │────┘
└──────────┘
Queries either oneBoth providers hold the same records. Your IaC tool is the single source of truth, and both providers are just mirrors of that truth.
Prerequisites
You'll need:
- A DNScale account with an API key — Get your API key
- A Hetzner DNS account with an API token — Hetzner DNS Console
- Terraform (v1.0+) or DNSControl (v4.0+) installed
- A domain you control with access to set NS records at the registrar
Terraform: Multi-Provider Setup
Terraform makes multi-provider DNS clean with its native support for multiple providers and for_each loops. You define your records once in a locals block and create them in both providers.
Provider Configuration
# providers.tf
terraform {
required_providers {
dnscale = {
source = "dnscaleou/dnscale"
version = "~> 1.0"
}
hetznerdns = {
source = "timohirt/hetznerdns"
version = "~> 2.2"
}
}
}
provider "dnscale" {
api_key = var.dnscale_api_key
}
provider "hetznerdns" {
apitoken = var.hetzner_dns_token
}# variables.tf
variable "dnscale_api_key" {
description = "DNScale API key"
type = string
sensitive = true
}
variable "hetzner_dns_token" {
description = "Hetzner DNS API token"
type = string
sensitive = true
}
variable "domain" {
description = "Domain to manage"
type = string
default = "example.com"
}Define Records Once
The key to multi-provider DNS with Terraform is defining your records in a single place. Use locals to create a shared record set that covers all your DNS record types:
# records.tf
locals {
dns_records = {
# A records
"root-a" = {
name = "@"
type = "A"
value = "203.0.113.10"
ttl = 3600
}
"www-a" = {
name = "www"
type = "CNAME"
value = "example.com."
ttl = 3600
}
# Mail
"mx-primary" = {
name = "@"
type = "MX"
value = "mail.example.com."
ttl = 3600
priority = 10
}
"mail-a" = {
name = "mail"
type = "A"
value = "203.0.113.20"
ttl = 3600
}
# SPF
"spf" = {
name = "@"
type = "TXT"
value = "v=spf1 mx -all"
ttl = 3600
}
# IPv6
"root-aaaa" = {
name = "@"
type = "AAAA"
value = "2001:db8::1"
ttl = 3600
}
}
}When choosing TTL values, keep them identical across both providers. Mismatched TTLs cause inconsistent caching behaviour depending on which provider a resolver happens to query.
Create in Both Providers
# dnscale.tf
resource "dnscale_zone" "primary" {
name = var.domain
}
resource "dnscale_record" "all" {
for_each = local.dns_records
zone_id = dnscale_zone.primary.id
name = each.value.name
type = each.value.type
content = each.value.value
ttl = each.value.ttl
priority = lookup(each.value, "priority", null)
}# hetzner.tf
resource "hetznerdns_zone" "secondary" {
name = var.domain
ttl = 3600
}
resource "hetznerdns_record" "all" {
for_each = local.dns_records
zone_id = hetznerdns_zone.secondary.id
name = each.value.name
type = each.value.type
value = each.value.value
ttl = each.value.ttl
}For MX records on Hetzner DNS, the priority is typically included in the
valuefield (e.g.,"10 mail.example.com."). You may need to adjust formatting depending on the provider version. Check the Hetzner DNS Terraform provider docs for specifics.
Apply
export TF_VAR_dnscale_api_key="your-dnscale-key"
export TF_VAR_hetzner_dns_token="your-hetzner-token"
terraform init
terraform plan # Review changes for both providers
terraform apply # Push to both simultaneouslyTerraform creates resources in both providers in parallel, so a single apply updates everything. For a deeper dive on the DNScale provider specifically, see the Terraform provider guide.
DNSControl: Multi-Provider Setup
DNSControl has native multi-provider support built in. You can attach multiple DNS providers to a single domain, and DNSControl pushes identical records to all of them in one command.
Credentials
// creds.json
{
"dnscale": {
"TYPE": "DNSCALE",
"api_key": "your-dnscale-key"
},
"hetzner": {
"TYPE": "HETZNER",
"api_token": "your-hetzner-token"
}
}Keep creds.json out of version control:
echo "creds.json" >> .gitignoreConfiguration
DNSControl's DnsProvider() function accepts multiple providers for a single domain. Records are automatically pushed to all attached providers.
DNScale assigns nameservers server-side when a zone is created, so they aren't automatically included in registrar delegation. You need to declare them explicitly with NAMESERVER(). DNScale's own NS records (like ns1.dnscale.eu) are system-managed and invisible to DNSControl, but third-party NS records at the apex are fully supported — DNSControl will sync them to DNScale alongside your other records:
// dnsconfig.js
var REG_NAMECHEAP = NewRegistrar("namecheap");
var DSP_DNSCALE = NewDnsProvider("dnscale");
var DSP_HETZNER = NewDnsProvider("hetzner");
D("example.com", REG_NAMECHEAP,
DnsProvider(DSP_DNSCALE),
DnsProvider(DSP_HETZNER),
// DNScale nameservers must be declared explicitly for registrar delegation
NAMESERVER("ns1.dnscale.eu"),
NAMESERVER("ns2.dnscale.eu"),
// A records
A("@", "203.0.113.10", TTL(3600)),
A("mail", "203.0.113.20", TTL(3600)),
// IPv6
AAAA("@", "2001:db8::1", TTL(3600)),
// CNAME
CNAME("www", "example.com.", TTL(3600)),
// Mail
MX("@", 10, "mail.example.com.", TTL(3600)),
// SPF
TXT("@", "v=spf1 mx -all", TTL(3600)),
END);Hetzner's nameservers are picked up automatically from the provider, while DNScale's require the explicit NAMESERVER() entries. Both sets are sent to the registrar for NS delegation.
Preview and Push
# See what would change on both providers
dnscontrol preview
# Apply changes to both providers
dnscontrol pushThe preview output shows changes per provider, so you can verify both DNScale and Hetzner will receive the correct records before pushing.
For more details on DNSControl with DNScale, see the DNSControl guide.
Keeping Records in Sync
The biggest risk with multi-provider DNS is drift — records getting out of sync between providers. Your IaC tool solves this by being the single source of truth. Understanding how DNS propagation works is also important here, since changes take time to reach all resolvers globally.
IaC as the Source of Truth
Do not edit records directly in the DNScale or Hetzner dashboards. Route changes through your Terraform or DNSControl configuration so the intended state stays consistent across both providers.
CI/CD Pipeline
Automate deployments so records are pushed to both providers on every merge to main:
# .github/workflows/dns.yml
name: DNS Deploy
on:
push:
branches: [main]
paths: ["dns/**"]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
- name: Apply DNS changes
working-directory: dns/
env:
TF_VAR_dnscale_api_key: ${{ secrets.DNSCALE_API_KEY }}
TF_VAR_hetzner_dns_token: ${{ secrets.HETZNER_DNS_TOKEN }}
run: |
terraform init
terraform apply -auto-approveDrift Detection
Run periodic checks to catch any manual changes or API inconsistencies:
# Terraform: detect drift
terraform plan -detailed-exitcode
# Exit code 2 means drift detected
# DNSControl: preview will show any differences
dnscontrol previewSchedule this in CI (e.g., a daily cron job) and alert on unexpected differences.
NS Delegation at Your Registrar
After deploying records to both providers, update the NS records at your domain registrar to include nameservers from both DNScale and Hetzner. If you are unfamiliar with how NS delegation works, see What is an NS Record?.
A typical NS set looks like:
ns1.dnscale.eu
ns2.dnscale.eu
hydrogen.ns.hetzner.com
oxygen.ns.hetzner.com
helium.ns.hetzner.deThe exact DNScale nameserver names depend on your account. Check your zone details in the DNScale dashboard for the assigned nameservers.
Set all of these as NS records at your registrar. Most registrars let you add as many nameservers as you need.
Verify Delegation
After updating NS records (allow up to 48 hours for propagation — see DNS Propagation Explained), verify with:
dig NS example.com +shortYou should see nameservers from both providers in the response. You can also verify each provider is responding correctly:
# Query DNScale directly
dig @ns1.dnscale.eu example.com A +short
# Query Hetzner directly
dig @hydrogen.ns.hetzner.com example.com A +shortBoth should return the same IP address.
Limitations & Considerations
DNSSEC
Multi-provider DNSSEC is complex. Each provider signs the zone with its own keys, so you either need:
- Both providers to support multi-signer DNSSEC (RFC 8901) — not widely supported yet
- One provider to be the signer and transfer signed zones to the other
- Disable DNSSEC intentionally on multi-provider zones only when the risk trade-off is accepted and no DS record remains at the parent
Do not run one signed provider beside one unsigned provider while a DS record exists at the parent. Validators can receive an answer from either provider and treat the unsigned side as bogus. If DNSSEC is required, use multi-signer DNSSEC, transfer a signed zone from one signer to the other provider, or keep a single signed authoritative provider until the multi-provider signing model is tested. See the DNSSEC key management guide for single-provider DNSSEC setup.
TTL Alignment
Keep TTLs identical across both providers. Mismatched TTLs mean resolvers cache records for different durations depending on which provider they queried, leading to inconsistent behavior. Our TTL best practices guide covers how to choose the right values.
Propagation Delays
When you push changes, both providers process them independently. There's a brief window (usually seconds, sometimes minutes) where one provider has the new records and the other still serves the old ones. For most use cases this is fine — just avoid making changes that would break if partially applied.
Provider-Specific Record Types
Stick to standard record types that both providers support: A, AAAA, CNAME, MX, TXT, SRV, CAA, NS. Provider-specific extensions or proprietary record types won't be portable.
Next Steps
- Managing DNS with Terraform — deeper dive into the DNScale Terraform provider
- Managing DNS with DNSControl — full DNSControl setup and configuration
- DNSSEC Key Management — set up DNSSEC for single-provider zones
- Global DNS Resolution Balancing — geographic traffic distribution across DNS nodes
- DNS Failover Design Patterns — TTL-aware failover and disaster-recovery models
- Anycast DNS Network — how anycast routing delivers queries to a preferred POP
- Secondary DNS — an alternative redundancy approach using zone transfers
Frequently asked questions
- Do resolvers actually use both providers, or just one?
- Usually both over time, but not in a perfectly balanced way. Resolver behavior varies: many track nameserver responsiveness, prefer lower-latency servers, and retry another listed server after timeouts. If one provider goes dark, many resolvers will recover by trying the other set, but timing depends on resolver implementation, cached delegation data, and network conditions.
- Will Google/Cloudflare/Quad9 cache from both providers?
- They can cache answers learned from either provider, and large public resolvers generally track nameserver responsiveness. That is not the same as guaranteed balancing across the full NS list. As long as both providers serve identical records, resolver choice should not change the answer users receive.
- What if the two providers' records drift out of sync?
- Resolvers may return different answers depending on which provider answered, which is bad — especially for low-TTL records that change often. Use Terraform/DNSControl as the single source of truth and run drift detection in CI on every push. The multi-provider-dns-deployment guide above shows a GitHub Actions workflow that fails the pipeline if either provider drifts.
- Should I use AXFR instead of API-based sync?
- AXFR (zone transfers) work between providers that both support them, but it is fragile — TSIG key management, firewall holes for TCP/53 port 53, and provider-specific quirks. API-based sync via Terraform or DNSControl is cleaner: you define records once, push to both providers in parallel, and version-control the result. Also works with providers that don't expose AXFR.
- Is multi-provider DNS overkill for small sites?
- For a personal blog, usually yes. For domains where DNS downtime has commercial cost — e-commerce, B2B SaaS, infrastructure APIs — a second authoritative provider can be inexpensive insurance. Compare the extra provider cost and synchronization complexity against the business impact of a DNS outage.
Related guides
Automation
Managing DNS with Terraform
Manage DNS zones, records, and DNSSEC as code with the DNScale Terraform provider. Install the provider, create records, use CI/CD, and avoid DNS downtime.
Automation
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.
Automation
DNS for Cloud Infrastructure — Best Practices and Architecture
Learn cloud DNS best practices including service discovery, multi-cloud strategies, automation with Terraform, and TTL optimization for dynamic infrastructure.
Automation
DNS as Code Best Practices
A practical guide to managing DNS as code with ownership, review, previews, drift detection, scoped credentials, and safe migration patterns.
Ready to manage your DNS with confidence?
DNScale provides anycast DNS hosting with a global network, real-time analytics, and an easy-to-use API.
Start free