Introducing PostScale -- email API for transactional, inbound, and masked addresses. PostScale

    AutomationExpert

    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:

    1. Define your records once in Terraform or DNSControl
    2. Push identical records to both DNScale and Hetzner DNS
    3. Set NS records at your registrar pointing to nameservers from both providers
    4. 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 one

    Both 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:

    1. A DNScale account with an API key — Get your API key
    2. A Hetzner DNS account with an API token — Hetzner DNS Console
    3. Terraform (v1.0+) or DNSControl (v4.0+) installed
    4. 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 value field (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 simultaneously

    Terraform 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" >> .gitignore

    Configuration

    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 push

    The 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-approve

    Drift 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 preview

    Schedule 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.de

    The 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 +short

    You 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 +short

    Both 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

    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

    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