Using bind for local domain

Network configuration

Difficulty: ★★★★★

Serving a local network

Resolving local network devices can be a pain because the name query is often forwarded to root nameservers or ISP cache servers which returns NXDOMAIN.

To create a robust LAN whether it is for home or small business use you need an internet facing domain which access to DNS control panel. These notes uses bind on a Raspberry Pi running the default Raspberry Pi OS.

.local is used with the AVAHI zeroconf specification. Do not invent you own .lan or something similar - the result will not be as expected.

Experience with terminal is mandatory as all setup is done using a SSH connection and any error will break your connection. Knowledge of DNS, zone files and dhcp especially ISC bind and ISC dhcp is also mandatory.

Install bind and dhcp

Ensure your system is up-to-date then install bind and dhcp packages

apt update && sudo apt upgrade -y
apt install bind9 isc-dhcp-server -y

Example config

Object Value
local domain
local net/subnet
local gateway IP
local nameserver IP
dhcp range to 200
temp nameserver IP

The temporary nameserver is provided by

Static IP

Assign a static IP to your Pi by editing your network configuration.

Create a file /etc/network/interfaces and add content

auto lo
iface lo inet loopback

auto eth0
iface eth0 inet static
        # dns-nameservers

Double check your entries and reload

sudo systemctl daemon-reload
sudo systemctl reload network

From another terminal on your workstation ping your Pi


On your Pi check the lookup


Ensure you have a working Pi before continuing

Setup your public DNS

Login into your domain dns controlpanel and create the following records and save the changes

  1. A-record with IP
  2. Nameserver for

Dump zone file from your control panel and verify the zone is correct     43200   IN  A        43200   IN  NS
                        43200       A zone

Enable and start the local nameserver

systemctl enable bind9.service
systemctl start bind9.service

Create a local zone file /etc/bind/ with content

$TTL 1h
@       IN  SOA (
                1       ; Serial
                3600    ; Refresh
                3600    ; Retry
                1W      ; Exire
                3H      ; Negative Cache TTL
@       IN      NS
ns      IN      A
mail    IN      A
webmail IN      CNAME

Check the zone

named-checkconf /etc/bind/ reverse zone

Create a reverse zone file /etc/bind/db.172.16.10 with content

@       IN      SOA (
                        1               ; Serial
                        604800          ; Refresh
                        86400           ; Retry
                        2419200         ; Expire
                        604800          ; Negative Cache TTL
@       IN      NS
2       IN      PTR
3       IN      PTR

Check the zone

named-checkconf /etc/bind/db.172.160.10

Edit /etc/bind/zones.rfc1918 and comment the line for your reverse zone file and save

# zone ""  { type master; file "/etc/bind/db.empty"; };

Check the zone

named-checkconf /etc/bind/zones.rfc1918

Adding zones

Edit /etc/bind/named.conf.local and add the new new zones and save the file

zone "" {
    type master;
    file "/etc/bind/;

zone "" {
    type master;
    file "/etc/bind/db.172.16.10";

Check the zone

named-checkconf /etc/bind/named.conf.local

Check root servers

Check the root hints in db.root and update the file if necessary (remember to check the zone if you edit)

dig +bufsize=1200 +norec NS . | egrep -v ';|^$' | sort

Check the options file

Edit the file /etc/bind/named.conf.options if necessary - remember to check the zone after edit.

Time of reckoning

Reload the bind9 service

systemctl reload bind9.service

On the Pi test your lookup


Change your Raspberry Pi network to use local bind service by editing the file /etc/network/interfaces file Change the temporay nameserver to save the file and reload networking.


systemctl reload networking

On the Pi test your lookup one more time - you should get the same answer - this time from your Pi


Setting up isc-dhcp

Edit the file /etc/dhcp/dhcpd.conf with content

ddns-update-style interim;
ddns-updates on;
do-forward-updates on;
option domain-name "";
option domain-name-servers;
option broadcast-address;
option routers;
default-lease-time 86400;
max-lease-time 604800;

subnet netmask {

Only one DHCP service is allowed for a network so disable all other DHCP services.

When you have disabled router DHCP - enable and start the isc-dhcp-server.service

systemctl enable isc-dhcp-server.service
systemctl start isc-dhcp-server.service

BIND ad blocker

When you have gone through lengths to set up your dns - you might as well utilize bind to block ads and known malware domains.

Below is the README from which I have utilized to sanitize my network.

The BIND ad blocker fetches various blocklists and generate a BIND zone from them.

Configure BIND to return NXDOMAIN for ad and tracking domains to stop clients from contacting them.

Requires BIND 9.8 or newer for RPZ support.

Uses the following sources:


Python packages

See requirements.txt

To install

pip install -r requirements.txt

Configure BIND

Add the response-policy statement to the BIND options

// For AdBlock
response-policy {
    zone "";

Add your rpz zone. Replace with a domain of your choice.

// AdBlock
zone "" {
    type master;
    file "/etc/bind/";
    masterfile-format text;
    allow-query { none; };

Create a zone file for your zone. Replace with the domain you used before.

@ 3600 IN SOA @ 0 86400 7200 2592000 86400
@ 3600 IN NS


    usage: [-h] [--no-bind] [--raw] [--empty] zonefile origin

    Update zone file from public DNS ad blocking lists

    positional arguments:
      zonefile    path to zone file
      origin      zone origin

    optional arguments:
      -h, --help  show this help message and exit
      --no-bind   Don't try to check/reload bind zone
      --raw       Save the zone file in raw format. Requires named-compilezone
      --empty     Create header-only (empty) rpz zone file

Example: /etc/bind/ will update the zone file with the fetched adserver lists and issue a rndc reload origin afterwards.


You can either use an additional zone to whitelist domains (Or add them to config.yml) See Whitelist for adding a whitelist zone.