DNS Cache Setup


This document does not talk about how to set up a DNS name server; it talks about setting up a DNS cache, often confusingly referred to as a DNS caching server.

The roles of DNS name servers and DNS caches are distinct although they are often combined on the same physical machine and even into the same running software instance, BIND can be set up this way. The reader’s digest version of the difference between the two roles is this: a DNS name server serves zone data to other machines and is authoritative for one or more domains while a DNS cache caches the results of DNS queries. This cache of results may or may not be made available for other machines to use. A DNS cache is what appears in a computer’s /etc/resolv.conf file while a DNS name server should not.


Thebe is located at a remote site away from the rest of my network. As explained in my Arda Network architecture document, Thebe communicates with the rest of my machines through a VPN set up between itself and Metis, another FreeBSD machine acting as the router for my network. All the machines on my network use my own DNS name servers for DNS lookups as explained in my DNS & DHCP Setup document. This is fine for the machines located in my home. But for Thebe, this means every name lookup has to travel through the VPN. I wanted a way to reduce this traffic.

The answer was to set up a DNS cache on Thebe. With a DNS cache running locally, the results of DNS queries are stored for later use. Without caching, DNS queries of machines on my local network take over 100 milliseconds while queries for machines on the internet consistently take over 300 milliseconds. When a DNS query finds an answer in the local cache, the total time for the query is only 2 milliseconds.

When it came to deciding what software to use for my DNS cache, I was looking for certain characteristics. I wanted something lightweight and easy to configure. Since I knew I only wanted to set up a DNS cache and not a DNS name server, using software that could only act as a cache and not a name server seemed like a good way to achieve these goals. For this reason, I chose to use dnscache from Daniel Bernstein’s djbdns package. I’m using the latest version of djbdns which is 1.05. I’m also using supervise and multilog from the daemontools package to control the dnscache process and to handle logging chores respectively. Like most of D. Bernstein’s software, dnscache is designed to be used with these tools and they really are quite handy. I recommend you use them too.

dnscache Setup

After installing the daemontools and djbdns packages I could begin configuring dnscache. But to have a DNS cache work for Thebe, I had to be careful of the DNS name servers I told dnscache to use when answering queries. Why you ask? Because of the private nature of my DNS name servers. The world at large does not know about the machines on my local network, it does not have access to the zone information stored in my name servers. That means that to find the IP address of Io for example, Thebe needs to query my name servers directly. Because of this, I tell dnscache to use my internal name servers to answer all queries about my domain and to use public DNS name servers to answer queries about everything else. With this setup, only queries about my own domain travel from Thebe over the IPSec tunnel to my home network. I’ll get to the details of how I accomplished this a little further on.

The first thing to do before configuring my shiny new DNS cache is to create the users the software runs as. Here are the two users that I use to run dnscache and multilog respectively.


Most of the configuration of dnscache can be accomplished with one command. Here is the command I used to configure my cache.

	dnscache-conf dnscache dnslog /var/dnscache

This command creates the directory structure used by dnscache and puts reasonable default values into most of the configuration files. Here is a listing of the directory structure used by dnscache. Keep in mind that this snapshot shows the directory tree after I completed configuration of dnscache, the above command creates a lot of these files but not all of them.

drwxr-sr-t    6 root     root         4096 Oct  9 23:16 /var/dnscache/
drwxr-sr-x    2 root     root         4096 Oct  9 23:37 /var/dnscache/env/
-rw-r--r--    1 root     root            8 Oct  9 23:08 /var/dnscache/env/CACHESIZE
-rw-r--r--    1 root     root            8 Oct  9 23:08 /var/dnscache/env/DATALIMIT
-rw-r--r--    1 root     root           10 Oct  9 23:19 /var/dnscache/env/IP
-rw-r--r--    1 root     root            8 Oct  9 23:08 /var/dnscache/env/IPSEND
-rw-r--r--    1 root     root           19 Oct  9 23:08 /var/dnscache/env/ROOT
drwxr-sr-x    4 root     root         4096 Oct  9 23:16 /var/dnscache/log/
-rwxr-xr-x    1 root     root           61 Oct  9 23:09 /var/dnscache/log/run
drwxr-sr-x    4 root     root         4096 Oct  9 23:08 /var/dnscache/root/
drwxr-sr-x    2 root     root         4096 Oct  9 23:08 /var/dnscache/root/ip/
-rw-------    1 root     root            0 Oct  9 23:08 /var/dnscache/root/ip/
drwxr-sr-x    2 root     root         4096 Oct  9 23:56 /var/dnscache/root/servers/
-rw-r--r--    1 root     root          182 May 11 18:47 /var/dnscache/root/servers/@
-rw-r--r--    1 root     root           26 May 11 19:21 /var/dnscache/root/servers/10.168.192.in-addr.arpa
-rw-r--r--    1 root     root           26 May 11 19:21 /var/dnscache/root/servers/arda.homeunix.net
-rw-r--r--    1 root     root          182 May 11 23:35 /var/dnscache/root/servers/in-addr.arpa
-rwxr-xr-x    1 root     root          141 Oct  9 23:08 /var/dnscache/run
-rw-------    1 root     root          128 Oct  9 23:08 /var/dnscache/seed

Here are the contents of the configuration files found in /var/dnscache/env.

Control File Contents
CACHESIZE 10000000
DATALIMIT 10485760
ROOT /var/dnscache/root


