Every time a Visitor types a domain name into a browser, a process takes place behind the scenes before even a single byte of a web page reaches their screen: that domain name must first be translated into an IP address. This translation process is the job of the DNS (Domain Name System), and in many infrastructures, whether for public domains or internal office networks, Sysadmins themselves are responsible for running their DNS servers. Developers need internal domains like db.internal.local to point to database servers without having to memorize IPs, or companies need their own DNS servers so they do not rely entirely on third-party resolvers, which is where BIND9 comes in as one of the most mature and widely used DNS server software in the world.
This chapter continues the network discussion from Chapter 8 and assumes the server already has a stable static IP, since a DNS server whose address keeps changing is clearly not a good idea. We will start with a brief review of DNS concepts, proceed to the installation and configuration of BIND9 as an authoritative server complete with forward and reverse zones, learn about the primary-secondary scheme for redundancy, test resolution using dig and nslookup, and close with the configuration of a simple caching DNS server.
9.1 DNS Concepts
DNS works like a giant, distributed phone book across the world: humans find it easier to remember names like example.com rather than IP addresses like 93.184.216.34, while machines and routers still need IP addresses to actually send data packets. DNS bridges these two needs. Without DNS, the internet could still technically function, but it would be nearly impossible for humans to use on a daily basis.
The DNS structure is hierarchical, similar to a directory structure from root to leaves. At the very top is the root zone (represented by a trailing dot at the end of a domain name), below it are TLDs (Top-Level Domains) such as .com, .id, or .org, followed by registered domains like example.com, which can further branch out into subdomains like www.example.com or db.internal.example.com. Each segment in this hierarchy is managed by a server called an authoritative nameserver, which holds the original and valid data for that zone, as opposed to a recursive resolver whose task is to query other servers on behalf of the client and temporarily store the results in cache.
When an application on a User's computer requests the address www.example.com, the resolver queries sequentially starting from the root server, gets directed to the .com server, and then gets redirected again to the authoritative nameserver belonging to example.com, until it finally gets the correct IP address response. This step-by-step process is called an iterative query, and the result is temporarily stored in the resolver's cache so that subsequent requests for the same domain do not need to repeat the entire process from scratch.
DNS data itself is stored in the form of resource records, each with a type serving a different function. The types most commonly used by Sysadmins are:
- A: maps a domain name to an IPv4 address.
- AAAA: maps a domain name to an IPv6 address.
- CNAME: alias, points one domain name to another domain name.
- MX: points to the mail server handling email for that domain.
- NS: points to the authoritative nameserver managing the zone.
- PTR: the reverse of A, maps an IP address back to a domain name (reverse DNS).
- SOA: Start of Authority, stores zone metadata such as serial number and refresh intervals.
- TXT: stores free text, often used for domain verification or email policies like SPF.
This conceptual foundation is enough to dive into practice. The next section will show how all the terms above actually appear in real BIND9 configuration files.
9.2 BIND9 Installation and Configuration
BIND9 (Berkeley Internet Name Domain, version 9) is DNS server software developed by the Internet Systems Consortium (ISC) and serves as the most dominant DNS server implementation in Linux environments, including being the standard DNS server package in Ubuntu repositories. Sysadmins use it both to serve public zones accessible to Visitors from the internet and internal zones used only by Developers and applications inside the office network.
9.2.1 Installing the BIND9 Package
Installing BIND9 on Ubuntu Server involves several complementary packages: bind9 as the main daemon, bind9utils for utility tools like configuration validation, and bind9-dnsutils which provides dig and nslookup for testing later in Section 9.3.
Practical Steps
- Update the package list, then install BIND9 along with its supporting tools.
sudo apt update sudo apt install bind9 bind9utils bind9-dnsutils - Check the version of BIND9 that is actually installed. Always verify this version directly on the server, as package versions can vary across Ubuntu releases.
named -v dpkg -l bind9 - Ensure the
bind9service automatically runs and stays active on boot after installation.sudo systemctl status bind9 sudo systemctl enable bind9
Verification and Troubleshooting
- Ensure BIND9 is actually listening on port 53, for both UDP protocol (standard DNS queries) and TCP (used for zone transfers and large responses).
sudo ss -lntup | grep named - Open port 53 if the server uses UFW so that other clients on the network can send queries to this DNS server.
sudo ufw allow 53 - In production, BIND9 on Ubuntu runs under an AppArmor profile (see Chapter 33) which restricts which directories the
namedprocess is allowed to access. If zone files are placed outside of/etc/bind/or/var/cache/bind/later, BIND9 might fail to read those files with a permission denied message in the logs even if regular Linux permissions are correct. Checkjournalctl -u bind9and/var/log/syslogfor messages pointing to AppArmor if this happens.
9.2.2 Structure of named.conf Configuration
The entire BIND9 configuration on Ubuntu is centered in the /etc/bind/ directory. The main file is named.conf, but its content consists only of three include lines that split the configuration into three separate files for cleaner management.
Practical Steps
- View the contents of
named.confto understand its file division.cat /etc/bind/named.conf named.conf.optionsstores global settings such as working directories and forwarders. Open this file to see its default configuration.sudo nano /etc/bind/named.conf.optionsnamed.conf.localis where we define custom zones, and it is usually empty (containing only comments) after a fresh installation. This file is the one most frequently edited by Sysadmins.sudo nano /etc/bind/named.conf.localnamed.conf.default-zonescontains built-in zones likelocalhostand reverse zones for127.0.0.1. Leave this file as is, since BIND9 needs its contents to function normally.cat /etc/bind/named.conf.default-zones
Next, we will add custom zones through named.conf.local, complete with zone files whose contents actually hold the DNS records.
9.2.3 Zone Files: Forward and Reverse
There are two mapping directions that need to be configured separately. Forward zone maps domain names to IP addresses; this is what Visitor browsers use every day. Reverse zone does the opposite, mapping IP addresses back to domain names via PTR records, often required for mail server verification or network auditing purposes.
Practical Steps
- Register the forward zone and reverse zone in
named.conf.local. The following example uses the internal domainexample.localand the network192.168.1.0/24; adjust according to the actual network scheme used.zone "example.local" { type primary; file "/etc/bind/db.example.local"; }; zone "1.168.192.in-addr.arpa" { type primary; file "/etc/bind/db.192"; }; - Create the forward zone file by copying BIND9's built-in local zone template as a starting point.
sudo cp /etc/bind/db.local /etc/bind/db.example.local sudo nano /etc/bind/db.example.local - Adjust its content with the records actually needed. Pay attention to the trailing dot at the end of complete domain names (Fully Qualified Domain Name/FQDN), an important marker that distinguishes absolute names from relative names in zone files.
; ; BIND data file for example.local ; $TTL 604800 @ IN SOA ns1.example.local. admin.example.local. ( 3 ; Serial 604800 ; Refresh 86400 ; Retry 2419200 ; Expire 604800 ) ; Negative Cache TTL ; @ IN NS ns1.example.local. @ IN A 192.168.1.10 ns1 IN A 192.168.1.10 www IN A 192.168.1.20 mail IN A 192.168.1.30 IN MX 10 mail.example.local. - Create the reverse zone file in a similar way.
sudo cp /etc/bind/db.127 /etc/bind/db.192 sudo nano /etc/bind/db.192 - Fill the reverse zone file with PTR records only, mapping the last octet of the IP address to the domain name.
; ; BIND reverse data file for 192.168.1.0/24 ; $TTL 604800 @ IN SOA ns1.example.local. admin.example.local. ( 1 ; Serial 604800 ; Refresh 86400 ; Retry 2419200 ; Expire 604800 ) ; Negative Cache TTL ; @ IN NS ns1.example.local. 10 IN PTR ns1.example.local. 20 IN PTR www.example.local.
Verification and Troubleshooting
- Always validate the syntax of the main configuration and zone files before reloading, similar to the
netplan generatehabit in Chapter 8. This command catches syntax errors before they are actually applied.sudo named-checkconf sudo named-checkzone example.local /etc/bind/db.example.local sudo named-checkzone 1.168.192.in-addr.arpa /etc/bind/db.192 - Once validation passes, reload BIND9 so the new zones are read.
sudo systemctl reload bind9 - The most common mistake here is forgetting to type the trailing dot at the end of an FQDN in fields like
NSandMX. BIND9 will treat it as a relative name and automatically append the zone name to the end if there is no trailing dot, resulting in an incorrect domain name without throwing an explicit error. - The Serial number in the SOA record must be incremented every time a zone file is modified. BIND9 uses this number to detect whether there are changes that need to be propagated to secondary servers, a topic discussed in the next section.
9.2.4 Primary and Secondary DNS Servers
Relying on a single DNS server is a single point of failure: all domain name resolution across the network collapses once that server goes down, even if other servers and applications are completely fine. The solution is running at least two servers, one acting as the primary (formerly called master) which holds the original zone file and serves as the sole place where changes are made, and another as the secondary (formerly called slave) which automatically copies data from the primary via a process called zone transfer.
Practical Steps
- On the primary server, add
allow-transferto restrict who can pull zone copies, andalso-notifyso the primary automatically notifies the secondary whenever changes occur.zone "example.local" { type primary; file "/etc/bind/db.example.local"; allow-transfer { 192.168.1.11; }; also-notify { 192.168.1.11; }; }; - Install BIND9 on the second server following the same steps as Section 9.2.1, then define the same zone with type
secondary, pointing to the primary server's IP address.zone "example.local" { type secondary; file "/var/cache/bind/db.example.local"; masters { 192.168.1.10; }; }; - Validate and reload the configuration on the secondary server.
sudo named-checkconf sudo systemctl reload bind9
Verification and Troubleshooting
- Check whether the zone transfer succeeded by looking at the zone file automatically created by BIND9 in the secondary server's cache directory.
ls -la /var/cache/bind/ sudo named-checkzone example.local /var/cache/bind/db.example.local - If the transfer does not happen, check the logs for zone transfer error messages. This is usually caused by an incorrect secondary IP written in
allow-transfer, or a firewall blocking TCP port 53 between the two servers.journalctl -u bind9 -b --no-pager | grep -i transfer - Every zone file modification on the primary requires incrementing the Serial number in SOA, then run
rndc notify example.localon the primary if you want to force the secondary to pull updates immediately without waiting for the SOA refresh interval to expire. - In production, this primary-secondary pattern is also what public domain providers use to keep DNS accessible even if one datacenter experiences an outage. The more critical a zone is, the more important it is to place the secondary in a network location completely separate from the primary.
9.3 Testing DNS Resolution: dig and nslookup
A zone configuration that looks correct in a file might not necessarily work as expected. dig (Domain Information Groper) is a Sysadmin's mainstay tool for testing DNS resolution in detail, while nslookup serves as a simpler alternative widely known since the older DNS server era.
Practical Steps
- Query the domain directly against the newly configured DNS server, without relying on the system's default resolver.
dig @192.168.1.10 example.local - Use the
+shortoption for a more concise output when you only need its IP address.dig @192.168.1.10 example.local +short - Test reverse DNS resolution using the
-xoption, using the IP address as input.dig @192.168.1.10 -x 192.168.1.20 - Query specific record types, such as MX to check mail server configuration.
dig @192.168.1.10 example.local MX - As an alternative,
nslookupcan also be used by explicitly specifying the DNS server.nslookup example.local 192.168.1.10
Verification and Troubleshooting
- Pay attention to the ANSWER SECTION in the
digoutput, which is where the records actually returned by the server are displayed. The flags section in the header is also important; the appearance of theaaflag (authoritative answer) indicates the response came directly from an authoritative server, not cached by another resolver. - If
digreturns anNXDOMAINstatus, the queried domain is indeed not registered in that zone, often caused by a typo in the domain name or the zone not being reloaded after editing. - If
digreturns aSERVFAILstatus, there is usually an issue on the server side, such as a zone file failing to load due to a syntax error that bypassed initial checks, or the destination server not running BIND9 at all. Recheck withnamed-checkconfandnamed-checkzoneon the target server. - Compare
digresults targeting your own server againstdigto a public resolver likedig @1.1.1.1 example.comfor public domains, making it clear whether the issue lies within your own zone configuration or on the external network side.
9.4 Simple Caching DNS Server
Not all DNS servers need to host their own zones. Many offices or internal networks only need a local DNS server tasked with forwarding queries to public resolvers like Cloudflare or Google, while temporarily storing the results in cache so that subsequent queries for the same domain are answered much faster without leaving the network again. A setup like this is called a caching-only server, and BIND9 can perform this role without needing a single zone defined locally.
Practical Steps
- Edit
named.conf.options, enablerecursion, and add forwarders, which are other resolvers that will be forwarded any query not belonging to locally owned zones.options { directory "/var/cache/bind"; recursion yes; allow-recursion { 192.168.1.0/24; localhost; }; forwarders { 1.1.1.1; 8.8.8.8; }; dnssec-validation auto; listen-on-v6 { any; }; }; - Validate and reload the configuration as usual.
sudo named-checkconf sudo systemctl reload bind9
Verification and Troubleshooting
- Compare the response times of the same query run twice consecutively. The first query must go through the full process to the forwarders, while the second query is answered directly from local cache and is significantly faster.
dig @192.168.1.10 ubuntu.com +noall +stats dig @192.168.1.10 ubuntu.com +noall +stats - Notice the Query time value in both outputs; a drastic reduction on the second query indicates the cache is working as intended.
- Use
rndc flushinstead of waiting for record TTLs to expire on their own if you need to manually clear the cache, such as when testing DNS changes on the public domain side.sudo rndc flush - Restrict
allow-recursionto the internal network only, never open it toanyon a server that is also accessible from the internet. A DNS server with open recursion to the public (an open resolver) is vulnerable to exploitation by Attackers as part of DNS amplification attacks, where small queries are spoofed to appear as if coming from a victim, causing the server to flood the victim with significantly larger responses. This is one of the most dangerous DNS misconfigurations that Sysadmins must avoid.
Up to this point, we have covered BIND9 from core DNS concepts, installation, and configuration of both forward and reverse zones, primary-secondary schemes for redundancy, testing resolution using dig and nslookup, to securing a caching server setup. Chapter 10 will continue network topics with DHCP servers, including how to integrate them with the DNS we just built in this chapter.

