Maak kennis met PostScale -- e-mail-API voor transactionele, inkomende en gemaskeerde adressen. PostScale

    Multi-provider DNS met Terraform & DNSControl

    Implementeer DNS-zones bij DNScale en Hetzner DNS voor redundantie met Terraform of DNSControl.

    Je DNS via één enkele provider laten lopen betekent een single point of failure. Als die provider uitvalt — door een storing, een DDoS-aanval of een misconfiguratie — gaan je domeinen offline. Multi-provider DNS elimineert dat risico door dezelfde zone tegelijkertijd vanuit twee onafhankelijke providers te serveren.

    Deze gids behandelt het opzetten van DNScale en Hetzner DNS als dubbele providers met Terraform of DNSControl. Beide tools laten je records eenmalig definiëren en naar beide providers pushen, zodat alles gesynchroniseerd blijft zonder handmatige duplicatie.

    Waarom multi-provider DNS

    DNS is de basis van elke internetdienst die je draait. Een providerstoring betekent niet alleen dat je website onbereikbaar is — het betekent dat e-mail stopt, API's onbereikbaar worden en diensten die afhankelijk zijn van SRV- of TXT-records kapotgaan.

    Multi-provider DNS werkt door je domein te delegeren naar nameservers van beide providers. Resolvers wereldwijd bevragen welke set nameservers ook antwoordt, dus als de ene provider uitvalt, blijft de andere antwoorden leveren.

    Wanneer is multi-provider zinvol?

    • Productiedomeinen waar downtime echte kosten met zich meebrengt
    • Compliance-omgevingen die redundantie vereisen
    • Globale diensten waar providerdekking per regio verschilt

    Wanneer is het overdreven? Voor ontwikkeldomeinen, interne tooling of domeinen waar een paar minuten downtime acceptabel is, is een enkele provider met goede uptime meestal voldoende.

    Hoe het werkt

    Het concept is eenvoudig:

    1. Definieer je records eenmalig in Terraform of DNSControl
    2. Push identieke records naar zowel DNScale als Hetzner DNS
    3. Stel NS-records in bij je registrar die wijzen naar nameservers van beide providers
    4. Resolvers bevragen beide providers — wie het eerst antwoordt wint
    ┌──────────────┐
    │   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
    │ (identiek)   │     │ (identiek)   │
    └──────────────┘     └──────────────┘
           ▲                    ▲
           │    ┌──────────┐    │
           └────│ Resolver │────┘
                └──────────┘
            Bevraagt een van beide

    Beide providers bevatten dezelfde records. Je IaC-tool is de enige bron van waarheid en beide providers zijn slechts spiegels van die waarheid.

    Vereisten

    Je hebt nodig:

    1. Een DNScale-account met een API-sleutel — Haal je API-sleutel op
    2. Een Hetzner DNS-account met een API-token — Hetzner DNS Console
    3. Terraform (v1.0+) of DNSControl (v4.0+) geïnstalleerd
    4. Een domein dat je beheert met toegang om NS-records bij de registrar in te stellen

    Terraform: multi-provider configuratie

    Terraform maakt multi-provider DNS overzichtelijk met zijn native ondersteuning voor meerdere providers en for_each-loops. Je definieert je records eenmalig in een locals-blok en maakt ze aan bij beide providers.

    Providerconfiguratie

    # 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"
    }

    Records eenmalig definiëren

    De sleutel tot multi-provider DNS met Terraform is het definiëren van je records op één plek. Gebruik locals om een gedeelde recordset te maken:

    # 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
        }
      }
    }

    Aanmaken bij beide 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
    }

    Voor MX-records op Hetzner DNS wordt de prioriteit doorgaans opgenomen in het value-veld (bijv. "10 mail.example.com."). Mogelijk moet je de opmaak aanpassen afhankelijk van de providerversie. Raadpleeg de Hetzner DNS Terraform provider docs voor details.

    Toepassen

    export TF_VAR_dnscale_api_key="your-dnscale-key"
    export TF_VAR_hetzner_dns_token="your-hetzner-token"
     
    terraform init
    terraform plan    # Bekijk wijzigingen voor beide providers
    terraform apply   # Push naar beide tegelijkertijd

    Terraform maakt resources bij beide providers parallel aan, dus een enkele apply werkt alles bij. Zie voor een diepere duik in de DNScale-provider specifiek de Terraform-providergids.

    DNSControl: multi-provider configuratie

    DNSControl heeft native multi-provider ondersteuning ingebouwd. Je kunt meerdere DNS-providers koppelen aan één domein en DNSControl pusht identieke records naar alle providers in één commando.

    Referenties

    // creds.json
    {
      "dnscale": {
        "TYPE": "DNSCALE",
        "api_key": "your-dnscale-key"
      },
      "hetzner": {
        "TYPE": "HETZNER",
        "api_token": "your-hetzner-token"
      }
    }

    Houd creds.json buiten versiebeheer:

    echo "creds.json" >> .gitignore

    Configuratie

    De DnsProvider()-functie van DNSControl accepteert meerdere providers voor één domein. Records worden automatisch naar alle gekoppelde providers gepusht:

    // dnsconfig.js
    var REG_NONE = NewRegistrar("none");
    var DSP_DNSCALE = NewDnsProvider("dnscale");
    var DSP_HETZNER = NewDnsProvider("hetzner");
     
    D("example.com", REG_NONE,
      DnsProvider(DSP_DNSCALE),
      DnsProvider(DSP_HETZNER),
     
      // 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);

    Dat is alles. Beide providers staan bij hetzelfde domein vermeld en DNSControl regelt de rest.

    Preview en push

    # Bekijk wat er zou veranderen bij beide providers
    dnscontrol preview
     
    # Pas wijzigingen toe bij beide providers
    dnscontrol push

    De preview-output toont wijzigingen per provider, zodat je kunt verifiëren dat zowel DNScale als Hetzner de juiste records ontvangen voordat je pusht.

    Zie voor meer details over DNSControl met DNScale de DNSControl-gids.

    Records gesynchroniseerd houden

    Het grootste risico bij multi-provider DNS is drift — records die uit sync raken tussen providers. Je IaC-tool lost dit op door de enige bron van waarheid te zijn.

    IaC als bron van waarheid

    Bewerk nooit records rechtstreeks in de DNScale- of Hetzner-dashboards. Alle wijzigingen gaan via je Terraform- of DNSControl-configuratie. Dit zorgt ervoor dat beide providers altijd identieke records hebben.

    CI/CD-pipeline

    Automatiseer deployments zodat records bij elke merge naar main naar beide providers worden gepusht:

    # .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

    Driftdetectie

    Voer periodieke controles uit om handmatige wijzigingen of API-inconsistenties te detecteren:

    # Terraform: drift detecteren
    terraform plan -detailed-exitcode
    # Exitcode 2 betekent drift gedetecteerd
     
    # DNSControl: preview toont eventuele verschillen
    dnscontrol preview

    Plan dit in CI (bijv. een dagelijkse cronjob) en stel alerts in bij onverwachte verschillen.

    NS-delegatie bij je registrar

    Na het deployen van records naar beide providers, werk je de NS-records bij je domeinregistrar bij zodat ze nameservers van zowel DNScale als Hetzner bevatten.

    Een typische NS-set ziet er zo uit:

    ns1.dnscale.eu
    ns2.dnscale.eu
    hydrogen.ns.hetzner.com
    oxygen.ns.hetzner.com
    helium.ns.hetzner.de

    De exacte DNScale-nameservernamen hangen af van je account. Controleer je zonedetails in het DNScale-dashboard voor de toegewezen nameservers.

    Stel al deze in als NS-records bij je registrar. De meeste registrars laten je zoveel nameservers toevoegen als je nodig hebt.

    Delegatie verifiëren

    Na het bijwerken van NS-records (sta tot 48 uur propagatie toe), verifieer met:

    dig NS example.com +short

    Je zou nameservers van beide providers moeten zien in het antwoord. Je kunt ook verifiëren dat elke provider correct antwoordt:

    # DNScale rechtstreeks bevragen
    dig @ns1.dnscale.eu example.com A +short
     
    # Hetzner rechtstreeks bevragen
    dig @hydrogen.ns.hetzner.com example.com A +short

    Beide moeten hetzelfde IP-adres retourneren.

    Beperkingen en overwegingen

    DNSSEC

    Multi-provider DNSSEC is complex. Elke provider ondertekent de zone met eigen sleutels, dus je hebt nodig:

    • Beide providers ondersteunen multi-signer DNSSEC (RFC 8901) — nog niet breed ondersteund
    • Eén provider is de ondertekenaar en draagt ondertekende zones over aan de andere
    • DNSSEC overslaan op multi-providerzones totdat multi-signer-ondersteuning verbetert

    Voor de meeste configuraties is het overslaan van DNSSEC op multi-providerzones de pragmatische keuze. Als DNSSEC een harde vereiste is, overweeg dan om één provider met sterke uptimegaranties te gebruiken. Zie de DNSSEC-gids voor single-provider DNSSEC-configuratie.

    TTL-afstemming

    Houd TTL's identiek bij beide providers. Verschillende TTL's betekenen dat resolvers records voor verschillende duur cachen afhankelijk van welke provider ze bevroegen, wat leidt tot inconsistent gedrag.

    Propagatievertragingen

    Wanneer je wijzigingen pusht, verwerken beide providers ze onafhankelijk. Er is een kort venster (meestal seconden, soms minuten) waarin de ene provider de nieuwe records heeft en de andere nog de oude serveert. Voor de meeste toepassingen is dit prima — vermijd alleen wijzigingen die kapotgaan bij gedeeltelijke toepassing.

    Provider-specifieke recordtypen

    Houd je aan standaard recordtypen die beide providers ondersteunen: A, AAAA, CNAME, MX, TXT, SRV, CAA, NS. Provider-specifieke extensies of propriëtaire recordtypen zijn niet overdraagbaar.

    Volgende stappen