BIND can act as both your primary DNS server (the authoritative source for a zone) and a secondary DNS server (a read-only copy that synchronizes changes from the primary).
Here’s how to set up a simple primary/secondary pair for a fictional zone, example.com.
Let’s imagine our primary server has the IP 192.168.1.10 and our secondary server has the IP 192.168.1.20.
Primary Server Configuration (/etc/bind/named.conf.local on 192.168.1.10)
First, on the primary server, we define the zone and specify that it’s the master.
zone "example.com" {
type master;
file "/etc/bind/zones/db.example.com";
allow-transfer { 192.168.1.20; }; // Allow zone transfers to our secondary
};
Next, we create the zone file itself (/etc/bind/zones/db.example.com).
$TTL 604800
@ IN SOA ns1.example.com. admin.example.com. (
3 ; Serial
604800 ; Refresh
86400 ; Retry
2419200 ; Expire
604800 ) ; Negative Cache TTL
;
@ IN NS ns1.example.com.
@ IN NS ns2.example.com. // This will be our secondary
ns1 IN A 192.168.1.10
ns2 IN A 192.168.1.20
www IN A 192.168.1.100
The crucial part here is the Serial number (3 in this case). Every time you make a change to the zone file, you must increment this serial number. This is how secondary servers detect that new data is available. The allow-transfer directive is key; it explicitly permits the secondary server (192.168.1.20) to request a full copy of the zone.
After making these changes, reload BIND:
sudo systemctl reload named
Secondary Server Configuration (/etc/bind/named.conf.local on 192.168.1.20)
On the secondary server, we define the same zone but declare it as a slave. We also tell it where to get the zone data from (the primary server’s IP).
zone "example.com" {
type slave;
file "/var/cache/bind/db.example.com"; // Where BIND will store the zone copy
masters { 192.168.1.10; }; // The IP of our primary server
};
The masters directive tells BIND which IP address to query for zone transfers. The file directive specifies where BIND should store the downloaded zone data. This directory (/var/cache/bind/) must be writable by the BIND user (often bind or named).
Reload BIND on the secondary:
sudo systemctl reload named
At this point, the secondary server will attempt to contact the primary server, request the example.com zone, and store a local copy in /var/cache/bind/db.example.com.
Verification
On the secondary server, you can check the BIND logs for messages indicating a successful zone transfer. You can also use dig to query both servers:
On the primary (192.168.1.10):
dig @192.168.1.10 www.example.com A
On the secondary (192.168.1.20):
dig @192.168.1.20 www.example.com A
Both should return the same IP address for www.example.com (192.168.1.100).
Making Changes
To add a new record (e.g., a mail server mail.example.com pointing to 192.168.1.50):
-
On the primary server (
192.168.1.10):- Edit
/etc/bind/zones/db.example.com. - Increment the serial number to
4. - Add the new record:
mail IN A 192.168.1.50. - Reload BIND:
sudo systemctl reload named.
- Edit
-
On the secondary server (
192.168.1.20):- BIND will automatically detect the serial number change (via checking with the master at the refresh interval defined in the SOA record, or via NOTIFY if configured).
- It will then initiate a zone transfer and update its local copy.
- You can force a check by using
rndc retransfer example.comon the secondary.
The most surprising true thing about BIND’s zone transfers is that the Refresh timer in the SOA record is the maximum time the secondary will wait before checking for updates, but the primary server can (and usually does) proactively notify secondaries of changes using the DNS NOTIFY mechanism, making updates nearly instantaneous if both servers are properly configured to support it.
The next concept you’ll likely explore is DNSSEC for securing your DNS records.