How to Configure BIND in Linux

how to configure BIND in Linux

how to configure BIND in Linux

BIND (Berkeley Internet Name Domain) is the most widely deployed DNS server software in the world. It powers everything from small internal company DNS to some of the internet’s root name servers. If you want to run your own authoritative DNS server — hosting real zones for your own domains — BIND is the industry-standard tool for the job.

This article walks through installing BIND, understanding its configuration structure, creating forward and reverse zones, and securing and troubleshooting your deployment — building on concepts from caching DNS servers but going a step further into full authoritative DNS hosting.


Caching vs. Authoritative: An Important Distinction

Before diving in, it’s important to understand the difference:

Server TypeRole
Caching/RecursiveAnswers queries by asking other servers and remembering the results temporarily
AuthoritativeHolds the actual, official records for a domain and answers directly with no caching involved

BIND can be configured as either — or both — but in this article, we focus on configuring BIND as an authoritative server for your own domain zones.

flowchart LR
    A[Internet Client] --> B[Authoritative BIND Server]
    B -->|Official Zone Data| A
    C[Zone File example.com.zone] --> B

Step 1: Install BIND

Debian/Ubuntu

sudo apt update
sudo apt install bind9 bind9utils bind9-doc -y

RHEL/CentOS/Rocky

sudo dnf install bind bind-utils -y

Step 2: Understand the File Structure

FilePurpose
/etc/bind/named.conf (Debian) or /etc/named.conf (RHEL)Main configuration entry point
named.conf.optionsGlobal server options
named.conf.localCustom zone declarations
/var/lib/bind/ or /var/named/Where zone files are stored

Step 3: Define a Forward Zone

A forward zone maps hostnames to IP addresses (the most common DNS use case).

Edit named.conf.local (Debian) or add to named.conf (RHEL):

zone "example.com" {
    type master;
    file "/etc/bind/zones/db.example.com";
    allow-transfer { 203.0.113.5; };  // secondary DNS server
};

Create the zones directory and file:

sudo mkdir -p /etc/bind/zones
sudo nano /etc/bind/zones/db.example.com

Sample Zone File

$TTL    86400
@       IN      SOA     ns1.example.com. admin.example.com. (
                        2026072401  ; Serial (YYYYMMDDnn)
                        3600        ; Refresh
                        1800        ; Retry
                        604800      ; Expire
                        86400 )     ; Minimum TTL

; 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.12

; MX record
@       IN      MX  10  mail.example.com.

; CNAME record
ftp     IN      CNAME   www.example.com.

; TXT record (e.g., SPF)
@       IN      TXT     "v=spf1 mx ~all"

Explanation of the SOA Record Fields

FieldMeaning
SerialVersion number of the zone file — must increase every time you edit it, or secondary servers won’t notice the change
RefreshHow often secondaries check for updates
RetryHow long secondaries wait before retrying a failed refresh
ExpireHow long secondaries keep serving stale data if the primary is unreachable
Minimum TTLDefault TTL for negative caching (NXDOMAIN responses)

Step 4: Define a Reverse Zone

A reverse zone maps IP addresses back to hostnames (used for PTR lookups — important for mail server reputation and diagnostics).

Add to named.conf.local:

zone "113.0.203.in-addr.arpa" {
    type master;
    file "/etc/bind/zones/db.203.0.113";
};

Create the file:

sudo nano /etc/bind/zones/db.203.0.113
$TTL    86400
@       IN      SOA     ns1.example.com. admin.example.com. (
                        2026072401
                        3600
                        1800
                        604800
                        86400 )

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

10      IN      PTR     www.example.com.
11      IN      PTR     ns2.example.com.
12      IN      PTR     mail.example.com.

Note the reversed octet order in the zone name (113.0.203.in-addr.arpa for the 203.0.113.0/24 network).


Step 5: Validate and Apply

Always validate configuration and zone files before restarting:

sudo named-checkconf
sudo named-checkzone example.com /etc/bind/zones/db.example.com
sudo named-checkzone 113.0.203.in-addr.arpa /etc/bind/zones/db.203.0.113

Restart the service:

sudo systemctl restart bind9

Step 6: Test Your Zones

dig @localhost example.com
dig @localhost www.example.com
dig @localhost -x 203.0.113.10

Setting Up a Secondary (Slave) DNS Server

