My local DNS setup

Reading time: 4 minutes. Published on .

A few years ago, I wrote about the hardware setup of my home server. Recently, I’ve decided to put it to work beyond storing mere data and mirroring my IMAP server. I was annoyed by the weird behaviour of local name resolution of my home router, so why not operate my own DNS server?

Since I also wanted a few hosts in my local network to be reachable like ssh host1, I also set up a DHCP server that announces the correct search domain. But let’s talk about DNS first.

My local DNS server should satisfy three criteria:

  1. fast recursive lookup
  2. DNSSEC validation
  3. authoritative for a few hosts in my LAN

Like most good stories, this one started in the ArchWiki.1 I quickly narrowed it down to Unbound. I run Debian on that server, so the installation was just an apt install unbound away.

As usual, the Debian package maintainers have provided a baseline configuration that I just had to adapt to my tastes. If you install unbound-anchor too, you should also get DNSSEC. The ArchWiki page on Unbound has instructions on testing the DNSSEC setup.

I added two more things to the configuration. First, I created the directory /etc/unbound/server.conf.d for additional local zones. In the main configuration, I included the contents of that directory as follows:

        include: "/etc/unbound/server.conf.d/*.conf"

Note that the include statement here must go below server, otherwise it will not work.

With that in place, I added a file for my LAN hosts:

# /etc/unbound/server.conf.d/intranet.conf

local-zone: "" static
local-data: "        IN A"
local-data: "        IN A"
local-data-ptr: ""
local-data-ptr: ""

Now I get lookup and reverse lookup for a bunch of my devices with static addresses.

Finally, I downloaded a blocklist with adservers and saved it as /etc/unbound/server.conf.d/blocklist.conf. Any request to those adservers will be redirected to, which effectively gives me a decent ad filter for all devices in my LAN, including my phone. Credits go to Peter Lowe for providing this list;2 conveniently available in Unbound format.

Moving on to the DHCP server. This one was very simple to setup. I installed the isc-dhcp-server package and edited the /etc/dhcp/dhcpd.conf file.

I only had to adapt the following settings:

option domain-name "";
option domain-name-servers; # IP address of the server

subnet netmask {
  option routers;

… and that was about it. Now, whenever I use a local host name, the domain gets appended automatically based on DHCP configuration, and Unbound can resolve it correctly.3

The DNS + DHCP setup is nothing fancy, but it’s been serving me well so far.

Update: Prompted by a Twitter conversation in June 2021, I wanted to make sure that Unbound caches properly. In order to figure that out, I had to add a few more lines to the config file:

        control-enable: yes

This allows the CLI utility unbound-control to query various pieces of information from the DNS server. For example, I can dump the cache:

$ sudo unbound-control dump_cache
;rrset 79968 1 0 8 3     79968   IN      CNAME

... lots more ...

As of writing this update, the cache contains approximately 17k lines, including the DNS root servers.

According to unbound-control stats_noreset, about half of my DNS requests are served from the cache, and the median response time is 26 ms (average being 53 ms), although I’m not entirely sure I’m interpreting those numbers correctly.

  1. Despite its name, it is a really useful resource for all Linux distributions. 

  2. Bonus points if you wrote a cronjob that auto-updates the list; I couldn’t be bothered. 

  3. Yes, my router could do that before too, even automatically based on the hostnames. No, that was not sufficient, since I also want to resolve hostnames from another network that’s behind another router.