Before cloud DNS providers with APIs, before databases and dynamic updates, there were zone files. Plain text files that defined every record in a zone. And honestly? They’re still everywhere.
Understanding zone file format isn’t just historical knowledge — it’s practical. You’ll encounter zone files in BIND configurations, in debugging output, in migration exports, and in the RFCs themselves.
Zone File Format and Syntax
Zone files follow the “master file format” defined in RFC 1035 §5. It’s been stable since 1987.
Basic Structure
$TTL 3600 ; Default TTL for the zone
$ORIGIN example.com. ; Base domain for relative names
; SOA record (required, exactly one)
@ IN SOA ns1.example.com. admin.example.com. (
2024012801 ; Serial
3600 ; Refresh (1 hour)
600 ; Retry (10 minutes)
604800 ; Expire (1 week)
300 ; Minimum (negative cache TTL)
)
; Nameserver records
@ IN NS ns1.example.com.
@ IN NS ns2.example.com.
; Address records
@ IN A 93.184.216.34
www IN A 93.184.216.34
mail IN A 93.184.216.35
; Mail routing
@ IN MX 10 mail.example.com.
; Text records
@ IN TXT "v=spf1 mx -all"
Key Syntax Elements
Comments: Anything after ; is a comment
Directives: Lines starting with $ are directives
$TTL: Default TTL for records that don’t specify one$ORIGIN: The domain appended to relative names$INCLUDE: Include another file
@ symbol: Represents the current $ORIGIN (zone apex)
Relative vs absolute names:
www(no trailing dot) → relative → becomeswww.example.com.mail.example.com.(trailing dot) → absolute → used as-is
Parentheses: Allow multi-line records (whitespace is ignored inside)
Record Format
name ttl class type rdata
www 3600 IN A 93.184.216.34
If ttl is omitted, uses the $TTL default. If name is omitted, uses the previous record’s name (continuation).
Common Gotchas
Forgetting the trailing dot:
; WRONG: This creates mail.example.com.example.com.
mail IN CNAME mail.example.com
; RIGHT: This creates the intended CNAME
mail IN CNAME mail.example.com.
Relative MX/NS targets:
; WRONG: "mail" becomes mail.example.com., then that + origin = double suffix
@ IN MX 10 mail
; WRONG: Even worse — a bare name in the rdata field
@ IN NS ns1
; RIGHT: Always use FQDNs for MX and NS targets
@ IN MX 10 mail.example.com.
@ IN NS ns1.example.com.
SOA Record Deep Dive
The Start of Authority record is required at the zone apex. It defines zone metadata and controls replication behavior.
example.com. IN SOA ns1.example.com. hostmaster.example.com. (
2024012801 ; Serial
3600 ; Refresh
600 ; Retry
604800 ; Expire
300 ; Minimum
)
Field-by-Field
| Field | Example | Meaning | Recommendations |
|---|---|---|---|
| MNAME | ns1.example.com. | Primary nameserver | Must be a valid NS for the zone |
| RNAME | hostmaster.example.com. | Admin email (@ → .) | hostmaster@ or dns-admin@ |
| Serial | 2024012801 | Zone version | Increment on every change |
| Refresh | 3600 | Secondary check interval | 1-24 hours typical |
| Retry | 600 | Retry after failed refresh | 1/4 to 1/10 of refresh |
| Expire | 604800 | When to stop serving if primary unreachable | 1-4 weeks |
| Minimum | 300 | Negative cache TTL | 60-3600 seconds |
Serial Number Conventions
The serial number must increase with every zone change. Secondary servers compare serials to detect updates.
YYYYMMDDnn format (recommended):
2024012801 ; January 28, 2024, revision 01
2024012802 ; January 28, 2024, revision 02
2024012901 ; January 29, 2024, revision 01
This format is human-readable and naturally sorts correctly. The nn suffix allows up to 100 changes per day.
Unix timestamp: Some systems use epoch seconds. Works but isn’t human-readable.
Simple increment: Just add 1 each time. Works but loses date context.
What If Serial Goes Backward?
If you accidentally decrease the serial (e.g., restoring a backup), secondaries won’t see an update — they think they already have a newer version.
Fixes:
- Jump the serial to a value higher than any previous (e.g., use current YYYYMMDDnn)
- Use serial arithmetic (RFC 1982) — but this is complex and error-prone
- Manually reset all secondary servers
Zone Transfers
Zone transfers replicate zone data from primary to secondary servers. Two protocols exist: AXFR and IXFR.
AXFR: Full Zone Transfer
AXFR (Authoritative Transfer) transfers the entire zone. Defined in RFC 5936.
How it works:
- Secondary connects to primary via TCP port 53
- Secondary sends AXFR query
- Primary sends all records, bracketed by SOA records
- Secondary replaces its entire zone data with the new version
Secondary → Primary: AXFR query for example.com
Primary → Secondary:
SOA (serial 2024012801)
NS ns1.example.com.
NS ns2.example.com.
A record for @
A record for www
... all records ...
SOA (serial 2024012801) ← marks end of transfer
When AXFR is used:
- Initial setup of a new secondary
- When the secondary’s serial is too far behind (gap in IXFR chain)
- When explicitly requested by administrator
IXFR: Incremental Zone Transfer
IXFR (Incremental Transfer) sends only changes since a specified serial. Defined in RFC 1995.
How it works:
- Secondary includes its current serial in the IXFR query
- Primary sends a sequence of deletions and additions
- Secondary applies changes incrementally
Secondary → Primary: IXFR for example.com, I have serial 2024012800
Primary → Secondary:
SOA (serial 2024012801) ← new serial
SOA (serial 2024012800) ← delete section for old serial
A www 93.184.216.34 ← record to delete
SOA (serial 2024012801) ← add section for new serial
A www 93.184.216.35 ← record to add
SOA (serial 2024012801) ← end marker
IXFR benefits:
- Far less data transferred for small changes
- Faster synchronization
- Lower bandwidth usage
IXFR limitations:
- Primary must keep history of changes (journal)
- If changes are too old, falls back to AXFR
Refresh and Retry
Secondaries poll primaries based on SOA timing values:
- Secondary waits
refreshseconds - Secondary queries primary’s SOA
- If serial increased, request IXFR/AXFR
- If primary unreachable, retry every
retryseconds - After
expireseconds unreachable, stop serving the zone
Modern DNS also supports NOTIFY (RFC 1996): primaries push notifications to secondaries immediately after zone changes, eliminating wait time.
TSIG: Authenticating Transfers
Zone transfers are sensitive — you don’t want arbitrary clients downloading your entire zone or, worse, sending fake updates. TSIG (Transaction Signature) provides authentication.
How TSIG Works
TSIG uses shared secret keys and HMAC algorithms to sign DNS messages (RFC 8945).
- Both primary and secondary are configured with the same key
- Requests include a TSIG record with an HMAC signature
- Server verifies the signature before responding
- Responses are also signed
BIND configuration example:
key "xfer-key" {
algorithm hmac-sha256;
secret "base64-encoded-secret==";
};
TSIG for Dynamic Updates
TSIG also authenticates dynamic DNS updates — allowing authorized clients to modify zone records without editing zone files.
# nsupdate with TSIG authentication
nsupdate -k /path/to/tsig.key << EOF
server ns1.example.com
zone example.com
update delete test.example.com A
update add test.example.com 300 A 192.0.2.100
send
EOF
Modern Zone Management
Zone files are battle-tested, but modern operations often use other approaches:
API-Driven DNS Providers
Cloudflare, Route 53, Google Cloud DNS, and others expose HTTP APIs:
# Add a record via API
curl -X POST "https://api.cloudflare.com/client/v4/zones/ZONE_ID/dns_records" \
-H "Authorization: Bearer TOKEN" \
-d '{"type":"A","name":"test","content":"192.0.2.100","ttl":300}'
Benefits:
- Immediate propagation (no zone transfer delay)
- Built-in redundancy and anycast
- Version control through API audit logs
Infrastructure as Code
Tools like Terraform, Pulumi, and OctoDNS manage DNS as code:
# Terraform example
resource "cloudflare_record" "www" {
zone_id = var.zone_id
name = "www"
value = "93.184.216.34"
type = "A"
ttl = 300
}
Benefits:
- Changes reviewed in pull requests
- Automatic deployment pipelines
- State tracking and drift detection
Git-Managed Zone Files
Some operations keep traditional zone files but manage them with Git:
# Edit zone file
vim db.example.com
# Increment serial (critical!)
# Commit and push
git commit -am "Add new A record for api.example.com"
git push
# CI/CD deploys to DNS servers
This combines zone file portability with modern version control.
When to Use Each
| Approach | Best For |
|---|---|
| Raw zone files | Self-hosted BIND/NSD, maximum control |
| API providers | Easy management, built-in HA, low maintenance |
| Terraform/IaC | Multi-provider, reproducible infrastructure |
| Git + zone files | Self-hosted with version control |
Zone File Utilities
Useful tools for working with zone files:
# Validate zone file syntax
named-checkzone example.com db.example.com
# Convert AXFR to zone file
dig @ns1.example.com example.com AXFR > example.com.zone
# Pretty-print and sort
ldns-read-zone db.example.com
# Diff two zone versions
diff <(ldns-read-zone old.zone) <(ldns-read-zone new.zone)
Key Takeaways
- Zone file format is plain text, defined in RFC 1035, still widely used
- $ORIGIN and trailing dots control relative vs absolute name interpretation
- SOA records define zone metadata: serial, refresh, retry, expire, minimum
- Serial numbers must increment on every change — YYYYMMDDnn is the standard convention
- AXFR transfers entire zones; IXFR transfers only changes
- TSIG authenticates transfers and dynamic updates using shared secrets
- Modern approaches include APIs, Terraform, and Git — but zone files remain the interchange format
Even if you never hand-edit a zone file, understanding the format helps you read dig output, debug issues, and migrate between providers. In the final chapter, we’ll go even deeper: the DNS protocol itself.