| |

LFCE 36 ๐Ÿง BIND9 โ€” Zones and Records

โฑ๏ธ Estimated read: 14 min | |

A BIND9 zone file is the authoritative data source for a domain. It is a plain text file that declares the domain’s name servers, its mail exchangers, its host addresses, and the start-of-authority record that ties the whole zone together. The previous chapter configured the server to load a zone; this chapter explains what goes inside the zone file and why each record exists. A zone file that parses without error is not necessarily correctโ€”a missing trailing dot, a stale serial number, or a CNAME at the zone apex can all produce a zone that loads but answers incorrectly.

The format is defined by RFC 1035 and refined by decades of operational practice. Every record follows the same structure: an owner name, an optional TTL, a class, a type, and the record data. The $TTL directive sets the default for the entire file, and the SOA record is mandatory and must appear first. The other recordsโ€”NS, A, AAAA, CNAME, MX, TXT, PTRโ€”declare the actual data that clients query for.

Key point: The SOA record’s serial number must increase every time the zone file changes. Secondary servers compare their serial against the primary’s, and a serial that does not increase prevents the secondary from transferring the updated zone . The refresh, retry, expire, and minimum fields in the SOA control how secondaries poll the primary, how long they wait between retries, when they stop answering queries if they cannot reach the primary, and how long negative answers are cached .


Why zone files require careful construction

The trailing-dot problem.

A domain name in a zone file that does not end with a dot is relative to the zone’s origin. ns1 becomes ns1.example.com. when the zone is example.com. But ns1.example.com without the trailing dot becomes ns1.example.com.example.com.โ€”a name that does not exist. Every fully qualified name in a zone file must end with a dot, or the zone will append the origin again . This is the single most common syntax error in zone files, and it produces a zone that loads but has incorrect hostnames.

The SOA-serial problem.

The serial number is a 32-bit unsigned integer. Secondary servers use it to determine whether their copy of the zone is current. When the primary’s serial is greater than the secondary’s, the secondary requests a zone transfer. If the serial is the same or lower, the secondary does not update, even if the zone file’s contents have changed . The conventional format is YYYYMMDDNN, where NN is a two-digit revision counter for that day . A serial that exceeds the 32-bit limit wraps to zero, which can cause secondaries to stop updating. The practical limit is 4,294,967,295 .

The CNAME-apex problem.

A CNAME record creates an alias. It cannot coexist with any other record at the same owner name. If the zone apex (@) has an NS record, an MX record, and a TXT record for SPF, it cannot also have a CNAME. The apex must have A or AAAA records, not a CNAME. A CNAME at www pointing to @ is valid because www has no other records .


a. The SOA record and its fields

The SOA (Start of Authority) record declares the zone’s primary name server, the administrator’s email address, and the timing parameters that govern secondary server behavior. It is the first record in the file and is mandatory.

; Zone file for example.com
$TTL 86400                                    ; Default TTL: 1 day

@   IN  SOA ns1.example.com. admin.example.com. (
        2025010101    ; Serial: YYYYMMDDNN format
        3600          ; Refresh: secondaries check every hour
        900           ; Retry: retry after 15 minutes on failure
        604800        ; Expire: stop answering after 1 week without contact
        300           ; Minimum (negative TTL): cache NXDOMAIN for 5 minutes
)

The @ symbol represents the zone’s origin, which is example.com. in this case. The first name after SOA is the primary name server’s fully qualified domain name, ending with a dot. The second name is the administrator’s email address with the @ replaced by a dot and a trailing dot appended: admin@example.com becomes admin.example.com. .

The refresh value tells secondaries how often to check the primary’s serial. The retry value tells them how long to wait after a failed check before trying again. The expire value tells them when to stop answering queries if they have not successfully contacted the primaryโ€”this prevents secondaries from serving stale data indefinitely. The minimum field, in modern BIND, is the negative caching TTL: how long a resolver should cache an NXDOMAIN response .

The serial in this example is 2025010101. If the zone file is edited on January 2, 2025, the serial becomes 2025010201. If it is edited twice on that day, the serial becomes 2025010202. The serial must always increase, never decrease, and never wrap past the 32-bit maximum.


