Setting up your own authoritative DNS server jots

This post was written by eli on March 30, 2019
Posted Under: Internet,Server admin

What’s this?

These are somewhat random jots I made while setting up an authoritative BIND server, so that a simple VPS machine can function standalone. Well, almost standalone, as it takes some help from a slave DNS to supply the second DNS entry. But even if that slave goes away suddenly, the show will go on. So practically speaking, it’s a one machine show.


Todo when replacing slave server (note to self)

Note that it all about changing IP addresses, as the slave server is referred to with my own “ns2″ subdomain.

  • Update the glue record for the domain on which the name server is a subdomain.
  • Update the A record for the name server in the relevant bind zone files (and bump serial number, right…?).
  • Update allow-transfer and possibly also-notify in named.conf.local for all zones.
  • Update the DNS monitoring script.

General notes

  • DNS records are maintained on one server only (the master server), and the secondary server(s) follow suit. This is a quick & painless solution, and the update is virtually immediate when done right. This is discussed below. If you’re updating two servers, you’re doing it horribly wrong.
  • Use named-checkconf and named-checkzone to verify the configuration file and zone files, respectively
  • The service’s name in Debian 8 is “bind9″ (for systemctl purposes etc.). It’s a non-LSB (systemd) service, executed from /lib/systemd/system/bind9.service.
  • /etc/default/bind9 is ignored.
  • Note that the “dig” utility’s output is in fact a zone record. Copy and paste (actually, shorted the zone to the subdomain only for simplicity)
  • “dig axfr” attempts to make a domain transfer for the said domain. Or more like:
    $ dig axfr
  • All HOWTOs talk about reverse zones. As if someone normal had the delegation to answer those queries. Just ignore these parts.
  • The “command channel” that the server listens to on localhost:953 can be used with rndc, however this utility is intended for controlling the server in large terms (add a zone file etc.) and not through individual zone records. This is what nsupdate is for, but if you start playing with a dynamic record of a zone, keep your hands off the zone files. Citing nsupdate’s man page: “Zones that are under dynamic control via nsupdate or a DHCP server should not be edited by hand. Manual edits could conflict with dynamic updates and cause data to be lost.”
  • The allow-transfer parameter defines which IPs are allowed to issue a transfer request (copy all zone data). The default is anyone. Possibly restrict this to just the known slave servers.

Recursive, non-recursive and AXFR

The confusing part about a name server is that it conveys information in three different ways (that I can think of), for different purposes:

  • Recursive queries: It’s the cut-the-bullsh*t request for an IP of a domain name, issued by a e.g. a web browser. The server functions as the DNS of a (usually limited) net segment (defined by the allow-recursion option), and will ask around servers as necessary to reach the bottom line result of an IP address.
  • Non-recursive queries: The server answers supplies only records that is written in its own zone files (and maybe also cached records? Not sure about that). This is the mode for an authoritative server, supplying the records for some specific domains it’s responsible for.
  • AXFR: This is the “give me all you got” request from a slave of an authoritative server. This allows setting up the records on one machine, and have several other servers follow suit. Discussed below.

The DNS protocol allocates a bit on the query which tells if the request is recursive or not, and also a bit on the response, saying if was or not.

As “dig” makes recursive requests by default, authoritative servers (which are typically configured not to support recursive requests) will usually answer with a non-recursive response. Which will usually be exactly what we wanted in the first place (or why did we ask an authoritative server with “dig” in the first place?).

So for authoritative servers, the warning actually indicates proper behavior:

$ dig

; <<>> DiG 9.11.3-1ubuntu1.2-Ubuntu <<>>
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 57926
;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 4, ADDITIONAL: 9
;; WARNING: recursion requested but not available

[ ... the name servers for given here ... ]

Recursive queries were allowed to all by default in BIND until version 9.4.1, but later versions has it turned off by default, my own included. It makes sense, as most people who use BIND are likely to make a small authoritative server, and not a public DNS for everyone to enjoy.

Glue records or not

It’s very sleek to name your name servers with the domain it covers. It’s like answering for As written in any guide to setting up a DNS, this makes a cyclic dependency: In order to find, you first need to ask the delegated DNS,, what IP it has. This is solved with glue records (the IPs are given explicitly in the “Additional Section” on the NS query).