For redundancy, real-world DNS deployments almost always use at least two servers: a master and one or more secondaries that automatically pull (“transfer”) zone data from the master.

sequenceDiagram
    participant Master as Master DNS (ns1)
    participant Slave as Secondary DNS (ns2)
    Slave->>Master: Query SOA serial number
    Master-->>Slave: Return current serial
    Slave->>Master: Request zone transfer (AXFR/IXFR)
    Master-->>Slave: Send zone data
    Slave->>Slave: Update local zone copy

On the Master

Already handled by the allow-transfer line shown earlier.

On the Secondary Server

zone "example.com" {
    type slave;
    file "/var/cache/bind/db.example.com";
    masters { 203.0.113.10; };
};

Restart BIND on the secondary — it will automatically pull the zone from the master.


Comparison Table: Zone Types in BIND

Zone TypeDescriptionUse Case
masterHolds the original, editable copy of a zonePrimary authoritative server
slaveAutomatically syncs a read-only copy from a masterRedundancy, load distribution
forwardSends all queries for a zone to another serverSplit-DNS environments
stubCaches only the NS records of a zone, not full dataRarely used, specialized delegation cases

Real-World Example: Company with Internal and External DNS (Split-DNS)

Many companies run split-horizon DNS — the same domain name resolves differently depending on whether the query comes from inside or outside the company network. For example, mail.example.com might resolve to a public IP externally, but to an internal private IP for employees on the office network.

BIND supports this using views:

acl "internal" { 192.168.1.0/24; };
acl "external" { any; };

view "internal-view" {
    match-clients { internal; };
    zone "example.com" {
        type master;
        file "/etc/bind/zones/db.example.com.internal";
    };
};

view "external-view" {
    match-clients { external; };
    zone "example.com" {
        type master;
        file "/etc/bind/zones/db.example.com.external";
    };
};

This lets internal employees reach internal-only resources by name, while external users only ever see public-facing IPs.


Cisco Example: Integrating with Network ACLs

In real deployments, DNS traffic (port 53, both UDP and TCP) often needs explicit firewall or ACL permission on Cisco devices sitting in front of a BIND server:

ip access-list extended ALLOW_DNS
 permit udp any host 203.0.113.10 eq 53
 permit tcp any host 203.0.113.10 eq 53

TCP port 53 matters too — not just UDP — because zone transfers and large DNS responses (like DNSSEC-signed records) often require TCP.


Python Example: Automating Zone File Serial Updates

Forgetting to bump the SOA serial number is one of the most common BIND mistakes — secondaries won’t pick up changes if you forget. Here’s a small Python helper to automate it:

import re
from datetime import date

def bump_serial(zone_file_path):
    with open(zone_file_path, 'r') as f:
        content = f.read()

    today = date.today().strftime('%Y%m%d')
    match = re.search(r'(\d{10})\s*;\s*Serial', content)

    if match:
        old_serial = match.group(1)
        if old_serial.startswith(today):
            new_serial = str(int(old_serial) + 1)
        else:
            new_serial = today + "01"
        content = content.replace(old_serial, new_serial)

        with open(zone_file_path, 'w') as f:
            f.write(content)
        print(f"Serial updated: {old_serial} -> {new_serial}")
    else:
        print("No serial number found")

bump_serial('/etc/bind/zones/db.example.com')

Best Practices


Troubleshooting

SymptomLikely CauseFix
Zone not loadingSyntax error in zone fileRun named-checkzone
Secondary not updatingSerial number not incremented, or transfer blockedBump serial; check allow-transfer and firewall rules for TCP/53
dig shows old records after editBIND didn’t reloadRun sudo rndc reload
REFUSED on external queriesallow-query too restrictiveAdjust ACLs appropriately for public vs internal zones
Reverse lookups failReverse zone missing or misconfigured PTR recordVerify in-addr.arpa zone and PTR entries

Useful Diagnostic Commands

sudo rndc reload                     # Reload all zones without restart
sudo rndc reload example.com         # Reload a single zone
sudo rndc status                     # Show server status
sudo tail -f /var/log/syslog | grep named

Summary

BIND remains the gold standard for running authoritative DNS on Linux. Once you understand the relationship between named.conf, zone declarations, and zone files themselves — plus concepts like SOA serials, forward/reverse zones, secondaries, and split-DNS views — you have the foundation to run production-grade DNS infrastructure for any organization, from a small business to a large enterprise.


Further Reading

Exit mobile version