Building a Simple DNS using BIND9

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:
mail.example.com
blog.example.com
...you get the idea.
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:
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.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 };
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
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
