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 Type | Role |
|---|---|
| Caching/Recursive | Answers queries by asking other servers and remembering the results temporarily |
| Authoritative | Holds 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] --> BStep 1: Install BIND
Debian/Ubuntu
sudo apt update
sudo apt install bind9 bind9utils bind9-doc -yRHEL/CentOS/Rocky
sudo dnf install bind bind-utils -yStep 2: Understand the File Structure
| File | Purpose |
|---|---|
/etc/bind/named.conf (Debian) or /etc/named.conf (RHEL) | Main configuration entry point |
named.conf.options | Global server options |
named.conf.local | Custom 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.comSample 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
| Field | Meaning |
|---|---|
| Serial | Version number of the zone file — must increase every time you edit it, or secondary servers won’t notice the change |
| Refresh | How often secondaries check for updates |
| Retry | How long secondaries wait before retrying a failed refresh |
| Expire | How long secondaries keep serving stale data if the primary is unreachable |
| Minimum TTL | Default 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.113Restart the service:
sudo systemctl restart bind9Step 6: Test Your Zones
dig @localhost example.com
dig @localhost www.example.com
dig @localhost -x 203.0.113.10Setting 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 copyOn 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 Type | Description | Use Case |
|---|---|---|
master | Holds the original, editable copy of a zone | Primary authoritative server |
slave | Automatically syncs a read-only copy from a master | Redundancy, load distribution |
forward | Sends all queries for a zone to another server | Split-DNS environments |
stub | Caches only the NS records of a zone, not full data | Rarely 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 53TCP 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
- Always increment the SOA serial after any zone file edit.
- Validate before restarting using
named-checkconfandnamed-checkzoneevery single time. - Run at least two DNS servers (master + secondary) for redundancy.
- Restrict zone transfers with
allow-transferto prevent unauthorized copying of your entire DNS zone (a common information-leak vector). - Enable DNSSEC signing for zones you control, to protect against spoofing.
- Use views for split-DNS rather than maintaining entirely separate DNS server instances.
- Keep TTLs reasonable — very long TTLs slow down changes propagating; very short TTLs increase load.
Troubleshooting
| Symptom | Likely Cause | Fix |
|---|---|---|
| Zone not loading | Syntax error in zone file | Run named-checkzone |
| Secondary not updating | Serial number not incremented, or transfer blocked | Bump serial; check allow-transfer and firewall rules for TCP/53 |
dig shows old records after edit | BIND didn’t reload | Run sudo rndc reload |
REFUSED on external queries | allow-query too restrictive | Adjust ACLs appropriately for public vs internal zones |
| Reverse lookups fail | Reverse zone missing or misconfigured PTR record | Verify 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 namedSummary
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.
