Get your own DNS server up and running with Bind9 on Ubuntu or Debian

The goal of this article is to describe how to setup your own DNS server to serve the domain name service entries for a TLD ( In the last part of the article I describe how to increase the speed of the system and the security in order to use it as public authoritative DNS nameserver for your domain.

Install the Nameserver using the Package Manager

Access the server, become the root user, install the bind9 package and stop the server for the moment:

apt-get install bind9
/etc/init.d/bind9 stop

On Debian squeeze, the current version being installed is BIND 9.6-ESV-R3 (find out by running named -v on the command line locally or dig @your-nameserver version.bind txt chaos remotely).

Configure a Forward Lookup Zone File

Now create your zone configuration file for the domain, /etc/bind/ In this file you set up your subdomains, how the domain is resolved for the email system etc.:

;; `/etc/bind/` - Forward lookup zone file for the TLD
;; For explanations: <>
; The SOA Resource Record (in the following format:)
;name [ttl] [class] rr      name-server           e-mail             ( [...] )
@           IN      SOA  (
                        2011041101      ; Serial (usually YYYYMMDDSS): increment after any change
                                8H      ; Refresh (8 hours)
                                2H      ; Retry (2 hours)
                                4W      ; Expire (4 weeks)
                                3H )    ; NX (3 hours) TTL Negativ Cache

@                               IN      NS
                                IN      MX      10
                                IN      A

ns                              IN      A
localhost                       IN      A
computer1                       IN      A
mailserver                      IN      A
computer2                       IN      CNAME   mailserver

You might want to learn more about the different RR types on

$TTL (time to live): The number of seconds a domain name is cached locally before expiration and return to authoritative nameservers for updated information.

Explanation of the the SOA record:

  • Serial
    The zone serial number, incremented when the zone file is modified, so the slave and secondary name servers know when the zone has been changed and should be reloaded.
  • Refresh
    This is the number of seconds between update requests from secondary and slave name servers.
  • Retry
    This is the number of seconds the secondary or slave will wait before retrying when the last attempt has failed.
  • Expire
    This is the number of seconds a master or slave will wait before considering the data stale if it cannot reach the primary name server.
  • NX
    TTL for negative caching. (This value has previously been used to determine the minimum TTL.)

Another bunch of important tips:

  • If you leave out the name in a line it is taken from the line above.
  • @ will be replaced by the FQDN of $ORIGIN (if it is set) or by the domainname set in the line zone "domainname" { in the file /etc/bind/named.conf.local.
  • When setting up the subdomain www you might want to add it as a CNAME record for @.
  • You can leave out the TTL and CLASS entry for a record line (TTL will be set to the value of $TTL and the class will be IN - internet).

Create a Reverse Lookup Zone File

Create the file /etc/bind/db.1.168.192

;; db.1.168.192 - Reverse lookup zone for domain-name
@       IN      SOA (
                                2011041101      ; Serial
                                        8H      ; Refresh
                                        2H      ; Retry
                                        4W      ; Expire
                                        2D )    ; TTL Negative Cache

@       IN      NS
                                               ;;; what it stands for:
10      IN      PTR        ; The nameserver:
200     IN      PTR ; Computer #1:
201     IN      PTR ; Computer #2:

add your new zone files to the local config file named.conf.local

Now add your new zone configuration files to your /etc/bind/named.conf.local:

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

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

Allow your subnet to access the DNS server

If you want to access your new DNS server from your subnet 192.168.1/24 (all IPs from up to, add allow-query-cache {192.168.1/24;}; to your /etc/bind/named.conf.options so that it looks like this:

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

  auth-nxdomain no;    # conform to RFC1035

  allow-query-cache {192.168.1/24;};

Start the nameserver bind9

Now you should be ready to restart bind9 with your new configuration:

/etc/init.d/bind9 restart

If could fail (like in the following code block):

lion:/etc/bind# /etc/init.d/bind9 restart
Stopping domain name service...: bind9rndc: connect failed: connection refused
Starting domain name service...: bind9 failed!

Then have a look at /var/log/syslog which will help you out with debugging information:

Apr 11 17:27:24 lion named[29991]: loading configuration from '/etc/bind/named.conf'
Apr 11 17:27:24 lion named[29991]: /etc/bind/named.conf:36: missing ';' before '}'
Apr 11 17:27:24 lion named[29991]: loading configuration: failure
Apr 11 17:27:24 lion named[29991]: exiting (due to fatal error)

In this case a colon ; was missing in the configuration file.

Setting up an authoritative-only DNS server with Bind9

The following paragraph is based on pages 154-155 of the book "Pro DNS and BIND 10", Apress, 2011, ISBN: 9781430230489.

To set up an authoritative-only DNS server with Bind9 on Ubuntu/Debian I recommend to replace the default set of configuration files on the standard Ubuntu/Debian installation (including named.conf, named.conf.options, named.conf.local and some zone files such as db.0, db.255, db.127, db.local, db.empty, zones.rfc1918, db.root, all stored in /etc/bind/) with the following global configuration file /etc/bind/named.conf:

// ----------------------- Options ----------------------- 
options {
  // all relative paths use this directory as a base
  directory "/var/cache/bind";
  // version statement changed for security (to avoid hacking known weaknesses)
  version "not currently available";
  // This prevents bind from serving other than authoritative requests:
  recursion no;
  // disables all zone transfer requests for performance as well as security reasons
  dnssec-enable no; // zone not signed
  minimal-responses yes; // optional - improved performance
  additional-from-auth no; // optional - improved performance
  additional-from-cache no; // optional - minimal performance change

// ----------------------- Logging ----------------------- 
// log to /var/log/named/zytrax-named all events from info UP in severity (no debug)
// uses 3 files in rotation swaps files when size reaches 250K
// failure messages up to this point are in (syslog) /var/log/messages
  channel custom_log{
    file "/var/log/bind9_info.log" versions 3 size 250k;
    severity info;
  category default{

// ----------------------- Zones ----------------------- 
zone "" in{
  type master;
  file "/etc/bind/";
  allow-transfer {;}; // slave server for the domain
// reverse map for local address at (uses for illustration)
zone "1.168.192.IN-ADDR.ARPA" in{
  type master;
  file "/etc/bind/db.1.168.192";

// local host domain (required)
zone "localhost" in{
  type master;
  file "/etc/bind/master.localhost";
// localhost reverse map
  zone "0.0.127.IN-ADDR.ARPA" in{
  type master;
  file "/etc/bind/localhost.rev";

Where /etc/bind/ and /etc/bind/db.1.168.192 are your custom zone files (as described earlier in this post).


  • An authoritative-only DNS server does not provide services for any domain except those for which it is either a master or a slave. As a consequence the root.servers zone file is not present (cf. zone "."). The recursion no; statement inhibits recursive behavior; the name server will return REFUSED status (BIND >=9.7) if it receives a query for any domain or zone for which it is neither master nor slave.
  • DNSSEC is turned off using dnssec-enable no; (this zone is not signed and since BIND 9.5, yes is the BIND 9 default setting).
  • Zone transfers are disabled (allow-transfer {none;};) in the options section which means you can avoid possible DDoS attacks via zone transfer on the localhost or reverse-map zones as well as the more obvious zone. Zone transfer is allowed to the assumed slave zone at in the zone only. This configuration also assumes that the master file is moved to this server by some out-of band method such as USB stick, secure FTP, ssh, etc. It is also possible to make this a slave server to a hidden master. However, the IP address of this hidden master (because it will appear in the slave's masters statement) might get compromised if an attacker can read the named.conf file on your system; since this external server must be able to communicate with the hidden master (to transfer the zone), so can an attacker—with less benign motives.
  • To boost the performance of Bind, you have to reduce the volume of data returned to requests as much as possible. This is done by setting the options minimal-responses, additional-from-auth and additional-from-cache.
    minimal-responses alone, depending on the configuration, can theoretically almost double query throughput. In the real world, this is not exact, as some of the data that is omitted is still required by the resolver. In the case of in-zone data, for example, it will immediately result in another query. After all it can still have a significant effect.
    Neither of the statements additional-from-auth no; nor additionalfrom-cache no; will have any effect on performance with the example configuration above. But with some changes they could have.
    If additional-from-auth was not present (it defaults to yes) and this name server was also authoritative (master or slave) for any out-of-zone reference, then this name server would add data to the ADDITIONAL SECTION in responses to queries by following the out-of-zone references.
    additional-from-cache prevents adding any data to the ADDITIONAL SECTION if it happens to be lying around in the cache. Since recursion is turned off completely, there should be nothing in the cache, but it does prevent the cache from even being searched. Prior to BIND 9.7, this statement also controlled returning referrals to a query for a zone this server was not authoritative for. Since BIND 9.7 such queries now result in REFUSED status being returned. The effect of additional-from-cache no; is probably less than minimal and it could be omitted with little loss of performance.
  • The reverse-map zone (zone "1.168.192.IN-ADDR.ARPA") is assumed to represent the domain's public addresses (the use of a private IP RFC 1918 address netblock is simply to avoid using a real public IPv4 address range). This reverse-map zone would only be present if the domain owner has the responsibly for reverse mapping. Either because they own the IPv4 netblock or their service provider has delegated the responsibility. In all cases, because mail servers do reverse-map look-ups, you will need to ensure that your mail server(s) have properly configured reverse maps either by creating and maintain a reverse-map zone or ensuring that your service providers adds the required the reverse map entries. In the case of IPv6, reverse maps are compulsory and will be in the user's responsibility.
  • BIND provides 3 statements to control the caching behavior: max-cache-size and max-cache-ttl, neither of which will have any effect on performance in the preceding case; and allow-recursion, which allows a list of hosts that are permitted to use recursion – all others are not (a kind of "poor man’s view clause").

Please not that we didn't include the root zone ("."). You are, however, required to include it in some cases: The configuration requires a root zone if NOTIFY is being used (set by default unless you set notify no;) and if one or more of the NS RR entries is out-of-zone and if this server is not authoritative for the out-of-zone domain name. Please refer to p. 153 in the book "Pro DNS and BIND 10", Apress, 2011, ISBN: 9781430230489 for more information.

The localhost forward zone file /etc/bind/master.localhost and its reverse zone file /etc/bind/localhost.rev should be set to the following:

;;; /etc/bind/master.localhost
$TTL 1d ;
$ORIGIN localhost.
@   IN   SOA   @   hostmaster.localhost. (
                 2011041101 ; serial
                 3H ; refresh
                 15M ; retry
                 1w ; expire
                 3h ; nx
@   IN   NS    @ ; localhost is the name server
    IN   A ; the loop-back address


;;; /etc/bind/localhost.rev
; could use $ORIGIN 0.0.127.IN-ADDR.ARPA.
@   IN   SOA   @    hostmaster.localhost. (
                3h )
    IN   NS    localhost.
1   IN   PTR   localhost.

Get the logging right / debug Bind9

If you add a logging section to your /etc/bind/named.conf, you can send debug output to a dedicated logfile. If your logfile is /var/log/bind9_info.log, you have to create it manually and give it the right ownership:

touch /var/log/bind9_info.log
chown bind:root /var/log/bind9_info.log

For extended debugging, you might consider to temporarily set the following logging section:

logging {
    category "default" { "debug"; };
    category "general" { "debug"; };
    category "database" { "debug"; };
    category "security" { "debug"; };
    category "config" { "debug"; };
    category "resolver" { "debug"; };
    category "xfer-in" { "debug"; };
    category "xfer-out" { "debug"; };
    category "notify" { "debug"; };
    category "client" { "debug"; };
    category "unmatched" { "debug"; };
    category "network" { "debug"; };
    category "update" { "debug"; };
    category "queries" { "debug"; };
    category "dispatch" { "debug"; };
    category "dnssec" { "debug"; };
    category "lame-servers" { "debug"; };
    channel "debug" {
    file "/var/log/bind.log" versions 2 size 50m;
        print-time yes;
        print-category yes;