b. NS, A, AAAA, and CNAME records

The NS record declares an authoritative name server for the zone. Every zone must have at least one, and best practice is to have at least two for redundancy. The A and AAAA records map hostnames to IPv4 and IPv6 addresses. The CNAME record creates an alias.

; Name servers
@       IN  NS      ns1.example.com.        ; Primary name server
@       IN  NS      ns2.example.com.        ; Secondary name server

; Glue records for in-zone name servers
ns1     IN  A       192.168.1.10
ns2     IN  A       192.168.1.11

; Host addresses
@       IN  A       192.168.1.20            ; Apex A record
@       IN  AAAA    2001:db8::20            ; Apex AAAA record
www     IN  A       192.168.1.20            ; www A record
mail    IN  A       192.168.1.25            ; Mail server A record
mail    IN  AAAA    2001:db8::25            ; Mail server AAAA record

; Alias
ftp     IN  CNAME   www                     ; ftp is an alias for www

The ns1 and ns2 records are called glue records when the name server is inside the zone it serves. Without glue, a resolver would need to resolve ns1.example.com before it could query it, which is a circular dependency. The glue A record breaks that cycle .

The apex A record (@ IN A 192.168.1.20) means the domain name itself resolves to an address. This is common for websites that serve content at example.com as well as www.example.com. The ftp CNAME points to www, so a query for ftp.example.com returns the same address as www.example.com .


c. MX, TXT, and PTR records

The MX record declares a mail exchanger for the domain. The TXT record holds arbitrary text, most commonly SPF and DMARC policies. The PTR record maps an IP address to a hostname in a reverse zone.

; Mail exchanger
@       IN  MX  10  mail.example.com.       ; Priority 10; lower is preferred

; SPF: only the mail host may send email for this domain
@       IN  TXT     "v=spf1 ip4:192.168.1.25 ip6:2001:db8::25 -all"

; DMARC: quarantine failures, send reports
_dmarc  IN  TXT     "v=DMARC1; p=quarantine; rua=mailto:dmarc@example.com"

The MX record’s priority field determines preference: the lower the number, the higher the preference. If multiple MX records exist, mail is delivered to the lowest-priority server first, and the next priority is tried only if the first fails . The trailing dot on mail.example.com. is required.

The TXT record’s value is a quoted string. The SPF record (v=spf1 ...) tells receiving mail servers which hosts are authorized to send email for the domain. The -all at the end means “reject all other senders.” The DMARC record at _dmarc.example.com tells receivers what to do when SPF or DKIM fails .

A reverse zone for the 192.168.1.0/24 subnet uses PTR records. The zone name is 1.168.192.in-addr.arpa, and the owner names are the last octet of the IP address.

; Reverse zone file for 192.168.1.0/24
$TTL 86400
@   IN  SOA ns1.example.com. admin.example.com. (
        2025010101 3600 900 604800 300
)
@   IN  NS  ns1.example.com.

10  IN  PTR ns1.example.com.                ; 192.168.1.10
11  IN  PTR ns2.example.com.                ; 192.168.1.11
20  IN  PTR www.example.com.                ; 192.168.1.20
25  IN  PTR mail.example.com.               ; 192.168.1.25

The PTR record’s owner name is the host part of the IP address. A query for 10.1.168.192.in-addr.arpa returns ns1.example.com. .


Complete Example Session

# ============================================
# PART 1: THE ZONE HEADER AND SOA
# ============================================
# Create the zone file with the SOA record
sudo tee /etc/bind/zones/example.com.zone > /dev/null <<'EOF'
$TTL 86400
@   IN  SOA ns1.example.com. admin.example.com. (
        2025010101 ; serial
        3600       ; refresh
        900        ; retry
        604800     ; expire
        300        ; negative TTL
)
EOF

# ============================================
# PART 2: THE NAME SERVER RECORDS
# ============================================
sudo tee -a /etc/bind/zones/example.com.zone > /dev/null <<'EOF'
@       IN  NS      ns1.example.com.
@       IN  NS      ns2.example.com.
EOF

