How to Build a Custom DNS Server in Debian 12 Bookworm System

This article explains how to build a custom DNS server in a Debian 12 Bookworm system.

Setting up your own DNS (Domain Name System) server can provide better control over your network, enhance performance through caching, and offer custom filtering or forwarding options. Whether you’re a system administrator looking to manage internal domains or an enthusiast aiming to deepen your networking skills, building a custom DNS server on Debian 12 “Bookworm” is a practical and educational project.

This guide will walk you through the process of setting up a custom DNS server on a Debian 12 system using BIND9 (Berkeley Internet Name Domain), the most commonly used DNS server software in Linux environments.


What is a DNS Server?

DNS is often described as the “phonebook of the internet.” When you type a domain like www.example.com into your browser, DNS translates that human-readable address into an IP address (like 93.184.216.34) that computers use to identify each other on the network.

A custom DNS server allows you to:

  • Host and resolve your own domain names.
  • Cache queries for faster performance.
  • Block access to specific domains (useful for content filtering).
  • Use split-horizon DNS for internal and external resolution.
  • Serve DNS records for private internal networks.

Why Build Your Own DNS Server?

There are several reasons why you might want to run your own DNS server:

  • Privacy and security: Reduce reliance on third-party services and avoid DNS leaks.
  • Performance: A local caching DNS server reduces query latency.
  • Control: Host private zones, internal subdomains, or custom redirections.
  • Learning: Understand how DNS works at a fundamental level.

Prerequisites

Before you begin, make sure you have the following:

  • A Debian 12 Bookworm system (can be physical, virtual, or cloud-hosted).
  • Root or sudo privileges.
  • A static IP address assigned to your server.
  • Basic familiarity with Linux commands and networking.

Step 1: Update the System

Start by updating your system to ensure all packages are current.

sudo apt update && sudo apt upgrade -y

Step 2: Install BIND9 DNS Server

Debian 12 provides BIND9 in its default repositories.

sudo apt install bind9 bind9utils bind9-doc dnsutils -y
  • bind9: Core DNS server daemon.
  • bind9utils: Useful utilities like rndc.
  • bind9-doc: Documentation.
  • dnsutils: Contains the dig and nslookup tools for testing.

Step 3: Configure BIND9

The main configuration files are located in /etc/bind/. The three primary files you’ll work with are:

  • /etc/bind/named.conf
  • /etc/bind/named.conf.options
  • /etc/bind/named.conf.local

A. Configure Global Options

Open the named.conf.options file:

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

Here’s a basic configuration you can use:

options {
    directory "/var/cache/bind";

    recursion yes;
    allow-query { any; };
    listen-on { any; };
    listen-on-v6 { any; };

    forwarders {
        1.1.1.1;
        8.8.8.8;
    };

    dnssec-validation auto;

    auth-nxdomain no;
    listen-on-v6 { any; };
};

Save and close the file (CTRL + X, then Y, then Enter).

The forwarders option lets your DNS server forward unresolved queries to external DNS servers (Cloudflare and Google DNS in this case).


B. Configure a Local Zone

Let’s create a custom domain example.local for your internal network.

Edit the /etc/bind/named.conf.local file:

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

Add the following block:

zone "example.local" {
    type master;
    file "/etc/bind/db.example.local";
};

Step 4: Create the Zone File

Now create the actual DNS zone file for example.local.

sudo cp /etc/bind/db.local /etc/bind/db.example.local
sudo nano /etc/bind/db.example.local

Edit it like this:

$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.
ns1     IN      A       192.168.1.10
www     IN      A       192.168.1.20
mail    IN      A       192.168.1.30

Change the IPs to match your own internal addresses. The serial number should be incremented whenever changes are made to the zone file.


Step 5: Check Syntax and Restart BIND

Before restarting BIND, check for syntax errors:

sudo named-checkconf
sudo named-checkzone example.local /etc/bind/db.example.local

If everything is OK, restart BIND:

sudo systemctl restart bind9

Enable it to start on boot:

sudo systemctl enable bind9

Step 6: Configure Firewall

If you are using ufw (Uncomplicated Firewall), allow DNS:

sudo ufw allow 53

You may also need to open port 53 for both TCP and UDP depending on your network environment.


Step 7: Point Clients to Your DNS Server

To test or use your DNS server, you’ll need to configure your clients (PCs, routers, etc.) to use the Debian server’s IP address (e.g., 192.168.1.10) as their primary DNS server.

Temporarily Change DNS on Debian Client

Edit the /etc/resolv.conf file:

sudo nano /etc/resolv.conf

Add this line:

nameserver 192.168.1.10

Note: This change is temporary and may be overwritten by network manager. Use netplan or static network config for persistent changes.


Step 8: Test Your Custom DNS Server

Use dig to test if your DNS server is resolving correctly.

dig @192.168.1.10 www.example.local

Expected output should include the IP address defined in your zone file.


Step 9: Add Reverse Lookup Zone (Optional)

To enable reverse DNS (IP to hostname), configure a reverse zone.

Add to named.conf.local:

zone "1.168.192.in-addr.arpa" {
    type master;
    file "/etc/bind/db.192";
};

Create the reverse zone file:

sudo cp /etc/bind/db.127 /etc/bind/db.192
sudo nano /etc/bind/db.192

Example contents:

$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.
30      IN      PTR     mail.example.local.

Reload BIND:

sudo systemctl reload bind9

Security Considerations

To harden your DNS server:

  • Limit allow-query and allow-recursion to trusted IPs.
  • Disable recursion if you’re serving only authoritative zones.
  • Use DNSSEC for signed zones (advanced).
  • Monitor logs in /var/log/syslog or configure logging specifically for BIND.

Conclusion

Building a custom DNS server on Debian 12 Bookworm gives you complete control over how your network resolves domain names. From basic caching and forwarding to hosting private zones, BIND9 offers robust features to meet a wide variety of needs.

This tutorial covered:

  • Installing and configuring BIND9
  • Creating a forward and optional reverse DNS zone
  • Testing DNS functionality
  • Enabling firewall rules and client configuration

Whether you are deploying it on a home lab or a business intranet, running your own DNS server is a powerful step in managing your infrastructure. With regular maintenance and proper security practices, your custom DNS server will be a reliable backbone for your networking needs.