Ubuntu articles

Using bind as a local caching name server

Picture this: a local network connected to the internet with a shared DSL connection and a couple of unreliable DNS servers. Solution? Bind as a caching name server. We won't run a fully-fledged DNS server but we'll have a little reliable one in-house.

Bind (bind9, more specifically) is a powerful beast, but I am only using it as a local caching name server. There's a computer in our local network which we use as a file server, and it's idling most of the time, so performing several DNS queries from time to time will help it keep fit, and since all our computers will query it instead of querying another DNS server out there "in the internets", DNS queries will be faster, since they will be locally cached and served by this little machine. A secondary effect of running an internal DNS server is that machines can be accessed by its name and not by using an IP address.

Let's assume the machine who is going to act as local name server is called server, and the other computers who will connect to it are called machine1, machine2, machine3, and so on...

Server is running Ubuntu, so we install bind9 and any other dependency that is required, if asked to:

sudo apt-get install bind9

And now, to the fabulous world of text file editing!

The main BIND configuration file: /etc/bind/named.conf.local

Edit or create /etc/bind/named.conf.local (probably using sudo) and enter the following contents:

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

zone "0.168.192.in-addr.arpa" {
  type master;
  file "/etc/bind/zones/rev.0.168.192.in-addr.arpa";

Before we continue it might be interesting to explain a bit of how this works. The whole system of DNS addresses is built upon a hierarchy. At the bottom of everything are the root nodes, who might know, or not, about mappings between IP addresses and domain names. If they know, it means they define one or more zones. If they don't, they delegate the queries for everything else to another server.

So what we will be doing is creating a little tree in our server computer, with only one zone: our local network, where it will know how to translate between IP's and domain names, and we'll delegate everything else to the proper, grown up, professional domain servers.

In this schema, the local network will have domain names ending in .local (for example, machine1.local) (therefore the zone is called 'local' in the code above) and all these addresses are in the 192.168.0.xxx range (so the reverse zone is named 0.168.192.in-addr.arpa). That funky naming scheme for reverse zones has its own very valid reasons to be how it is, so look up some info and read about that if you're interested.

Bind configuration options (/etc/bind/named.conf.options)

So far we have defined our zone, but we haven't said it where should be looking for domain names that aren't in our zone. This is where the forwarders enter the game:

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

        // If there is a firewall between you and nameservers you want
        // to talk to, you may need to fix the firewall to allow multiple
        // ports to talk.  See http://www.kb.cert.org/vuls/id/800113

        // If your ISP provided one or more IP addresses for stable
        // nameservers, you probably want to use them as forwarders.
        // Uncomment the following block, and insert the addresses replacing
        // the all-0's placeholder.

        forwarders {
; // IT at home
; // Be dynamic ip
     auth-nxdomain no;    # conform to RFC1035
        listen-on-v6 { any; };

Before you copy and paste things, take a look at the forwarders section. Each line corresponds to a name server. Usually each ISP provides its own name servers, and more often than not, these are pretty unreliable, so users in the know replace them with better DNS servers. So in theory you should be putting your ISP provided name servers, but if you have issues with them, do not hesitate to replace them with alternative ones.

In my case, I am using IT at home name servers -- which they graciously provide for customers of Be and O2, given the unreliability of these DNS servers, but if you try to use them and are not in any of those networks, it might not work. Please change the servers as required.


Now we'll define the contents of our zone and its mappings. For readability purposes I won't list the full list of machines!

$TTL 1h
local.          IN      SOA     server.local. admin.server.local. (

local.          IN      NS      server.local.

server           IN      A
machine1       IN      A
machine2       IN      A

Some comments:

  • $TTL 1h means that whenever a machine queries the name server about these values, the response will be valid for an hour. It is the "Time To Live". If a machine needs to know who is machine2 after more than an hour has passed, it will query server again, because the local cached information will have expired.
  • Also make sure to include the last dot at the end of certain values (for example, server.local.).
  • admin.server.local in fact represents an e-mail address, but without the @ sign. It means admin@server.local, using bind's syntax.
  • The 200911131651 timestamp is used to detect whether the configuration has been updated or not -- a kind of manual file modification time (filemtime). In theory, if you make changes in this file you should update the timestamp accordingly.
  • The local. IN NS server.local. line is where we say "there is a .local domain, and its name server is server.local". This is a NS type record, in DNS jargon. The following lines are A type records (where A means... Address).
  • If you had more machines, just keep adding them to the list, following the same format. An IP address should be used only once, of course!

There are other types of records. For example, if we wanted to have an internal mail server, we could add it here and its record type would be MX (Mail eXchange?). For my purposes it's enough with type A records only.


Up until now, the name server (i.e. server) only knows how to map domains to IP addresses. But --stupid as it sounds-- it is unable to perform the reverse mapping by itself. We have to provide another file so that it knows which domain corresponds to which IP address. I am unsure whether this is a poor design decision, something inherited from another era when computing power was expensive, or something that can be avoided if using a different name server, but this is what you have to do if you use bind9:

@ IN SOA server.local. admin.server.local. (

        IN      NS      server.local.
2       IN      PTR     local
3       IN      PTR     machine1.local.
4       IN      PTR     machine2.local.

Note how the file is very similar to the previous one, only it is reversed.

To be honest, I am unsure about the 2 IN PTR local line, but since this seems to work by now (and it's not like a network of 1000 computers depend on this name server), I will leave it like that for the time being.

Start using your little name server

First thing to do is restarting the service:

sudo /etc/init.d/bind9 restart

But the funny thing is that no one will use this service unless it's told to do so. We can start by telling the server machine to use its own name server by editing the /etc/resolv.conf file and replacing its contents with this:

search local

It might disconnect briefly but finally it will end up using itself for domain queries. You can check by yourself using dig and looking at the server where the reply came from:

server@server:~$ dig google.com

; <<>> DiG 9.5.1-P2 <<>> google.com
;; global options:  printcmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 65397
;; flags: qr rd ra; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 0

;google.com.            IN    A

google.com.        188    IN    A
google.com.        188    IN    A
google.com.        188    IN    A

;; Query time: 18 msec
;; WHEN: Tue Nov 17 12:20:21 2009
;; MSG SIZE  rcvd: 76

The first time it's queried, since the value is not in the cache yet, it has to query the external name server. It takes 18ms to execute, which isn't bad actually. But if you query it again it returns a query time of 1ms or even 0ms.

So what is in the bind cache? Is it possible to see it?

You can dump the cache with

rndc dumpdb -cache

This creates the /var/cache/bind/named_dump.db file which you can open with any text editor and have a look at. Or for example you could grep it for certain values:

server@server:~$ less /var/cache/bind/named_dump.db | grep google.com
blogsearch.google.com.    850    CNAME    www2.l.google.com.
books.google.com.    19103    CNAME    www3.l.google.com.
code.google.com.    527573    CNAME    code.l.google.com.
groups.google.com.    278872    CNAME    groups.l.google.com.
images.google.com.    358154    CNAME    images.l.google.com.
googleapis-ajax.l.google.com. 252 \-AAAA ;-$NXRRSET
groups.l.google.com.    36    A
images.l.google.com.    23    A
news.l.google.com.    36    A
picasaweb.l.google.com.    36    A
video.l.google.com.    51    A
mail.google.com.    382224    CNAME    googlemail.l.google.com.

and get scared when you see how many google* sites you visit a day :-S (I have trimmed down the list, it was really long).

How to get other computers to use the new local name server?

You just need to edit their network settings and let them know that they should use the new name server: You might have to reconnect for the changes to be applied, but then, that's modern computing for you... 8-)

The secondary effect that I mentioned above is that from the time you use this dns server, you can refer to your machines using domain names, and not IP addresses. This is convenient if you have a local web server and want to access it from another computer but don't want to remember its IP address. For example, machine1 can now be accessed by typing http://machine1.local/ in the address bar.