# ============================================
# PART 3: THE GLUE RECORDS
# ============================================
sudo tee -a /etc/bind/zones/example.com.zone > /dev/null <<'EOF'
ns1     IN  A       192.168.1.10
ns2     IN  A       192.168.1.11
EOF

# ============================================
# PART 4: THE APEX ADDRESS RECORDS
# ============================================
sudo tee -a /etc/bind/zones/example.com.zone > /dev/null <<'EOF'
@       IN  A       192.168.1.20
@       IN  AAAA    2001:db8::20
EOF

# ============================================
# PART 5: THE HOST RECORDS
# ============================================
sudo tee -a /etc/bind/zones/example.com.zone > /dev/null <<'EOF'
www     IN  A       192.168.1.20
mail    IN  A       192.168.1.25
mail    IN  AAAA    2001:db8::25
ftp     IN  CNAME   www
EOF

# ============================================
# PART 6: THE MAIL AND TXT RECORDS
# ============================================
sudo tee -a /etc/bind/zones/example.com.zone > /dev/null <<'EOF'
@       IN  MX  10  mail.example.com.
@       IN  TXT     "v=spf1 ip4:192.168.1.25 ip6:2001:db8::25 -all"
_dmarc  IN  TXT     "v=DMARC1; p=quarantine; rua=mailto:dmarc@example.com"
EOF

# ============================================
# PART 7: THE REVERSE ZONE
# ============================================
sudo tee /etc/bind/zones/1.168.192.in-addr.arpa.zone > /dev/null <<'EOF'
$TTL 86400
@   IN  SOA ns1.example.com. admin.example.com. (
        2025010101 3600 900 604800 300
)
@   IN  NS  ns1.example.com.

10  IN  PTR ns1.example.com.
11  IN  PTR ns2.example.com.
20  IN  PTR www.example.com.
25  IN  PTR mail.example.com.
EOF

# ============================================
# PART 8: VALIDATE THE ZONES
# ============================================
sudo named-checkzone example.com /etc/bind/zones/example.com.zone
sudo named-checkzone 1.168.192.in-addr.arpa /etc/bind/zones/1.168.192.in-addr.arpa.zone

# ============================================
# PART 9: RELOAD AND TEST
# ============================================
sudo rndc reload
dig @localhost www.example.com A
dig @localhost example.com MX
dig @localhost -x 192.168.1.20

# ============================================
# PART 10: VERIFY THE SOA
# ============================================
dig @localhost example.com SOA
# Output shows the serial, refresh, retry, expire, and minimum

The ten parts build the zone file incrementally: the SOA header, the name servers, the glue records, the apex addresses, the host records, the mail and TXT records, the reverse zone, validation, reload, and SOA verification. The critical steps are Part 8 (validation before reload) and Part 9 (testing each record type with dig).


Quick Reference

Record Types

TypePurposeExample
SOAZone authority and timing@ IN SOA ns1.example.com. admin.example.com. (...)
NSAuthoritative name server@ IN NS ns1.example.com.
AIPv4 addresswww IN A 192.168.1.20
AAAAIPv6 addresswww IN AAAA 2001:db8::20
CNAMEAliasftp IN CNAME www
MXMail exchanger@ IN MX 10 mail.example.com.
TXTArbitrary text (SPF, DMARC)@ IN TXT "v=spf1 ..."
PTRReverse mapping20 IN PTR www.example.com.

SOA Fields

FieldMeaningTypical Value
SerialZone version; must increase2025010101
RefreshSecondary poll interval3600 (1 hour)
RetryRetry interval after failure900 (15 min)
ExpireStop answering if no contact604800 (1 week)
MinimumNegative cache TTL300 (5 min)

Validation Commands

CommandPurpose
named-checkzone <zone> <file>Validate zone file syntax
dig @server <name> <type>Query a specific record type
dig @server -x <ip>Reverse lookup

Best Practices

โœ… Do This:

# End every FQDN with a dot
@   IN  NS  ns1.example.com.                                 # โœ…

# Increment the serial on every change
2025010101 โ†’ 2025010102                                       # โœ…

# Use glue records for in-zone name servers
ns1 IN  A   192.168.1.10                                      # โœ…

# Validate before reloading
named-checkzone example.com /etc/bind/zones/example.com.zone  # โœ…