In my specific case, I went for the cyclic dependency option for both name servers, mainly because the domain name of the chosen slave server didn’t resolve consistently to a single IP address. So it seemed safer to give the IP address myself with my own kind-of-bogus nameserver domain plus glue records which I set up on my registrar’s web interface. So it’s not just sleek, but it’s a good way to keep things stable. No reason to be afraid of this — it’s actually better.

As most people use their domains on hosted services, the name servers are given by the service provider, and hence their domain names have nothing to do with the hosted domain. Typically, the domain’s registrar offers a web interface for setting up the name servers, but only by their domain names.

Any serious registrar allows setting up glue records for cyclic dependencies explicitly. As a matter of fact, it won’t let you set a name server pointing at the same domain without any glue record given first. It can however be a bit confusing on how to do it on the web interface. For example, it might be under “Advanced Features” in the web management tool, called “Add Host Names”. It can also be called “personal name servers”.

Aside from Top Level Domain servers, glue record are rarely necessary, and are given in the “Additional” section as a neat shortcut. Actually, are they really “glue” when not absolutely necessary? Either way, as shortcuts, it seems like there are no rules for when they are present and when they aren’t. It’s like every server has its own rules.

Some name servers obtain glue addresses for other name servers by issuing lookups (or relying on their cache), and then present them in the “Additional” section. This is considered bad practice. A recent bind 9 won’t do this as an authoritative server, and the “fetch-glue yes/no” option is not available anymore.

This mess includes Top Level Domain servers as well, even though one could expect that they wouldn’t issue glue records unless they’re necessary. This is not to be confused with the responses to the exact same queries from common DNSes, which are usually more generous. Again, no fixed rules for this. For example,

$ dig NS

; <<>> DiG 9.11.3-1ubuntu1.2-Ubuntu <<>> NS
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 30176
;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 4, ADDITIONAL: 2
;; WARNING: recursion requested but not available

; EDNS: version: 0, flags:; udp: 4096
;			IN	NS

;; AUTHORITY SECTION:		172800	IN	NS		172800	IN	NS		172800	IN	NS		172800	IN	NS


So why is there a glue record for It stands by itself, and has its own glue records. And why not the others? Because they’re not .com? Go figure.

At some early point I had some concerns on that the glue record relies on the given IP and not a DNS query made by either the registrar’s web app or the Top Level Domain name servers displaying a cached result. If that was the case, the IP address could be lost for some reason and the cyclic dependency would be impossible to resolve. After some playing around, I got pretty much convinces noone’s is even near to let that happen.

Zone file notes

  • Always bump the serial number when making changes or the slave won’t catch them. The number must increase.
  • Any domain not ending with a ‘.’ will be considered relative to the origin domain
  • ‘@’ means the origin domain. It’s like a no-string for a domain.
  • If no (sub)zone is given in the beginning line, it’s the previously line’s (“repeat name”).
  • TXT records are limited to 2 kB, but the strings given in the zone file (within quotation marks) are limited to 256 bytes. To overcome this, it’s allowed to divide the strings into pieces, each with its own quotation marks, and a space between them. The actual text is the concatenation of the two strings in quotation marks, after removing these quotation marks. Think of it as a multi-line string in C language.
  • The last number in the SOA record, often called TTL, is the negative caching TTL: How long a server is allowed to cache a “no record exists” answer.
  • For each SPF record (of type TXT), add an identical record with type SPF. Or bind9 whines with
    zone '' found SPF/TXT record but no SPF/SPF record found, add matching type SPF record

    even though SPF records died in 2014. But it won’t hurt (unless the slave doesn’t like it…?)

  • Unfortunately, there is no built-in variable / macro expansion in bind. Several strings, and in particular the IP address are repeated over and over again.
To check a zone file:
$ named-checkzone
To generate a canonical file (see if it was understood correctly):
$ named-compilezone -o out.txt

Propagating the domain records to slaves

Explained on this chapter of DNS and BIND. The short story is that some other server (the “slave”) issues AXFR queries to the master regarding a domain, and in response it get all the records for that domain. The slave then responds to DNS queries for that domain based upon the information obtained. This takes place when the master DNS sends a NOTIFY message to the slave, and/or with refresh intervals. And there’s a thing with the serial number, which must be higher on the master than the slave, or the slave considers its local data updated. Actually, some slaves will issue an AXFR regardless.