CACHESIZE is the size of the cache in bytes.
DATALIMIT is used by softlimit in the dnscache startup script. It sets the maximum per process segment size in bytes.
IP is the IP dnscache will listen on for queries.
IPSEND is the IP from which dnscache will send queries.
ROOT is the root of the chroot jail dnscache will run in.

I’ve increased CACHESIZE and DATALIMIT from the values put there by the dnscache-conf command. I noticed that items were being evicted from the cache within the time-to-live of your typical DNS record, thus reducing the efficiency of the cache. There is a link in the Further Reading section of this document that will take you to a page describing how to tell when you should change these values.

Since I want only Thebe to use the DNS cache, I put the loopback interface into the IP configuration file. This prevents any machine but Thebe from sending queries to dnscache.

In my configuration, IPSEND is set to which tells dnscache to use the primary IP address of the machine to send queries on. On a machine with multiple interfaces, you’ll likely want to put the specific IP address you want dnscache to use in this file.

You tell dnscache which IPs to accept DNS queries from by putting files into the root/ip directory. The source IP you want dnscache to accept queries from is the name of the file. The file itself is empty. You can also tell dnscache to accept queries from an entire subnet by dropping octets from the filename. For example, adding a file named 192.168.10 to the root/ip directory would cause dnscache to accept queries from all machines on the subnet.

Since I don’t want any machine other than Thebe to use the cache, I only have one file named in root/ip so that dnscache will accept queries from localhost only. This is really a redundant step since the IP configuration file tells dnscache to only listen on the loopback interface. But what others call redundancy, I call defense in depth.

The root/servers directory is where you tell dnscache what DNS servers to use when sending queries.

The @ and in-addr.arpa files contain the same list of root DNS servers, one IP per line. I used these commands to get the list of root DNS server IPs.

dnsip `dnsqr ns . | awk '/answer:/ { print $5 ; }' | sort` > /var/dnscache/root/servers/@
cp @ in-addr.arpa

Repeat the above commands periodically to keep your list of root name servers up to date. Root name servers don’t change very often so refreshing the list twice a year should be enough.

The arda.homeunix.net and 10.168.192.in-addr.arpa files contain the IPs of my DNS name servers like so.

So, when Thebe makes a DNS query, dnscache will will check to see what domain the query is for. If the query if for a domain name or IP address in arda.homeunix.net or, then dnscache will consult the DNS name servers on my network. If the query is for anything else, dnscache will use public name servers on the internet starting with the root servers listed in @ and in-addr.arpa. As explained earlier, I needed to do this so that Thebe could resolve computers on my local network properly.

Controlling dnscache with Daemontools

Daemontools is a package that includes programs used to control the startup and shutdown of long-running processes. This package is intended to ensure that processes that are supposed to run all the time actually do.

I’ve set up dnscache to be controlled by the supervise program from the daemontools package. I also use svscan as the overseer to ensure all processes controlled by supervise are started during system boot. Most of the work is done for you when you install dnscache. In fact, dnscache is installed assuming that you will be using svscan and supervise to startup and control the dnscache processes.

There are two processes that supervise will control. One is the dnscache process itself and the other is multilog which, if you haven’t already guessed, handles logging. These two processes correspond to the two users set up before configuring dnscache.

Installing dnscache also installs two run scripts within the dnscache directory tree. Often these scripts will meet you needs as is but you can edit them to change such things as resourse limits or the number and size of log files. In my case, I changed the location where multilog creates log files and the size of the log files. Here are what my scripts look like.

Here are what the two run scripts look like.

File Contents
exec 2>&1
exec <seed
exec envdir ./env sh -c 'exec envuidgid dnscache \
softlimit -o250 -d "$DATALIMIT" /usr/local/bin/dnscache'
exec setuidgid dnslog multilog t s512000 /var/log/dnscache \
'-*' '+* stats *' /var/log/dnscache/dnsstats

Multilog automatically creates log directories named in its run script when it starts for the first time if they aren’t already there. Here are what my log directories look like.

drwx------    2 dnslog   dnsuser      4096 Oct 16 19:43 /var/log/dnscache/
drwx------    2 dnslog   dnsuser      4096 Oct 16 19:43 /var/log/dnscache/dnsstats

I’ve set up multilog to send all log messages to the directory /var/log/dnscache while it sends all lines containing the string ‘stats’ to /var/log/dnscache/dnsstats. I set up multilog this way because it allowed me to keep more cache status history while using less disk space than if I simply increased the size of the regular log files.

Once the scripts are the way you want them, the only thing you need to do is create a link from the dnscache home directory to wherever your service directory is. On Thebe, mine is /service. So all I had to do was issue this command:

ln -s /var/dnscache /service

Five seconds later, dnscache and multilog were up and running. This worked because I had already installed daemontools on Thebe and svscan was already running.

Software Home Sites

Daemontools http://cr.yp.to/daemontools.html
djbdns http://cr.yp.to/djbdns.html

Further Reading

Comprehensive documentation describing how to set up tinydns, dnscache, web-based administration and much more. http://www.djbdnsrocks.org/
How to change the size of your dnscache cache and how to know when you should http://cr.yp.to/djbdns/cachesize.html
tinydns.org – Many patches and add-ons to djbdns http://www.tinydns.org/
See my DNS & DHCP page for lots of other DNS links DNS & DHCP Setup