# Use CNAME only for aliases, never at the apex
ftp IN  CNAME www                                             # โœ…

โŒ Don’t Do This:

# Omit the trailing dot on FQDNs
@   IN  NS  ns1.example.com    # Becomes ns1.example.com.example.com. # โŒ

# Forget to increment the serial
# Secondary servers will not transfer the update                     # โŒ

# Place a CNAME at the apex
@   IN  CNAME www    # Conflicts with NS, MX, SOA                   # โŒ

# Use a serial that decreases
2025010201 โ†’ 2025010101    # Secondary ignores the update           # โŒ

# Skip validation before reload
rndc reload    # Syntax error causes zone to fail to load            # โŒ

Common Pitfalls

PitfallWhy It HappensFix
Hostname resolves to wrong nameMissing trailing dotAppend . to FQDNs
Secondary does not updateSerial not incrementedIncrement serial on every change
Zone fails to loadCNAME at apex or syntax errorRemove CNAME; run named-checkzone
Reverse lookup failsPTR record missing or zone not configuredAdd PTR; configure reverse zone
MX not workingMissing trailing dot on mail servermail.example.com.
Negative answers not cachedMinimum field too lowIncrease to 300โ€“3600

Real-World Examples

1. SOA record

@ IN SOA ns1.example.com. admin.example.com. ( 2025010101 3600 900 604800 300 )

2. NS records

@ IN NS ns1.example.com.
@ IN NS ns2.example.com.

3. Glue record

ns1 IN A 192.168.1.10

4. Apex A record

@ IN A 192.168.1.20

5. CNAME alias

ftp IN CNAME www

6. MX record

@ IN MX 10 mail.example.com.

7. SPF TXT record

@ IN TXT "v=spf1 ip4:192.168.1.25 -all"

8. PTR record

20 IN PTR www.example.com.

9. AAAA record

www IN AAAA 2001:db8::20

10. Validate zone

named-checkzone example.com /etc/bind/zones/example.com.zone

Visual

Zone File Structure

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  ZONE FILE RECORD ORDER                                      โ”‚
โ”‚                                                              โ”‚
โ”‚  $TTL 86400              โ† Default TTL for all records       โ”‚
โ”‚                                                              โ”‚
โ”‚  @ IN SOA ...            โ† Mandatory; first record           โ”‚
โ”‚    โ”œโ”€โ”€ serial            โ† Must increase on change           โ”‚
โ”‚    โ”œโ”€โ”€ refresh           โ† Secondary poll interval           โ”‚
โ”‚    โ”œโ”€โ”€ retry             โ† Retry interval                    โ”‚
โ”‚    โ”œโ”€โ”€ expire            โ† Stop answering if no contact      โ”‚
โ”‚    โ””โ”€โ”€ minimum           โ† Negative cache TTL                โ”‚
โ”‚                                                              โ”‚
โ”‚  @ IN NS ...             โ† Name servers (at least one)       โ”‚
โ”‚  ns1 IN A ...            โ† Glue for in-zone name servers     โ”‚
โ”‚                                                              โ”‚
โ”‚  @ IN A ...              โ† Apex address                      โ”‚
โ”‚  www IN A ...            โ† Host addresses                    โ”‚
โ”‚  www IN AAAA ...         โ† IPv6 addresses                    โ”‚
โ”‚  ftp IN CNAME www        โ† Aliases                           โ”‚
โ”‚                                                              โ”‚
โ”‚  @ IN MX 10 mail...      โ† Mail exchanger                    โ”‚
โ”‚  @ IN TXT "v=spf1..."    โ† SPF policy                        โ”‚
โ”‚  _dmarc IN TXT "..."     โ† DMARC policy                      โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

The Trailing Dot

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  RELATIVE VS ABSOLUTE NAMES                                  โ”‚
โ”‚                                                              โ”‚
โ”‚  Zone origin: example.com.                                   โ”‚
โ”‚                                                              โ”‚
โ”‚  ns1               โ†’ ns1.example.com.     (relative)         โ”‚
โ”‚  ns1.example.com   โ†’ ns1.example.com.example.com.  (WRONG)   โ”‚
โ”‚  ns1.example.com.  โ†’ ns1.example.com.     (absolute)         โ”‚
โ”‚                                                              โ”‚
โ”‚  Rule: every fully qualified name must end with a dot.       โ”‚
โ”‚  The dot tells BIND the name is complete; without it,        โ”‚
โ”‚  BIND appends the zone origin.                               โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

