Building a Simple DNS using BIND9

Nail the CodeNail the Code
6 min read

Table of contents

Alright I did my theoretical basics and blogged about DNS fundamentals, so now it is time to put in more practical work. Get some of those muscles working...

For today's mini project I will be setting up my very own DNS using BIND9. After installing and starting the Ubuntu VM I installed BIND9 and all related dependencies using this command:

sudo apt install bind9 bind9utils bind9-doc -y

Once that is done I moved on to editing the zone file. A zone file in BIND9 is a text file that contains the DNS records for a specific domain. It is essentially your very own "phonebook" for the small part of the internet (the zone) your BIND server is authoritative for.

Let's say our BIND server is responsible for example.com, the zone would include everything under that such as:

All this info will be kept in your zone file, which BIND9 can read and understand.

I then used nano to edit the config file with the following command:

sudo nano /etc/bind/named.conf.local

The file has no configuration in it yet.

I then added the following line

zone "nailthecode.test"{
    type master;
    file "/etc/bind/zones/db.nailthecode.test";
};

So by doing this I have defined a zone and pointed it to a corresponding zone file path. The type master means that this server is the authoritative source for DNS information about the nailthecode.test zone.

Types

Apart from the master type there are three more types. They are:

  1. Slave
    This server copies zonal data from a master server via a zone transfer. It serves as a read-only replica of the master server. It can also respond to DNS queries authoritatively, just like a master server.

  2. Stub
    This server copies data from the master server, but only the NS records and optional A/AAAA records. It doesn't serve DNS records directly to the client. It points to specific name servers so that the answer to the request is achieved faster.

     "dev.nailthecode.test" {
         type stub;
         file "/etc/bind/zones/stub.dev.nailthecode.test";
         masters { 10.0.0.5; }; // the DNS server for dev.nailthecode.test
     };
    
  3. Forward
    This server just forwards a query and waits for an answer. It can also cache answers locally for faster responses on queries made in the future.

I then created the zones directory in /etc/bind/ and added the db.nailthecode.test file. Here is what my final code looked like.

$TTL 640000 ;Time To Live of all the records in this zone file
$ORIGIN nailthecode.test. ; origin domain

    @    IN  SOA  ns1.nailthecode.test. admin.nailthecode.test.(
;└──┬──┘ └┬┘ └┬┘ └──────┬──────────────┘
; name  class type     value    

; sync instructions for secodary (slave) servers
        2025071901 ; serial
        64000 ;refresh
        6000 ;retry
        64000 ;expire
        3600 ;negative TTL
)

@ IN NS ns1.nailthecode.test. ;main authoritative name server
@ IN NS ns2.nailthecode.test. ;optional ns for redundancy

ns1 IN A 10.0.0.2 ;ns1 ip address ipv4 (use AAAA for ipv6)
ns2 IN A 10.0.0.3 ;ns2 ip address

www IN A 10.0.0.10 ; www.nailthecode.test lives at 10.0.0.10
test IN A 10.0.0.20 ; test.nailthecode.test lives at 10.0.0.20

mail IN A 10.0.0.30 ; mailing ip
mail IN MX 10 mail.nailthecode.test. ; mail is sent to mail.nailthecode.test

NOTE: I added some explanatory comments here, please use the version below for copy pasting

$TTL 640000
$ORIGIN nailthecode.test.

@   IN  SOA ns1.nailthecode.test. admin.nailthecode.test. (
        2025071901 ; serial
        64000      ; refresh
        6000       ; retry
        64000      ; expire
        3600       ; negative TTL
)

@   IN NS ns1.nailthecode.test.
@   IN NS ns2.nailthecode.test.

ns1 IN A 10.0.0.2
ns2 IN A 10.0.0.3

www  IN A 10.0.0.10
test IN A 10.0.0.20

mail IN A 10.0.0.30
mail IN MX 10 mail.nailthecode.test.

IN stands for Internet and is almost always the type of class used in the DNS. Originally the DNS was designed to handle multiple network types, which these days, aren't popular or in use anymore.

Now we can test if our DNS is working.

TESTS TESTS TESTS

I tried restarting the bind9 server and I have already run into my first problem.

Job for named.service failed because the control process exited with error code.
See "systemctl status named.service" and "journalctl -xeu named.service" for details.

After a status check using systemctl status bind9

Warning: The unit file, source configuration file or drop-ins of named.service changed on disk. Run 'systemctl daemon-reload' to reload units.
× named.service - BIND Domain Name Server
     Loaded: loaded (/usr/lib/systemd/system/named.service; enabled; preset: enabled)
     Active: failed (Result: exit-code) since Sun 2025-07-20 00:56:00 CAT; 25s ago
       Docs: man:named(8)
    Process: 5131 ExecStart=/usr/sbin/named -f $OPTIONS (code=exited, status=1/FAILURE)
   Main PID: 5131 (code=exited, status=1/FAILURE)
        CPU: 76ms

Ok let's check the config files... sudo named-checkconf ahaaaa!!!

/etc/bind/named.conf.test:12: missing ';' before '}'
/etc/bind/named.conf:11: missing ';' before 'include'

I have fixed this in the code snippet above so you probably won't face the same issues.

once everything is fixed when running sudo systemctl status bind9 you will see something like this:

congratulations the DNS is active and running!

Now let's check the zones.

sudo named-checkzone nailthecode.test /etc/bind/zones/db.nailthecode.test

....... another error :(

/etc/bind/zones/db.nailthecode.test:4: no current owner name
zone nailthecode.test/IN: loading from master file /etc/bind/zones/db.nailthecode.test failed: no owner
zone nailthecode.test/IN: not loaded due to errors.

fixed. I think.

The issue was that I was copy pasting from a windows device to a linux vm, which caused file format errors.

The fix was installing dos2unix

sudo apt isntall dos2unix

and running this command from root

sudo dos2unix /etc/bind/zones/db.nailthecode.test

actually nope that wasn't it. It was all the comments I added in the first version of the zone file above.

NOTE*: Fixed code available for use above.*

run this again...

sudo named-checkzone nailthecode.test /etc/bind/zones/db.nailthecode.test

and you will get

zone nailthecode.test/IN: loaded serial 2025071901
OK

perfect let's move on.

Run this sudo named-checkconf -- there shouldn’t be any output.

Restart the bind9 server sudo systemctl restart bind9 and check the status sudo systemctl status bind9

Now we can use dig to test if our nailthecode.local zone is responding properly.

ERRORR!

NOTE to SELF: Don't use .local for testing purposes

Ok so the problem here was that before this blog was edited I was using nailthecode.local as the main domain name. However .local is already reserved in the system so I ran into an error. You probably won't face this issue, but hey, I like to document my failures. Not to prevent them, of course :) just so Future Me has somewhere to go back to when everything catches fire again.

NOW FOR THE FINAL TESTS

TESTING USING DIG

dig @localhost nailthecode.test
dig @localhost www.nailthecode.test
dig @localhost mail.nailthecode.test MX

You will get something like this for the first one

Second command result

and finally…

All Good!

You can try challenges such as this and more here.

Cover Photo by Jake Walker on Unsplash

0
Subscribe to my newsletter

Read articles from Nail the Code directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Nail the Code
Nail the Code