The most widely used authoritative and recursive DNS server software on the Internet, maintained by the Internet Systems Consortium (ISC).
Addresses below are RFC 5737 documentation ranges or placeholders - swap in your own.
Table of Contents#
- Overview
- DNS Server Types
- Installation
- Core Configuration
- Zone Files
- Access Control Lists
- Recursive vs Authoritative Configuration
- Zone Transfers
- DNSSEC
- Performance Tuning
- Testing and Validation
- Troubleshooting
- See Also
- Sources
1. Overview#
BIND9 (Berkeley Internet Name Domain version 9) translates domain names into IP addresses and vice versa. It can operate as:
- Authoritative server - provides definitive answers for zones it hosts
- Recursive resolver - resolves queries on behalf of clients by querying other DNS servers
- Both simultaneously (though separation is recommended for security)
BIND9 supports DNSSEC, TSIG authentication, dynamic DNS updates, Response Policy Zones (RPZ), and zone transfers (AXFR/IXFR).
Note: BIND9 9.16+ uses
primary/secondaryterminology. The legacymaster/slavesyntax is deprecated and will be removed in a future release.
2. DNS Server Types#
| Type | Description | Use Case |
|---|---|---|
| Primary (Master) | Holds the original read-write zone data | Zone management, record updates |
| Secondary (Slave) | Maintains a read-only copy transferred from the primary | Redundancy, geographic distribution |
| Caching/Recursive | Resolves queries by recursing through the DNS hierarchy | Client-facing resolver |
| Forwarder | Forwards queries to another resolver instead of recursing directly | Internal networks, policy enforcement |
3. Installation#
Debian/Ubuntu#
sudo apt-get install bind9 bind9utils bind9-doc dnsutilsRHEL/Fedora#
sudo dnf install bind bind-utilsArch Linux#
sudo pacman -S bindVerify Installation#
named -v
# BIND 9.18.x ...File Locations (Debian/Ubuntu)#
| File | Purpose |
|---|---|
/etc/bind/named.conf | Main configuration (includes others) |
/etc/bind/named.conf.options | Global options (forwarders, DNSSEC, ACLs) |
/etc/bind/named.conf.local | Zone definitions |
/var/cache/bind/ | Zone file storage and journal files |
/var/log/named/ | Log files (if configured) |
File Locations (RHEL/Fedora)#
| File | Purpose |
|---|---|
/etc/named.conf | Main configuration |
/var/named/ | Zone files |
/var/log/named/ | Log files |
4. Core Configuration#
named.conf Structure#
The main configuration file typically includes three subordinate files:
// /etc/bind/named.conf
include "/etc/bind/named.conf.options";
include "/etc/bind/named.conf.local";
include "/etc/bind/named.conf.default-zones";Global Options#
// /etc/bind/named.conf.options
options {
directory "/var/cache/bind";
// Interfaces to listen on
listen-on { 127.0.0.1; 192.0.2.1; };
listen-on-v6 { ::1; };
// Restrict recursive queries to trusted networks
allow-recursion { trusted-nets; };
// Forwarders (optional, for recursive mode)
forwarders {
1.1.1.1;
8.8.8.8;
};
forward only; // "only" = use forwarders exclusively; "first" = try forwarders, then recurse
// DNSSEC validation
dnssec-validation auto;
// Disable version exposure
version "not disclosed";
// Query logging (disabled by default for performance)
querylog no;
// Rate limiting
rate-limit {
responses-per-second 10;
window 5;
};
};Logging Configuration#
logging {
channel default_log {
file "/var/log/named/named.log" versions 5 size 50m;
severity info;
print-time yes;
print-severity yes;
print-category yes;
};
channel query_log {
file "/var/log/named/query.log" versions 3 size 20m;
severity info;
print-time yes;
};
channel security_log {
file "/var/log/named/security.log" versions 3 size 10m;
severity dynamic;
print-time yes;
};
category default { default_log; };
category queries { query_log; };
category security { security_log; };
category xfer-in { default_log; };
category xfer-out { default_log; };
category notify { default_log; };
};Create the log directory:
sudo mkdir -p /var/log/named
sudo chown bind:bind /var/log/named5. Zone Files#
Zone Definition#
Define zones in named.conf.local:
// Primary zone
zone "example.com" IN {
type primary;
file "/var/cache/bind/db.example.com";
allow-transfer { secondary-servers; };
also-notify { 192.0.2.2; };
notify yes;
};
// Reverse zone
zone "2.0.192.in-addr.arpa" IN {
type primary;
file "/var/cache/bind/db.192.0.2";
allow-transfer { secondary-servers; };
};Forward Zone File#
; /var/cache/bind/db.example.com
$TTL 86400
@ IN SOA ns1.example.com. admin.example.com. (
2026032201 ; Serial (YYYYMMDDNN format)
7200 ; Refresh (2 hours)
1200 ; Retry (20 minutes)
2419200 ; Expire (4 weeks)
60 ; Negative cache TTL (1 minute)
)
; Name servers
IN NS ns1.example.com.
IN NS ns2.example.com.
; A records
@ IN A 203.0.113.10
ns1 IN A 203.0.113.10
ns2 IN A 203.0.113.11
www IN A 203.0.113.10
mail IN A 203.0.113.20
; AAAA records
@ IN AAAA 2001:db8::10
www IN AAAA 2001:db8::10
; CNAME records
ftp IN CNAME www.example.com.
; MX records
@ IN MX 10 mail.example.com.
; TXT records
@ IN TXT "v=spf1 ip4:203.0.113.20 -all"
; SRV records
_submission._tcp IN SRV 0 1 587 mail.example.com.
; CAA records
@ IN CAA 0 issue "letsencrypt.org"
@ IN CAA 0 issuewild "letsencrypt.org"
@ IN CAA 0 iodef "mailto:admin@example.com"Reverse Zone File#
; /var/cache/bind/db.192.0.2
$TTL 86400
@ IN SOA ns1.example.com. admin.example.com. (
2026032201 ; Serial
7200 ; Refresh
1200 ; Retry
2419200 ; Expire
60 ; Negative cache TTL
)
IN NS ns1.example.com.
IN NS ns2.example.com.
10 IN PTR example.com.
11 IN PTR ns2.example.com.
20 IN PTR mail.example.com.SOA Record Fields#
| Field | Purpose | Typical Value |
|---|---|---|
| Primary NS | Authoritative name server | ns1.example.com. |
| Admin email | Contact (. replaces @) | admin.example.com. |
| Serial | Version number; increment on every change | YYYYMMDDNN |
| Refresh | How often secondaries check for updates | 7200 (2h) |
| Retry | Retry interval if refresh fails | 1200 (20m) |
| Expire | How long secondary keeps serving stale data | 2419200 (4w) |
| Negative TTL | Cache duration for NXDOMAIN responses | 60 (1m) |
6. Access Control Lists#
ACLs define named groups of IP addresses for use in other configuration directives:
// /etc/bind/named.conf.options (or named.conf.local)
acl "trusted-nets" {
127.0.0.0/8; // Localhost
10.0.0.0/8; // Internal RFC1918
172.16.0.0/12; // Internal RFC1918
192.168.0.0/16; // Internal RFC1918
};
acl "secondary-servers" {
192.0.2.2; // ns2
192.0.2.3; // ns3
};
options {
// Use ACLs in options
allow-query { any; }; // Anyone can query (authoritative)
allow-recursion { trusted-nets; }; // Only trusted nets can recurse
allow-transfer { secondary-servers; }; // Only secondaries can transfer
blackhole { 198.51.100.0/24; }; // Drop all traffic from this range
};Built-in ACLs#
| Name | Matches |
|---|---|
any | All hosts |
none | No hosts |
localhost | Server's own IP addresses |
localnets | Networks directly attached to the server |
7. Recursive vs Authoritative Configuration#
Authoritative-Only Server#
Serves answers only for zones it is configured to host:
options {
directory "/var/cache/bind";
recursion no; // Disable recursion
allow-query { any; }; // Answer queries from anyone
allow-transfer { secondary-servers; };
dnssec-validation no; // Not needed for authoritative-only
minimal-responses yes; // Reduce response size
};Recursive Resolver Only#
Resolves queries on behalf of clients; does not host any zones (except root hints):
options {
directory "/var/cache/bind";
recursion yes;
allow-recursion { trusted-nets; };
allow-query { trusted-nets; };
allow-transfer { none; };
forwarders {
1.1.1.1;
9.9.9.9;
};
dnssec-validation auto;
// Limit cache size
max-cache-size 256m;
max-cache-ttl 86400;
max-ncache-ttl 3600;
};Split-Horizon DNS (Views)#
Serve different answers depending on the client's source address:
view "internal" {
match-clients { trusted-nets; };
recursion yes;
zone "example.com" {
type primary;
file "/var/cache/bind/db.example.com.internal";
};
};
view "external" {
match-clients { any; };
recursion no;
zone "example.com" {
type primary;
file "/var/cache/bind/db.example.com.external";
};
};8. Zone Transfers#
Zone transfers replicate zone data from primary to secondary servers.
AXFR (Full Zone Transfer)#
Transfers the complete zone file. Used on initial synchronization or when IXFR is unavailable.
IXFR (Incremental Zone Transfer)#
Transfers only the changes since the last transfer based on serial number difference. More efficient for large zones with small changes.
Primary Configuration#
zone "example.com" IN {
type primary;
file "/var/cache/bind/db.example.com";
allow-transfer { secondary-servers; };
also-notify { 192.0.2.2; 192.0.2.3; };
notify yes;
};Secondary Configuration#
zone "example.com" IN {
type secondary;
file "/var/cache/bind/db.example.com";
primaries { 192.0.2.1; };
allow-transfer { none; };
};TSIG Authentication#
Secure zone transfers with Transaction Signatures (TSIG):
# Generate a TSIG key
tsig-keygen -a hmac-sha256 transfer-key > /etc/bind/transfer-key.confThe generated file contains:
key "transfer-key" {
algorithm hmac-sha256;
secret "base64-encoded-secret==";
};Include the key on both primary and secondary, then reference it:
// Primary
include "/etc/bind/transfer-key.conf";
zone "example.com" IN {
type primary;
file "/var/cache/bind/db.example.com";
allow-transfer { key "transfer-key"; };
also-notify { 192.0.2.2; };
};
// Secondary
include "/etc/bind/transfer-key.conf";
server 192.0.2.1 {
keys { "transfer-key"; };
};
zone "example.com" IN {
type secondary;
primaries { 192.0.2.1; };
file "/var/cache/bind/db.example.com";
};NOTIFY#
When a zone is updated on the primary, BIND sends NOTIFY messages to all NS records and addresses in also-notify. Secondaries receiving a NOTIFY immediately check the SOA serial and initiate a transfer if needed.
zone "example.com" {
type primary;
notify explicit; // Only notify hosts in also-notify
also-notify { 192.0.2.2; };
};9. DNSSEC#
DNSSEC adds cryptographic signatures to DNS records, allowing resolvers to verify that responses have not been tampered with.
Enable DNSSEC Validation (Resolver)#
In named.conf.options:
options {
dnssec-validation auto;
};This uses the built-in root trust anchors to validate signed zones.
Signing a Zone (Authoritative)#
Generate Keys#
# Generate a Zone Signing Key (ZSK)
dnssec-keygen -a ECDSAP256SHA256 -n ZONE example.com
# Output: Kexample.com.+013+12345
# Generate a Key Signing Key (KSK)
dnssec-keygen -a ECDSAP256SHA256 -n ZONE -f KSK example.com
# Output: Kexample.com.+013+67890Include Keys in Zone File#
Add the generated key files to the zone:
; At the end of db.example.com
$INCLUDE Kexample.com.+013+12345.key ; ZSK
$INCLUDE Kexample.com.+013+67890.key ; KSKSign the Zone#
# Sign the zone file, producing db.example.com.signed
dnssec-signzone -o example.com -N INCREMENT -K /etc/bind/keys/ \
/var/cache/bind/db.example.comUpdate Zone Configuration#
zone "example.com" {
type primary;
file "/var/cache/bind/db.example.com.signed";
allow-transfer { secondary-servers; };
key-directory "/etc/bind/keys";
auto-dnssec maintain; // Automatically re-sign when keys/zone change
inline-signing yes; // Sign on the fly without manual signing
};Submit DS Record to Registrar#
The DS (Delegation Signer) record establishes the chain of trust from the parent zone to your zone.
Extract the DS Record#
# Generate DS records from the KSK
dnssec-dsfromkey Kexample.com.+013+67890.key
# Output:
# example.com. IN DS 67890 13 2 <hex-digest-sha256>Submit to Registrar#
- Log in to your domain registrar
- Navigate to DNSSEC management for your domain
- Add a DS record with these values:
- Key Tag: 67890 (from the DS output)
- Algorithm: 13 (ECDSAP256SHA256)
- Digest Type: 2 (SHA-256)
- Digest: The hex string from the DS output
Verify the Chain of Trust#
# Check that DNSSEC is properly configured end-to-end
dig +dnssec +multi example.com SOA
# Verify the DS record is published in the parent zone
dig +short example.com DS
# Use an online validator
# https://dnsviz.net/d/example.com/dnssec/Key Rollover#
ZSK rollover (recommended every 1-3 months):
# Generate a new ZSK
dnssec-keygen -a ECDSAP256SHA256 -n ZONE example.com
# Include the new key in the zone, re-sign
# Wait for old signatures to expire (2x TTL), then remove old ZSKKSK rollover (recommended annually, requires DS record update at registrar):
- Generate a new KSK
- Publish both old and new KSK in the zone
- Submit the new DS record to the registrar
- Wait for the old DS to be removed and caches to expire
- Remove the old KSK from the zone
10. Performance Tuning#
Worker Threads#
options {
// Match to number of CPU cores
// BIND9 auto-detects by default
};Cache Tuning#
options {
max-cache-size 512m; // Limit cache memory usage
max-cache-ttl 86400; // Maximum positive cache TTL (1 day)
max-ncache-ttl 3600; // Maximum negative cache TTL (1 hour)
cleaning-interval 60; // Cache cleanup interval (minutes)
};Rate Limiting#
Protect against DNS amplification attacks:
options {
rate-limit {
responses-per-second 10; // Max responses per second per client
window 5; // Tracking window in seconds
slip 2; // Send truncated responses every N drops
all-per-second 100; // Overall rate limit
errors-per-second 5; // NXDOMAIN/SERVFAIL rate
};
};Minimal Responses#
Reduce response size to improve performance and reduce amplification risk:
options {
minimal-responses yes; // Omit unnecessary AUTHORITY/ADDITIONAL sections
minimal-any yes; // Return minimal response for ANY queries
};11. Testing and Validation#
Configuration Syntax Check#
named-checkconf
named-checkconf -z # Also check zone filesZone File Validation#
named-checkzone example.com /var/cache/bind/db.example.comService Management#
# Restart (full reload)
sudo systemctl restart named # RHEL/Fedora
sudo systemctl restart bind9 # Debian/Ubuntu
# Reload configuration without restart
sudo rndc reload
# Reload a specific zone
sudo rndc reload example.com
# Flush cache (recursive resolver)
sudo rndc flush
# View server status
sudo rndc statusQuery Testing#
# Basic query
dig @localhost example.com A
# Query with DNSSEC validation
dig @localhost +dnssec example.com A
# Trace the full resolution path
dig +trace example.com
# Test zone transfer
dig @ns1.example.com example.com AXFR
# Reverse lookup
dig @localhost -x 203.0.113.10
# Short output
dig +short example.com ATroubleshooting#
| Issue | Cause | Solution |
|---|---|---|
zone example.com/IN: loading from master file failed: file not found | Zone file path incorrect | Verify file path in zone definition; check permissions |
zone example.com/IN: not loaded due to errors | Syntax error in zone file | Run named-checkzone example.com /path/to/zonefile |
| SERVFAIL for valid domains | DNSSEC validation failure or forwarder issue | Check dnssec-validation setting; test with dig +cd to bypass validation |
| Zone transfer refused | ACL blocking the secondary | Add secondary IP to allow-transfer; verify TSIG key matches on both sides |
| Serial number not incrementing | Forgot to update serial | Increment serial in SOA; use YYYYMMDDNN format |
| High memory usage | Cache too large | Set max-cache-size to a reasonable limit |
| Slow query response | Recursive resolution bottleneck | Add forwarders; enable prefetch for popular records |
rndc: connect failed: connection refused | rndc not configured or key mismatch | Run rndc-confgen and update rndc.conf and named.conf |
| NOTIFY not reaching secondary | Firewall blocking port 53 TCP/UDP | Ensure both TCP and UDP port 53 are open between primary and secondary |