These NOTIFY requests are there to tell the slaves an update is required: Assuming that the “notify” setting of the master DNS it “yes”, when its DNS’s records are updated, it sends a NOTIFY message to all authoritative servers by default (plus those explicitly given with “also-notify”). Those which are defined as slaves of the notifying server check the serial number in the SOA record. If it is different from what they have, they issue a transfer request to the master (with an AXFR command, or the incremental variant, IXFR), and update their data from the info that arrives.

The “Refresh” entry of the SOA record relates to the slave’s periodical polling of the master. This time period is important only with old versions of BIND (before BIND 8), as they didn’t support the NOTIFY command. With the newer versions, it sets the periodic polling, which should have no significance except for a little load on both sides.

The notification messages and their responses are logged, and should be verified when changes are made.

The slaves may, and probably will, send NOTIFY messages to the authoritative DNSes when they’re done updating, but odds are that these will be ignored, as the common setting is that all slaves take info from a single master.

A NOTAUTH response means that the request was sent to an non-authoritative server (so it doesn’t have the info). But it can also be an excuse for refusing a transfer (the correct answer is REFUSED for that case).

Typical session when restarting bind after making changes (and changing the serial number!):

Mar 23 17:02:54 named[11923]: zone sending notifies (serial 2019032101)
Mar 23 17:02:54 named[11923]: zone sending notifies (serial 2019032101)
Mar 23 17:02:54 named[11923]: zone sending notifies (serial 2019032101)
Mar 23 17:02:57 named[11923]: client ( transfer of '': AXFR started
Mar 23 17:02:57 named[11923]: client ( transfer of '': AXFR ended
Mar 23 17:02:58 named[11923]: client ( transfer of '': AXFR started
Mar 23 17:02:58 named[11923]: client ( transfer of '': AXFR ended
Mar 23 17:02:58 named[11923]: client ( transfer of '': AXFR started
Mar 23 17:02:58 named[11923]: client ( transfer of '': AXFR ended

Finding a slave server

Having this all-in-one server, there’s only one things it can’t do by itself: Being a backup server. So you need to look for one. As DNS is a rather low-bandwidth service, the hosting price should be low to zero. I looked for one that provided this for free, mainly to save the hassle of annual payments. I’ve got enough of those.

It’s a backup after all, so if someone pulls the plug from the backup server all of the sudden, things will probably go on as usual for a while. So it’s in principle enough to trust the service provider that it won’t hijack your domain or something. And try picking one that will last, just to save the bother of setting the slave DNS up again.

As of March 2019, I found two alternatives for free slave services. There are probably many more.

BuddyNS is a company that was founded for supplying DNS services. It has lots of servers and a neat web interface. I got second thoughts when I realized that it’s a startup which hasn’t lifted off so well (again, March 2019), which makes it a potentially volatile choice.

So I went for Afraid FreeDNS (despite its not-so-encouraging name). They have quite a few options, but the free plan allows for a slave DNS mirror, and is called the “backup DNS” service. The web interface is simple, not so impressive, but very functional and to the point: A single page (login required to actually see something there), with a simple dashboard page saying what domains are being served, when they were last updated and when the last attempt took place. Plus a long log of events, including AXFRs that were either successful or failed, and if the latter why. And also AXFR requests that arrived from other servers to the slave and were rejected.

For any domain that needs slave coverage, the domain and its master DNS are fed into the web interface (a small “Add”) link. The master DNS must allow AXFRs from one IP address, It takes a few minutes for the slave DNSes to update.

It’s also possible to allow AXFRs from other slaves, by setting up Slave AXFR-ALLOW ACL records. By default, AXFRs are rejected (as they should).

There’s only one DNS server for this slave service,, with IP address according to its authoritative server. It may sometimes resolve as (note that this is the server that makes the AXFR requests). This double IP is harmless and by design, according to the DNS admin, who responded to my question on this matter.

I haven’t figured out the point of this as of yet, but the reason seems to be that the TLD server for .org gives the address as a glue record (which isn’t really necessary) when asked about any .org domain that has as a name server. So the DNS server asking about this caches this answer, and propagates it further. So the ISP-level DNS may sometimes answer and sometimes, depending on its mood and cache.

The solution is simple, and it’s actually what I would suggest anyhow: Make it look like your own. Use a domain name of your own, with cyclic dependency, glue records and all that, and refer to the secondary name server by IP (using this domain, of course). This also makes it much easier to move to another server if necessary.

Add a Comment

required, use real name
required, will not be published
optional, your blog address