Learn through the super-clean Baeldung Pro experience:
>> Membership and Baeldung Pro.
No ads, dark-mode and 6 months free of IntelliJ Idea Ultimate to start with.
Last updated: July 19, 2025
The Domain Name System (DNS) is like a phonebook for the internet. It translates human-readable domain names (like baeldung.com) into IP addresses that computers use to find each other.
Furthermore, configuring our own DNS server with BIND9 gives us more control over this process. BIND9 is a popular DNS server software for Linux, and it’s quite powerful. Consequently, it enables us to manage how devices on our network resolve domain names to IP addresses.
In this tutorial, we’ll walk through the process of setting up a BIND9 DNS server.
Before we install and configure BIND9, let’s prepare our server.
Let’s give our server a proper name. We need to give our server a hostname so we can easily identify it on the network.
We can set the hostname by editing the /etc/hostname file:
$ cat /etc/hostname
baeldung-dns
Inside the /etc/hostname file, we replace the existing hostname with our desired one, baeldung-dns.
Now that our server has a name, we need to make sure it knows its own address. Let’s do this by updating the /etc/hosts file:
$ sudo vim /etc/hosts
Next, let’s add a line that associates our new hostname with the server’s static IP address:
$ cat /etc/hosts
195.133.93.153 baeldung-dns.example.com baeldung-dns
127.0.0.1 localhost
...
This tells the server that the hostname baeldung-dns refers to itself and that its fully qualified domain name (FQDN) is baeldung-dns.example.com.
Finally, let’s restart our server to apply these changes:
$ sudo reboot
Once the server restarts, it uses the new hostname and IP address.
First, let’s ensure our system is up-to-date. We can do this by running a quick update:
$ sudo apt update && sudo apt upgrade -y
Next, let’s install the BIND9 package, along with some useful utilities and documentation:
$ sudo apt install bind9 bind9utils bind9-doc -y
The command downloads and installs BIND9, giving us the tools we need to set up our DNS server.
Furthermore, as soon as the installation is complete, let’s ensure the BIND9 service is up and running:
$ sudo systemctl status bind9
named.service - BIND Domain Name Server
Loaded: loaded (/lib/systemd/system/named.service; enabled; vendor preset: enabled)
Active: active (running) since Sat 2024-10-05 09:06:07 UTC; 1min 58s ago
Docs: man:named(8)
Main PID: 12196 (named)
Tasks: 14 (limit: 4537)
Memory: 8.3M
CGroup: /system.slice/named.service
└─12196 /usr/sbin/named -f -u bind
Hence, if the installation went smoothly, we should see that BIND9 is active (running) as shown above.
With our server properly set up and BIND9 installed, let’s configure BIND9 itself.
The first step in configuring BIND9 is to set up forwarders. Think of forwarders as asking a friend for help when we can’t find something ourselves. In this case, if our DNS server can’t resolve a domain name, it can ask these external DNS servers for assistance.
To configure forwarders, we need to edit the named.conf.options file. This file contains global options for our DNS server:
$ sudo vim /etc/bind/named.conf.options
Inside this file, find a section called forwarders. This is where we’ll add the IP addresses of external servers we want to use.
Additionally, we can use public DNS servers like Google Public DNS or Cloudflare DNS, or we can specify the DNS servers provided by our internet service provider.
Let’s use Google’s DNS servers in our configuration:
$ cat /etc/bind/named.conf.options
...
forwarders {
8.8.8.8;
8.8.4.4;
};
...
Consequently, this tells our DNS server: “If you can’t find the answer yourself, ask these servers.” Moreover, we can add more IP addresses if we want to use additional forwarders.
After adding the forwarders, we need to save the file and restart BIND9 for the changes to take effect:
$ sudo systemctl restart bind9
With forwarders set up, our server can now handle external DNS lookups.
After configuring forwarders, we need to define our DNS zones. Zones are like sections in our DNS server’s address book, each responsible for a specific domain or subnet. Let’s set up two types of zones: a forward lookup zone and a reverse lookup zone.
The named.conf.local file is where we define these zones. Let’s open it with our text editor:
$ sudo vim /etc/bind/named.conf.local
First, let’s define our forward lookup zone. This zone handles translations from domain names to IP addresses.
Then, let’s add a zone block for our domain, specifying it as master and providing the location of its zone file:
$ cat /etc/bind/named.conf.local
# Forward zone
zone "example.com" {
type master;
file "/etc/bind/zones/db.example.com";
};
This tells our DNS server that it’s the master source of information for example.com and that the DNS records for this zone are stored in the /etc/bind/zones/db.example.com file.
Next, let’s define our reverse lookup zone. This zone handles translations from IP addresses to domain names.
Similarly, in the same file, let’s add another zone block for our subnet, again specifying its type as master and the location of its zone file:
$ cat /etc/bind/named.conf.local
...
# Reverse zone
zone "153.93.133.195.in-addr.arpa" {
type master;
file "/etc/bind/zones/db.195";
};
However, this configuration tells our DNS server that it’s responsible for reverse lookups of the IP address 195.133.93.153, and the corresponding records are stored in /etc/bind/zones/db.195.
Additionally, after configuring the zones, we need to create the corresponding zone files.
Now that we’ve told our DNS server which zones it’s responsible for, let’s create the actual zone files. These files are like dedicated address books containing the specific mappings between domain names and IP addresses, along with other important information.
First, we need a place to store these zone files. BIND9 expects them to be in a specific directory, so let’s create it:
$ sudo mkdir /etc/bind/zones
Next, let’s create the forward zone file. This file handles the “forward” lookups, translating domain names into IP addresses.
Hence, we can start with a template file provided by BIND9:
$ cat /etc/bind/db.local
;
; BIND data file for local loopback interface
;
$TTL 604800
@ IN SOA localhost. root.localhost. (
2 ; Serial
604800 ; Refresh
86400 ; Retry
2419200 ; Expire
604800 ) ; Negative Cache TTL
;
@ IN NS localhost.
@ IN A 127.0.0.1
@ IN AAAA ::1
Let’s copy this file into db.example.com and then customize it:
$ sudo cp /etc/bind/db.local /etc/bind/zones/db.example.com
This copies the template into our db.example.com file, which we can edit to suit our purpose. Inside the file, let’s define the DNS records that map domain names to IP addresses:
$ cat /etc/bind/zones/db.example.com
;
; BIND data file for local loopback interface
;
$TTL 604800
@ IN SOA baeldung-dns.example.com. admin.example.com. (
2 ; Serial
604800 ; Refresh
86400 ; Retry
2419200 ; Expire
604800 ) ; Negative Cache TTL
;
@ IN NS baeldung-dns.example.com.
@ IN A 195.133.93.153
baeldung-dns IN A 195.133.93.153
Here, we’ve set up baeldung-dns.example.com as the primary name server for our domain and mapped the domain to the IP address 195.133.93.153.
Now, let’s create the reverse zone file. This file handles reverse lookups, translating IP addresses back into domain names.
Similarly, let’s start with the template we’ll customize:
$ cat /etc/bind/db.127
;
; BIND reverse data file for local loopback interface
;
$TTL 604800
@ IN SOA localhost. root.localhost. (
1 ; Serial
604800 ; Refresh
86400 ; Retry
2419200 ; Expire
604800 ) ; Negative Cache TTL
;
@ IN NS localhost.
1.0.0 IN PTR localhost.
Let’s copy the above file into db.195:
$ sudo cp /etc/bind/db.127 /etc/bind/zones/db.195
Next, let’s modify the file to reflect the correct IP address range and hostname:
$ cat /etc/bind/zones/db.195
;
; BIND reverse data file for local loopback interface
;
$TTL 604800
@ IN SOA baeldung-dns.example.com. admin.example.com. (
1 ; Serial
604800 ; Refresh
86400 ; Retry
2419200 ; Expire
604800 ) ; Negative Cache TTL
;
@ IN NS baeldung-dns.example.com.
153 IN PTR baeldung-dns.example.com.
Similarly, this maps the last octet of the IP address (153 in 195.133.93.153) back to baeldung-dns.example.com.
First, let’s double-check our zone files using the tool named-checkzone. This tool helps us spot any errors in our configuration:
$ sudo named-checkzone example.com /etc/bind/zones/db.example.com
zone example.com/IN: loaded serial 2
Ok
If everything looks good, we’ll see the Ok message indicating that the zone was loaded successfully. Next, let’s verify the reverse zone file in the same way:
$ sudo named-checkzone example.com /etc/bind/zones/db.195
zone example.com/IN: loaded serial 1
Ok
Again, a successful output means our reverse zone is properly configured.
Let’s restart the BIND9 service to apply all these changes we’ve made:
$ sudo systemctl restart bind9
With the service restarted, we can check if our DNS server actually resolves names correctly.
Let’s test using the dig tool to confirm the DNS server is operational:
$ dig @195.133.93.153 example.com
; <<>> DiG 9.18.28-0ubuntu0.22.04.1-Ubuntu <<>> @195.133.93.153 example.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 8560
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
; COOKIE: db32f227c48dd64c01000000672f6b1aec7a72aaf5646099 (good)
;; QUESTION SECTION:
;example.com. IN A
;; ANSWER SECTION:
example.com. 604800 IN A 195.133.93.153
;; Query time: 0 msec
;; SERVER: 195.133.93.153#53(195.133.93.153) (UDP)
;; WHEN: Sat Nov 09 14:00:58 GMT 2024
;; MSG SIZE rcvd: 84
These results indicate that our BIND9 server is running as expected.
With our BIND9 server running, let’s configure our client machines to use it for name resolution. Moreover, this ensures that devices on our network can take advantage of our local DNS server for faster and more reliable name lookups.
For Linux clients, we need to edit the /etc/resolv.conf file to specify our server’s IP address:
$ sudo vim /etc/resolv.conf
Furthermore, inside this file, let’s add a line with the nameserver directive, followed by the IP address of our BIND9 server:
nameserver 195.133.93.153
Moreover, we can also add a search directive to specify the domain that should be used for unqualified names:
search example.com
Therefore, if a user on the client machine tries to access baeldung-dns (without the domain), the client will automatically try to resolve baeldung-dns.example.com.
After saving the changes, the client should start using our BIND9 server.
In this article, we walked through the process of setting up a BIND9 DNS server. We successfully set up a local DNS server that can efficiently resolve names within our network.
Additionally, this DNS server not only improves name resolution speed but also gives us greater control over our DNS records and improves privacy.