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.
Answer snapshot
A Terraform DNS provider lets you declare DNS zones and records in HCL, review changes in pull requests, and apply them through the same workflow as the rest of your infrastructure. The DNScale provider manages zones, records, DNSSEC keys, and read-only data sources. Use remote state, scoped API keys, plan reviews, and careful TTL changes so DNS automation stays predictable.
What you'll learn
- Install and configure the DNScale Terraform provider with secure credential management
- Create DNS zones and common record types with verified HCL examples
- Import existing DNScale zones and records into Terraform state safely
- Review DNS changes through CI/CD without turning every pull request into a production mutation
The DNScale Terraform provider lets platform and DevOps teams manage authoritative DNS zones, DNS records, and DNSSEC keys as code. Instead of changing production DNS by hand in a dashboard, you can commit HCL to Git, review the diff, run terraform plan, and apply the change through the same controlled workflow you use for infrastructure.
This guide covers provider setup, record examples, imports, CI/CD, reusable modules, troubleshooting, and when Terraform is the right tool compared with the DNScale dashboard, API, or DNSControl.
Quick Actions
| Goal | Start here |
|---|---|
| Install the provider | Add dnscaleou/dnscale to required_providers |
| Authenticate safely | Store the API key in DNSCALE_API_KEY or a CI secret |
| Create a zone | Use the dnscale_zone resource |
| Add records | Use dnscale_record with full record names and trailing dots |
| Automate changes | Run terraform plan on pull requests and apply from a protected branch |
Need an API key first? Create a DNScale account, then create a scoped key from the dashboard. For the raw API surface behind the provider, see the DNScale API docs.
What Is a Terraform DNS Provider?
A Terraform DNS provider lets you declare DNS resources in HCL instead of editing them manually. For DNScale, the provider maps DNS objects to Terraform resources:
| DNScale object | Terraform object |
|---|---|
| DNS zone | dnscale_zone |
| DNS record | dnscale_record |
| DNSSEC key | dnscale_dnssec_key |
| Existing zone lookup | data "dnscale_zone" and data "dnscale_zones" |
| Existing records lookup | data "dnscale_records" |
| DNSSEC status lookup | data "dnscale_dnssec_status" |
Terraform is useful when DNS changes should be reviewed with code, tied to infrastructure resources, reused across environments, or deployed from CI/CD. A dashboard is still faster for a one-off test record or emergency change. A direct API script can be better for application-driven tasks such as short-lived tenant verification records.
Why Manage DNS as Code?
DNS controls traffic, certificates, email, and customer onboarding. Manual changes work until the team needs review, auditability, repeatability, and rollback.
| Problem with manual DNS | DNS as code benefit |
|---|---|
| Changes happen outside review | Pull requests and version control |
| It is hard to know who changed what | Git history and Terraform state |
| Repeated setup differs by environment | Reusable modules and variables |
| Human typo risk | terraform plan before terraform apply |
| Slow onboarding | DNS configuration documents itself in code |
| Manual drift after incidents | Scheduled plans can detect drift |
For broader operating practices, read DNS as code best practices, DNS in CI/CD pipelines, and GitHub Actions DNS workflows.
DNScale Terraform Provider Quickstart
The DNScale provider is published on the Terraform Registry as dnscaleou/dnscale.
- Install Terraform.
- Create a DNScale API key.
- Add the provider to
required_providers. - Set credentials with
DNSCALE_API_KEYor a sensitive Terraform variable. - Create a zone.
- Add records.
- Run
terraform init. - Run
terraform plan. - Run
terraform apply.
terraform {
required_providers {
dnscale = {
source = "dnscaleou/dnscale"
version = "~> 1.0"
}
}
}
provider "dnscale" {
# Prefer DNSCALE_API_KEY in the environment.
# api_url defaults to https://api.dnscale.eu
}export DNSCALE_API_KEY="dns_xxx"
terraform init
terraform planIf you prefer explicit variables, keep the value sensitive and feed it from your secret manager:
variable "dnscale_api_key" {
description = "DNScale API key"
type = string
sensitive = true
}
provider "dnscale" {
api_key = var.dnscale_api_key
}Create a DNS Zone
A DNS zone represents the domain managed by DNScale. The current provider schema requires name, region, and type.
resource "dnscale_zone" "example" {
name = "example.com"
region = "EU"
type = "master"
}
output "zone_id" {
value = dnscale_zone.example.id
}After the zone is created, delegate the domain at your registrar by pointing the parent-zone NS records to the DNScale nameservers shown in the dashboard or API. Keep delegation changes separate from record changes when possible; NS and DNSSEC mistakes can affect the whole domain.
Manage Common DNS Records
dnscale_record expects the full record name with a trailing dot, such as www.example.com. or example.com.. TTL is in seconds. Choose lower TTLs such as 300 for records that may change soon, and higher TTLs such as 3600 or 86400 for stable records. See DNS TTL best practices before migrations.
A Record
Use an A record to point a hostname to an IPv4 address.
resource "dnscale_record" "www_a" {
zone_id = dnscale_zone.example.id
name = "www.example.com."
type = "A"
content = "192.0.2.10"
ttl = 300
}AAAA Record
Use an AAAA record to publish IPv6 alongside IPv4.
resource "dnscale_record" "www_aaaa" {
zone_id = dnscale_zone.example.id
name = "www.example.com."
type = "AAAA"
content = "2001:db8::10"
ttl = 300
}CNAME Record
Use a CNAME record when a subdomain should follow another hostname. Avoid CNAMEs at names that also need MX, TXT, or other record data. For the common comparison, see CNAME vs A record.
resource "dnscale_record" "docs" {
zone_id = dnscale_zone.example.id
name = "docs.example.com."
type = "CNAME"
content = "example.gitbook.io."
ttl = 3600
}MX Record
Use MX records to route inbound email. DNScale uses the separate priority field for MX priority.
resource "dnscale_record" "mx_primary" {
zone_id = dnscale_zone.example.id
name = "example.com."
type = "MX"
content = "mail.example.com."
priority = 10
ttl = 3600
}TXT Record
Use TXT records for SPF, DKIM, DMARC, ownership verification, and other text values.
resource "dnscale_record" "spf" {
zone_id = dnscale_zone.example.id
name = "example.com."
type = "TXT"
content = "v=spf1 include:_spf.example.com -all"
ttl = 3600
}For email authentication records, also see SPF records explained, DKIM explained, and DMARC explained.
CAA Record
Use CAA records to control which certificate authorities may issue certificates for a domain.
resource "dnscale_record" "caa" {
zone_id = dnscale_zone.example.id
name = "example.com."
type = "CAA"
content = "0 issue \"letsencrypt.org\""
ttl = 3600
}SRV Record
Use SRV records for service discovery with priority, weight, port, and target in the content field.
resource "dnscale_record" "sip" {
zone_id = dnscale_zone.example.id
name = "_sip._tcp.example.com."
type = "SRV"
content = "10 5 5060 sip.example.com."
priority = 0
ttl = 3600
}ALIAS, HTTPS, SVCB, TLSA, SSHFP, and PTR
The provider also accepts advanced record types documented in the DNS record types reference, including ALIAS, HTTPS, SVCB, TLSA, SSHFP, and PTR. Use these only when the consuming service expects them:
- ALIAS records provide apex-friendly hostname aliasing.
- HTTPS records and SVCB records publish service binding metadata.
- TLSA records support DANE and depend on DNSSEC validation.
- SSHFP records publish SSH host fingerprints.
- PTR records map IP addresses back to names in reverse zones.
Terraform DNS Record Examples Table
| Task | Record type | Terraform resource | Related DNScale guide |
|---|---|---|---|
| Point a hostname to IPv4 | A | dnscale_record | A record guide |
| Point a hostname to IPv6 | AAAA | dnscale_record | AAAA record guide |
| Alias a subdomain | CNAME | dnscale_record | CNAME guide |
| Route inbound email | MX | dnscale_record with priority | MX record guide |
| Publish SPF, DKIM, DMARC, or verification text | TXT | dnscale_record | TXT record guide |
| Restrict certificate issuers | CAA | dnscale_record | CAA record guide |
| Publish service host and port | SRV | dnscale_record with priority | SRV record guide |
| Use apex hostname aliasing | ALIAS | dnscale_record | ALIAS record guide |
Manage DNSSEC With Terraform
The DNScale provider exposes dnscale_dnssec_key for KSK and ZSK resources. Use uppercase key types and algorithm names as shown in the provider docs.
resource "dnscale_dnssec_key" "ksk" {
zone_id = dnscale_zone.example.id
key_type = "KSK"
algorithm = "ECDSAP256SHA256"
active = true
published = true
}
resource "dnscale_dnssec_key" "zsk" {
zone_id = dnscale_zone.example.id
key_type = "ZSK"
algorithm = "ECDSAP256SHA256"
active = true
published = true
}
output "ds_records" {
value = dnscale_dnssec_key.ksk.ds
}DNSSEC has an extra registrar step: the DS record must be published at the parent zone. Do not change DNSSEC keys, DS records, or nameservers without a rollback plan. For the operational sequence, read DNSSEC setup for DNScale and DNSSEC key management.
Import Existing DNS Into Terraform
Import is useful when a zone already exists in DNScale and you want Terraform to take ownership without deleting and recreating records.
Start with an inventory:
- Export or list the existing zone and record IDs from the dashboard or API.
- Recreate the intended HCL using full record names with trailing dots.
- Import the existing resources into state.
- Run
terraform planand fix HCL until the plan shows no unwanted change. - Only then allow Terraform to apply changes.
# Import a zone by UUID.
terraform import dnscale_zone.example <zone-id>
# Import a record by zone ID and record ID.
terraform import dnscale_record.www <zone-id>/<record-id>
# Import a DNSSEC key by zone ID and key ID.
terraform import dnscale_dnssec_key.ksk <zone-id>/<key-id>Terraform import adopts resources one at a time; it does not automatically convert an entire BIND file or another provider's zone into HCL. For cross-provider migrations, first use zone import methods, then adopt the resulting DNScale records into Terraform if Terraform will become the source of truth.
You can also use data sources to inspect what exists:
data "dnscale_records" "all" {
zone_id = dnscale_zone.example.id
}
output "a_records" {
value = [for r in data.dnscale_records.all.records : r if r.type == "A"]
}How To Avoid DNS Downtime With Terraform
Lower TTLs Before High-Risk Changes
Lower TTLs before migrations, wait for the old TTL to expire, then make the cutover. Restore higher TTLs after the new state is stable. For the full pattern, see DNS TTL best practices.
Review Plan Output Before Applying
Read every DNS diff before terraform apply, especially deletes. A deleted MX record can stop inbound email; a bad CAA record can block certificate issuance; a bad DS record can cause DNSSEC SERVFAIL.
Change Nameservers and DNSSEC Separately
Do not bundle NS delegation, DS records, DNSSEC keys, and application record changes in one large pull request. Smaller changes make failures easier to isolate.
Verify From Authoritative and Recursive Resolvers
After applying, check what the authoritative nameservers serve and what public resolvers cache. Use the DNS propagation checker, DNS lookup tool, zone health check, and DNSSEC chain validator where relevant.
Keep Break-Glass Access
Terraform should be the routine path, not the only path. Keep emergency dashboard/API access available, then backfill emergency changes into HCL immediately after the incident.
Keep Public DNS Independent
For production domains, avoid making authoritative DNS depend on the same compute cloud it routes to. For architecture trade-offs, read DNS for cloud infrastructure and multi-provider DNS deployment.
Terraform in CI/CD
A safe DNS pipeline separates preview from apply:
| Stage | Runs when | Command |
|---|---|---|
| Format and validate | Pull request | terraform fmt -check and terraform validate |
| Preview | Pull request | terraform plan -input=false |
| Apply | Protected branch only | terraform apply -input=false |
| Drift check | Scheduled | terraform plan -detailed-exitcode |
Minimal GitHub Actions example:
name: terraform-dns
on:
pull_request:
paths: ["terraform/dns/**"]
push:
branches: ["main"]
paths: ["terraform/dns/**"]
jobs:
plan:
runs-on: ubuntu-latest
defaults:
run:
working-directory: terraform/dns
steps:
- uses: actions/checkout@v4
- uses: hashicorp/setup-terraform@v3
- run: terraform fmt -check
- run: terraform init -input=false
- run: terraform validate
- run: terraform plan -input=false
env:
DNSCALE_API_KEY: ${{ secrets.DNSCALE_READ_TOKEN }}
apply:
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
environment: production
defaults:
run:
working-directory: terraform/dns
steps:
- uses: actions/checkout@v4
- uses: hashicorp/setup-terraform@v3
- run: terraform init -input=false
- run: terraform apply -auto-approve -input=false
env:
DNSCALE_API_KEY: ${{ secrets.DNSCALE_PRODUCTION_TOKEN }}Use remote state and locking for teams. Keep production write credentials out of untrusted pull-request contexts. For more patterns, see DNS in CI/CD pipelines and GitHub Actions DNS workflows.
Reusable DNS Modules
Modules help when many zones share the same shape, such as website records, customer subdomains, or standard email records. Keep modules transparent enough that reviewers can still see what record data will change.
# modules/website-records/main.tf
variable "zone_id" {
type = string
}
variable "domain" {
type = string
}
variable "apex_ipv4" {
type = string
}
resource "dnscale_record" "apex" {
zone_id = var.zone_id
name = "${var.domain}."
type = "A"
content = var.apex_ipv4
ttl = 300
}
resource "dnscale_record" "www" {
zone_id = var.zone_id
name = "www.${var.domain}."
type = "CNAME"
content = "${var.domain}."
ttl = 300
}Good module candidates:
- Website records: apex A/AAAA plus
wwwCNAME. - Email records: MX, SPF, DKIM, DMARC, and optional MTA-STS/TLS-RPT.
- SaaS tenant records: customer CNAME plus verification TXT.
- Environment records: staging and production subdomains with separate variables.
Avoid hiding high-risk records such as NS, DS, DNSKEY, MX, and CAA behind overly clever abstractions.
Terraform vs Dashboard vs API vs DNSControl
| Method | Best for | Trade-offs |
|---|---|---|
| DNScale dashboard | Fast manual changes, exploration, emergency fixes | Less review and version-control discipline |
| DNScale API | Custom automation and application workflows | You maintain validation, retries, diffing, and rollback |
| Terraform provider | Infrastructure-as-code teams using HCL, remote state, and PR reviews | Requires state management and plan/apply discipline |
| DNSControl | DNS-specific workflows, readable zone files, multi-provider DNS | Uses a separate JavaScript-based workflow |
Terraform and DNSControl can coexist, but avoid having two systems manage the same zone or record set. Pick one source of truth per zone. For DNSControl setup, read the DNScale DNSControl guide.
Troubleshooting Terraform DNS Changes
| Problem | Likely cause | Fix |
|---|---|---|
| Provider cannot authenticate | Missing, expired, or wrongly scoped API key | Check DNSCALE_API_KEY, CI secrets, and API key scopes in authentication docs |
| Zone creation fails | Missing required region or type, or unsupported value casing | Use region = "EU" or "US" and type = "master" or "slave" |
| Record already exists | Resource is not imported or another writer created the record | Import the record or remove the duplicate writer |
| Plan wants to recreate records | HCL does not match provider-normalized names or IDs | Use full record names with trailing dots and compare with data "dnscale_records" |
| DNS change applied but users still see old answer | TTL and recursive resolver caching | Check authoritative answers and public caches with the DNS propagation checker |
| CNAME conflicts with other records | CNAME cannot coexist with other record data at the same name | Use A/AAAA/ALIAS or remove conflicting records; see CNAME vs A record |
| MX email is not routing | Priority, target hostname, or target address records are wrong | Verify MX records and the A/AAAA records for the mail host |
| Certificate issuance fails | CAA record is missing the right CA or too restrictive | Check CAA record configuration |
| DNSSEC validation fails | DS/DNSKEY mismatch or unsafe key timing | Review DNSSEC setup and test with the DNSSEC chain validator |
Related Guides and Tools
| Resource | Why it matters |
|---|---|
| DNS as code best practices | Operating model for reviewable DNS |
| DNS in CI/CD pipelines | Validation, preview, apply, and drift detection |
| GitHub Actions DNS workflows | Concrete GitHub Actions examples |
| DNS record types | Choose the right record type before writing HCL |
| DNS TTL best practices | Plan migrations and cache behavior |
| Zone import methods | Move existing DNS into DNScale before adopting Terraform |
| DNScale API records docs | Understand the record API behind the provider |
| DNS propagation checker | Verify changes across authoritative and recursive resolvers |
Manage DNS Records as Code With DNScale
DNScale gives you authoritative DNS that can be managed through the dashboard, API, Terraform provider, and DNSControl. Start with a scoped API key, put your DNS configuration in reviewable code, and use tools like DNS lookup, propagation checks, and zone health checks to verify changes after apply.
Frequently asked questions
- What is a Terraform DNS provider?
- A Terraform DNS provider is a plugin that maps DNS provider API objects, such as zones and records, into Terraform resources and data sources so DNS can be reviewed, planned, applied, and tracked in state.
- Can Terraform manage DNS records?
- Yes. With the DNScale provider, Terraform can manage DNS zones, A, AAAA, CNAME, MX, TXT, NS, SOA, SRV, CAA, PTR, ALIAS, TLSA, SSHFP, HTTPS, and SVCB records.
- How do I use the DNScale Terraform provider?
- Add source dnscaleou/dnscale to required_providers, set DNSCALE_API_KEY as an environment variable or pass a sensitive api_key variable, run terraform init, declare dnscale_zone and dnscale_record resources, then review terraform plan before applying.
- How should I store my DNScale API key for Terraform?
- Do not commit API keys. Use DNSCALE_API_KEY in your shell, CI secrets, Terraform Cloud variables, or a secret manager. Prefer zone-scoped API keys for automation so a leaked token cannot edit unrelated zones.
- Can I import an existing DNS zone into Terraform?
- Yes, existing DNScale resources can be imported one resource at a time. Zones use terraform import dnscale_zone.example <zone-id>; records use terraform import dnscale_record.www <zone-id>/<record-id>; DNSSEC keys use terraform import dnscale_dnssec_key.ksk <zone-id>/<key-id>.
- How do I avoid downtime when changing DNS with Terraform?
- Lower TTLs before migrations, review plan output, avoid combining delegation and record changes in one pull request, keep rollback records ready, and verify results with authoritative lookups and public resolvers.
- Can Terraform manage DNSSEC with DNScale?
- The provider includes dnscale_dnssec_key for KSK and ZSK management plus a dnscale_dnssec_status data source. Treat registrar-side DS publication as a separate operational step unless your registrar is also managed in Terraform.
- What is the difference between Terraform and DNSControl for DNS?
- Terraform is best when DNS belongs next to broader infrastructure state and HCL modules. DNSControl is best when DNS itself is the primary workflow, especially across many zones or multiple DNS providers.
- Should DNS be managed in the same Terraform state as cloud infrastructure?
- Sometimes, but keep the blast radius in mind. Shared state is useful when records reference load balancers or static IPs directly. Separate DNS state is safer for long-lived zones, delegation, email, and records that must survive compute-stack rebuilds.
Related guides
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.
Automation
DNS in CI/CD Pipelines
How to manage DNS changes through CI/CD with checks, previews, approvals, scoped secrets, drift detection, and safe rollback.
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