SOA Timing Relationships

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  refresh, retry, expire, minimum                             โ”‚
โ”‚                                                              โ”‚
โ”‚  Primary                          Secondary                  โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                      โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                โ”‚
โ”‚  โ”‚ Serial  โ”‚โ—€โ”€โ”€ refresh โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚ Serial  โ”‚                โ”‚
โ”‚  โ”‚ 2025... โ”‚                      โ”‚ 2025... โ”‚                โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                      โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                โ”‚
โ”‚       โ”‚                                โ”‚                     โ”‚
โ”‚       โ”‚  If refresh fails:             โ”‚                     โ”‚
โ”‚       โ”‚  wait retry seconds            โ”‚                     โ”‚
โ”‚       โ”‚  try again                     โ”‚                     โ”‚
โ”‚       โ”‚                                โ”‚                     โ”‚
โ”‚       โ”‚  If no contact for expire:     โ”‚                     โ”‚
โ”‚       โ”‚  secondary stops answering     โ”‚                     โ”‚
โ”‚       โ”‚                                โ”‚                     โ”‚
โ”‚       โ”‚  minimum controls how long     โ”‚                     โ”‚
โ”‚       โ”‚  NXDOMAIN is cached            โ”‚                     โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Summary

ItemValue
Mandatory recordSOA (first in file)
SOA serialMust increase on every change
SOA refreshSecondary poll interval
SOA retryRetry interval after failure
SOA expireStop answering if no contact
SOA minimumNegative cache TTL
Name serverNS record (at least one)
IPv4 addressA record
IPv6 addressAAAA record
AliasCNAME (never at apex)
MailMX record (priority + FQDN)
Text policyTXT record (SPF, DMARC)
ReversePTR record in in-addr.arpa zone
Validationnamed-checkzone <zone> <file>

Key takeaways:

  • Every FQDN in a zone file must end with a dot. Without it, BIND appends the zone origin again, producing a name like ns1.example.com.example.com. that does not exist .
  • The SOA serial must increase on every change. Secondary servers compare serials to decide whether to transfer. A serial that stays the same or decreases prevents the secondary from updating .
  • The SOA timing fields control secondary behavior. refresh is how often the secondary polls, retry is how long it waits after a failure, expire is when it stops answering if it cannot reach the primary, and minimum is the negative caching TTL .
  • CNAME records cannot coexist with other records. The apex must have A or AAAA records, not a CNAME, because it already has NS, SOA, and possibly MX and TXT records .
  • Glue records break the circular dependency. When a name server is inside the zone it serves, its A record must be in the zone file so resolvers can reach it without first resolving its name.
  • Validate before reloading. named-checkzone reports syntax errors and missing records before the zone is loaded. A zone that fails to load produces a server that appears running but refuses queries for that zone.

Remember: A zone file is the authoritative data for a domain. The SOA record declares the zone’s identity and timing. The NS records declare the name servers. The A and AAAA records declare host addresses. The CNAME records declare aliases. The MX and TXT records declare mail and policy. Every fully qualified name ends with a dot, the serial increases on every edit, and the file is validated before it is loaded. A zone file that parses is not necessarily correct; a zone file that answers queries correctly is the goal.


Was this helpful?


Stop using slow, ad-bloated tool sites! ๐Ÿคฎ

๐Ÿ”Ž Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
โœ… Finance (Mortgage, Interest, Inflation)
โœ… Tech (Base64, JSON, Dev Suite, IP)
โœ… Health (BMI, BMR, TDEE)
โœ… Productivity (Timer, Workspace, QR)

โšก๏ธ Fast & Private
๐Ÿ”’ No data leaves your device
๐Ÿ’Ž 100% Free

๐Ÿ”— Use it now: https://tools.kandz.me
๐Ÿ”– Bookmark itโ€”youโ€™ll need it later!