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#

  1. Overview
  2. DNS Server Types
  3. Installation
  4. Core Configuration
  5. Zone Files
  6. Access Control Lists
  7. Recursive vs Authoritative Configuration
  8. Zone Transfers
  9. DNSSEC
  10. Performance Tuning
  11. Testing and Validation
  12. Troubleshooting
  13. See Also
  14. 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/secondary terminology. The legacy master/slave syntax is deprecated and will be removed in a future release.

2. DNS Server Types#

TypeDescriptionUse Case
Primary (Master)Holds the original read-write zone dataZone management, record updates
Secondary (Slave)Maintains a read-only copy transferred from the primaryRedundancy, geographic distribution
Caching/RecursiveResolves queries by recursing through the DNS hierarchyClient-facing resolver
ForwarderForwards queries to another resolver instead of recursing directlyInternal networks, policy enforcement

3. Installation#

Debian/Ubuntu#

sudo apt-get install bind9 bind9utils bind9-doc dnsutils

RHEL/Fedora#

sudo dnf install bind bind-utils

Arch Linux#

sudo pacman -S bind

Verify Installation#

named -v
# BIND 9.18.x ...

File Locations (Debian/Ubuntu)#

FilePurpose
/etc/bind/named.confMain configuration (includes others)
/etc/bind/named.conf.optionsGlobal options (forwarders, DNSSEC, ACLs)
/etc/bind/named.conf.localZone definitions
/var/cache/bind/Zone file storage and journal files
/var/log/named/Log files (if configured)

File Locations (RHEL/Fedora)#

FilePurpose
/etc/named.confMain 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/named

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

FieldPurposeTypical Value
Primary NSAuthoritative name serverns1.example.com.
Admin emailContact (. replaces @)admin.example.com.
SerialVersion number; increment on every changeYYYYMMDDNN
RefreshHow often secondaries check for updates7200 (2h)
RetryRetry interval if refresh fails1200 (20m)
ExpireHow long secondary keeps serving stale data2419200 (4w)
Negative TTLCache duration for NXDOMAIN responses60 (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#

NameMatches
anyAll hosts
noneNo hosts
localhostServer's own IP addresses
localnetsNetworks 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.conf

The 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+67890

Include 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   ; KSK

Sign 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.com

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

  1. Log in to your domain registrar
  2. Navigate to DNSSEC management for your domain
  3. 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 ZSK

KSK rollover (recommended annually, requires DS record update at registrar):

  1. Generate a new KSK
  2. Publish both old and new KSK in the zone
  3. Submit the new DS record to the registrar
  4. Wait for the old DS to be removed and caches to expire
  5. 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 files

Zone File Validation#

named-checkzone example.com /var/cache/bind/db.example.com

Service 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 status

Query 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 A

Troubleshooting#

IssueCauseSolution
zone example.com/IN: loading from master file failed: file not foundZone file path incorrectVerify file path in zone definition; check permissions
zone example.com/IN: not loaded due to errorsSyntax error in zone fileRun named-checkzone example.com /path/to/zonefile
SERVFAIL for valid domainsDNSSEC validation failure or forwarder issueCheck dnssec-validation setting; test with dig +cd to bypass validation
Zone transfer refusedACL blocking the secondaryAdd secondary IP to allow-transfer; verify TSIG key matches on both sides
Serial number not incrementingForgot to update serialIncrement serial in SOA; use YYYYMMDDNN format
High memory usageCache too largeSet max-cache-size to a reasonable limit
Slow query responseRecursive resolution bottleneckAdd forwarders; enable prefetch for popular records
rndc: connect failed: connection refusedrndc not configured or key mismatchRun rndc-confgen and update rndc.conf and named.conf
NOTIFY not reaching secondaryFirewall blocking port 53 TCP/UDPEnsure both TCP and UDP port 53 are open between primary and secondary

See Also#

Sources#