---
myst:
html_meta:
description: Install and configure BIND9 DNS server on Ubuntu to map IP addresses and FQDNs, with tools for testing and troubleshooting DNS issues.
---
(install-dns)=
# Domain Name Service (DNS)
Domain Name Service (DNS) is an Internet service that maps IP addresses and {term}`fully qualified domain names (FQDN) ` to one another. In this way, DNS alleviates the need to remember IP addresses. Computers that run DNS are called **name servers**. Ubuntu ships with the Berkeley Internet Naming Daemon (BIND), the most common program used for maintaining a name server on Linux.
## Install DNS
At a terminal prompt, run the following command to install the `bind9` package:
```bash
sudo apt install bind9
```
A useful package for testing and troubleshooting DNS issues is the `dnsutils` package. Very often these tools will be installed already, but to check and/or install `dnsutils` enter the following:
```bash
sudo apt install dnsutils
```
## DNS configuration overview
There are many ways to configure BIND9. Some of the most common configurations include a caching nameserver, primary server, and secondary server.
- When configured as a **caching nameserver**, BIND9 will find the answer to name queries and remember the answer when the domain is queried again.
- As a **primary server**, BIND9 reads the data for a zone from a file on its host, and is authoritative for that zone.
- As a **secondary server**, BIND9 gets the zone data from another nameserver that is authoritative for the zone.
### Configuration files
The DNS configuration files are stored in the `/etc/bind` directory. The primary configuration file is `/etc/bind/named.conf`, which in the layout provided by the package just includes these files:
- **`/etc/bind/named.conf.options`**: Global DNS options
- **`/etc/bind/named.conf.local`**: For your zones
- **`/etc/bind/named.conf.default-zones`**: Default zones such as localhost, its reverse, and the root hints
The root nameservers are provided by the `/usr/share/dns/root.hints` file shipped with the `dns-root-data` package, and is referenced in the `named.conf.default-zones` configuration file above. Up until Ubuntu 18.04 LTS it used to be described in the file `/etc/bind/db.root`.
It is possible to configure the same server to be a caching name server, primary, and secondary: it all depends on the zones it is serving. A server can be the Start of Authority (SOA) for one zone, while providing secondary service for another zone. All the while providing caching services for hosts on the local LAN.
## Set up a caching nameserver
The default configuration acts as a caching server. Edit `/etc/bind/named.conf.options` to set the IP addresses of your ISP's DNS servers. Add or modify the `options` block so that it includes a `forwarders` section as follows:
```
options {
forwarders {
1.2.3.4;
5.6.7.8;
};
};
```
```{note}
Replace `1.2.3.4` and `5.6.7.8` with the IP addresses of actual nameservers.
```
To enable the new configuration, restart the DNS server. From a terminal prompt, run:
```bash
sudo systemctl restart bind9.service
```
[See the dig section](#dig) for information on testing a caching DNS server.
## Set up a primary server
In this section BIND9 will be configured as the primary server for the domain `example.com`. You can replace `example.com` with your FQDN (Fully Qualified Domain Name).
### Forward zone file
To add a DNS zone to BIND9, turning BIND9 into a primary server, first edit `/etc/bind/named.conf.local`:
```
zone "example.com" {
type master;
file "/etc/bind/db.example.com";
};
```
```{note}
If BIND will be receiving automatic updates to the file as with {term}`DDNS`, then use `/var/lib/bind/db.example.com` rather than `/etc/bind/db.example.com` both here and in the copy command below.
```
Now, create a zone file in `/etc/bind/db.example.com`. Up until Ubuntu 25.04 this could be copied from a template, e.g. in `/etc/bind/db.local`.
Edit the new zone file `/etc/bind/db.example.com` adding the {term}`FQDN` of your server, including the additional `.` at the end, the nameserver's IP address and a valid email address (`root@example.com`), but with a `.` instead of the usual `@` symbol, again including the `.` at the end. You can add a comment at the top (using semicolons), to indicate the domain that this file is for.
Create an **A record** for the base domain, `example.com`. Also, create an **A record** for `ns.example.com`, the name server in this example:
```
;
; BIND data file for example.com
;
$TTL 604800
@ IN SOA example.com. root.example.com. (
2 ; Serial
604800 ; Refresh
86400 ; Retry
2419200 ; Expire
604800 ) ; Negative Cache TTL
@ IN NS ns.example.com.
@ IN A 192.168.1.10
@ IN AAAA 2001:db8::10
ns IN A 192.168.1.10
```
You must increment the `Serial Number` every time you make changes to the zone file. If you make multiple changes before restarting BIND9, only increment `Serial` once.
Now, you can add DNS records to the bottom of the zone file. See {ref}`common-record-types` for details.
```{note}
Many admins like to use the "last edited" date as the Serial of a zone, such as **2020012100** which is **yyyymmddss** (where **ss** is the Serial Number)
```
Once you have made changes to the zone file, BIND9 needs to be restarted for the changes to take effect:
```bash
sudo systemctl restart bind9.service
```
### Reverse zone file
Now that the zone is set up and resolving names to IP Addresses, a **reverse zone** needs to be added to allow DNS to resolve an address to a name.
Edit `/etc/bind/named.conf.local` and add the following:
```
zone "1.168.192.in-addr.arpa" {
type master;
file "/etc/bind/db.192";
};
```
```{note}
Replace `1.168.192` with the first three octets of whatever network you are using. Also, name the zone file `/etc/bind/db.192` appropriately. It should match the first octet of your network.
```
Now create the `/etc/bind/db.192` file, changing the same options as `/etc/bind/db.example.com`:
```
;
; BIND reverse data file for local 192.168.1.XXX net
;
$TTL 604800
@ IN SOA ns.example.com. root.example.com. (
2 ; Serial
604800 ; Refresh
86400 ; Retry
2419200 ; Expire
604800 ) ; Negative Cache TTL
;
@ IN NS ns.
10 IN PTR ns.example.com.
```
The `Serial Number` in the reverse zone needs to be incremented on each change as well. For each **A record** you configure in `/etc/bind/db.example.com` that is for a different address, you will need to create a **PTR record** in `/etc/bind/db.192`.
After creating the reverse zone file restart BIND9:
```bash
sudo systemctl restart bind9.service
```
## Set up a secondary server
Once a primary server has been configured, a **secondary server** is highly recommended. This will maintain the availability of the domain if the primary becomes unavailable.
First, on the primary server, the zone transfer needs to be allowed. Add the `allow-transfer` option to the example **Forward** and **Reverse** zone definitions in `/etc/bind/named.conf.local`:
```
zone "example.com" {
type master;
file "/etc/bind/db.example.com";
allow-transfer { 192.168.1.11; };
};
zone "1.168.192.in-addr.arpa" {
type master;
file "/etc/bind/db.192";
allow-transfer { 192.168.1.11; };
};
```
```{note}
Replace `192.168.1.11` with the IP address of your secondary nameserver.
```
Restart BIND9 on the primary server:
```bash
sudo systemctl restart bind9.service
```
Next, on the secondary server, install the `bind9` package the same way as on the primary. Then edit the `/etc/bind/named.conf.local` and add the following declarations for the Forward and Reverse zones:
```
zone "example.com" {
type secondary;
file "db.example.com";
masters { 192.168.1.10; };
};
zone "1.168.192.in-addr.arpa" {
type secondary;
file "db.192";
masters { 192.168.1.10; };
};
```
Once again, replace `192.168.1.10` with the IP address of your primary nameserver, then restart BIND9 on the secondary server:
```bash
sudo systemctl restart bind9.service
```
In `/var/log/syslog` you should see something similar to the following (some lines have been split to fit the format of this document):
```text
client 192.168.1.10#39448: received notify for zone '1.168.192.in-addr.arpa'
zone 1.168.192.in-addr.arpa/IN: Transfer started.
transfer of '100.18.172.in-addr.arpa/IN' from 192.168.1.10#53:
connected using 192.168.1.11#37531
zone 1.168.192.in-addr.arpa/IN: transferred serial 5
transfer of '100.18.172.in-addr.arpa/IN' from 192.168.1.10#53:
Transfer completed: 1 messages,
6 records, 212 bytes, 0.002 secs (106000 bytes/sec)
zone 1.168.192.in-addr.arpa/IN: sending notifies (serial 5)
client 192.168.1.10#20329: received notify for zone 'example.com'
zone example.com/IN: Transfer started.
transfer of 'example.com/IN' from 192.168.1.10#53: connected using 192.168.1.11#38577
zone example.com/IN: transferred serial 5
transfer of 'example.com/IN' from 192.168.1.10#53: Transfer completed: 1 messages,
8 records, 225 bytes, 0.002 secs (112500 bytes/sec)
```
```{note}
A zone is only transferred if the `Serial Number` on the primary is larger than the one on the secondary. If you want to have your primary DNS notify other secondary DNS servers of zone changes, you can add `also-notify { ipaddress; };` to `/etc/bind/named.conf.local` as shown in the example below:
```
```
zone "example.com" {
type master;
file "/etc/bind/db.example.com";
allow-transfer { 192.168.1.11; };
also-notify { 192.168.1.11; };
};
zone "1.168.192.in-addr.arpa" {
type master;
file "/etc/bind/db.192";
allow-transfer { 192.168.1.11; };
also-notify { 192.168.1.11; };
};
```
```{note}
The default directory for non-authoritative zone files is `/var/cache/bind/`. This directory is also configured in AppArmor to allow the named daemon to write to it. See this page for {ref}`more information on AppArmor `.
```
## Testing your setup
### resolv.conf
The first step in testing BIND9 is to add the nameserver's IP address to a **hosts resolver**. The Primary nameserver should be configured as well as another host to double check things. Refer to {ref}`DNS client configuration ` for details on adding nameserver addresses to your network clients. In the end your `nameserver` line in `/etc/resolv.conf` should be pointing at `127.0.0.53` and you should have a `search` parameter for your domain. Something like this:
```text
nameserver 127.0.0.53
search example.com
```
To check which DNS server your local resolver is using, run:
```bash
resolvectl status
```
```{note}
You should also add the IP address of the secondary nameserver to your client configuration in case the primary becomes unavailable.
```
### dig
If you installed the `dnsutils` package you can test your setup using the DNS lookup utility `dig`:
After installing BIND9 use `dig` against the loopback interface to make sure it is listening on port 53. From a terminal prompt:
```bash
dig -x 127.0.0.1
```
You should see lines similar to the following in the command output:
```
;; Query time: 1 msec
;; SERVER: 192.168.1.10#53(192.168.1.10)
```
If you have configured BIND9 as a caching nameserver, "dig" an outside domain to check the query time:
```bash
dig ubuntu.com
```
Note the query time toward the end of the command output:
```
;; Query time: 49 msec
```
After a second `dig` there should be improvement:
```
;; Query time: 1 msec
```
### ping
Now let's demonstrate how applications make use of DNS to resolve a host name, by using the `ping` utility to send an ICMP echo request:
```bash
ping example.com
```
This tests if the nameserver can resolve the name `ns.example.com` to an IP address. The command output should resemble:
```
PING ns.example.com (192.168.1.10) 56(84) bytes of data.
64 bytes from 192.168.1.10: icmp_seq=1 ttl=64 time=0.800 ms
64 bytes from 192.168.1.10: icmp_seq=2 ttl=64 time=0.813 ms
```
### named-checkzone
A great way to test your zone files is by using the `named-checkzone` utility installed with the `bind9` package. This utility allows you to make sure the configuration is correct before restarting BIND9 and making the changes live.
To test our example forward zone file, enter the following from a command prompt:
```bash
named-checkzone example.com /etc/bind/db.example.com
```
If everything is configured correctly you should see output similar to:
```
zone example.com/IN: loaded serial 6
OK
```
Similarly, to test the reverse zone file enter the following:
```
named-checkzone 1.168.192.in-addr.arpa /etc/bind/db.192
```
The output should be similar to:
```
zone 1.168.192.in-addr.arpa/IN: loaded serial 3
OK
```
```{note}
The Serial Number of your zone file will probably be different.
```
### Quick temporary query logging
With the `rndc` tool, you can quickly turn query logging on and off, without restarting the service or changing the configuration file.
To turn query logging on, run:
```bash
sudo rndc querylog on
```
Likewise, to turn it off, run:
```bash
sudo rndc querylog off
```
The logs will be sent to `syslog` and will show up in `/var/log/syslog` by default:
```
Jan 20 19:40:50 new-n1 named[816]: received control channel command 'querylog on'
Jan 20 19:40:50 new-n1 named[816]: query logging is now on
Jan 20 19:40:57 new-n1 named[816]: client @0x7f48ec101480 192.168.1.10#36139 (ubuntu.com): query: ubuntu.com IN A +E(0)K (192.168.1.10)
```
```{note}
The amount of logs generated by enabling `querylog` could be huge!
```
## Logging
BIND9 has a wide variety of logging configuration options available, but the two main ones are **channel** and **category**, which configure where logs go, and what information gets logged, respectively.
If no logging options are configured the default configuration is:
```
logging {
category default { default_syslog; default_debug; };
category unmatched { null; };
};
```
Let's instead configure BIND9 to send **debug** messages related to DNS queries to a separate file.
We need to configure a **channel** to specify which file to send the messages to, and a **category**. In this example, the category will log all queries. Edit `/etc/bind/named.conf.local` and add the following:
```
logging {
channel query.log {
file "/var/log/named/query.log";
severity debug 3;
};
category queries { query.log; };
};
```
```{note}
The `debug` option can be set from 1 to 3. If a level isn't specified, level 1 is the default.
```
Since the **named daemon** runs as the `bind` user, the `/var/log/named` directory must be created and the ownership changed:
```bash
sudo mkdir /var/log/named
sudo chown bind:bind /var/log/named
```
Now restart BIND9 for the changes to take effect:
```bash
sudo systemctl restart bind9.service
```
You should see the file `/var/log/named/query.log` fill with query information. This is a simple example of the BIND9 logging options. For coverage of advanced options see the "Further Reading" section at the bottom of this page.
(common-record-types)=
## Common record types
This section covers some of the most common DNS record types.
- **`A` record**
This record maps an IP address to a {term}`hostname`.
```
www IN A 192.168.1.12
```
- **`CNAME` record**
Used to create an alias to an existing A record. You cannot create a `CNAME` record pointing to another `CNAME` record.
```
web IN CNAME www
```
- **`MX` record**
Used to define where emails should be sent to. Must point to an `A` record, not a `CNAME`.
```
@ IN MX 1 mail.example.com.
mail IN A 192.168.1.13
```
- **`NS` record**
Used to define which servers serve copies of a zone. It must point to an `A` record, not a `CNAME`. This is where primary and secondary servers are defined.
```
@ IN NS ns.example.com.
@ IN NS ns2.example.com.
ns IN A 192.168.1.10
ns2 IN A 192.168.1.11
```
## Further reading
- {ref}`install-dnssec`
- [Upstream BIND9 Documentation](https://bind9.readthedocs.io/en/latest/)
- [DNS and BIND](https://www.oreilly.com/library/view/dns-and-bind/0596100574/) is a popular book now in its fifth edition. There is now also a [DNS and BIND on IPv6](https://www.oreilly.com/library/view/dns-and-bind/9781449308025/) book.
- A great place to ask for BIND9 assistance, and get involved with the Ubuntu Server community, is the {matrix}`Ubuntu Server ` Matrix channel.