Self Hosted DNS
Prerequisites
Section titled “Prerequisites”What you will need
Section titled “What you will need”This project assumes that you have already followed our Raspberry Pi installation project, and have the resulting configured Raspberry Pi. If you are installing on different hardware or a different OS, these steps may not all be the same for you.
Quick Review: What is DNS?
Section titled “Quick Review: What is DNS?”(D)omain (N)ame (S)ystem
Make DNS queries to lookup DNS records.
Very short explanation of some types of DNS records:
- A: “address”, hostname -> IP address
- AAAA: “address x4” hostname -> IPv6 address
- PTR: “pointer”, IP address -> hostname
- CNAME: “canonical name”, hostname -> hostname
- MX: “mail exchange”
- TXT: “text”
- NS: “nameserver”
ⓘ Note: This is just a quick review! Check out our “What is DNS?” presentation for more info: https://suddenlysixam.club/2025/09/23/meeting.html
ⓘ Note: IPv4 32 bits, IPv6 128 bits, 32 x 4 = 128)
Commands to help us test
Section titled “Commands to help us test”host - DNS lookup utility (man page)
e.g.
host google.comnslookup
Section titled “nslookup”nslookup - query Internet name servers interactively (man page)
e.g.
nslookup google.comnslookup -type=ns google.comnslookup google.com 1.1.1.1dig - DNS lookup utility (man page)
e.g.
dig google.comping — send ICMP ECHO_REQUEST packets to network hosts (man page)
e.g.
ping google.comping google.com -c 4Raspberry Pi BIND9 Setup
Section titled “Raspberry Pi BIND9 Setup”Basic Pi Configuration
Section titled “Basic Pi Configuration”Package installation: updates & basic network
Section titled “Package installation: updates & basic network”sudo apt updatesudo apt upgradesudo apt install vim bind9-dnsutilsⓘ Note:
bind9-dnsutilsgives us (among other things)nslookup&dig. It has a package dependency forbind9-hostwhich gives ushost.
ⓘ Note:
dnsutilsis a transitional package forbind9-dnsutils. So we will not use it here, but you may seednsutilsused in other guides.
Set hostname
Section titled “Set hostname”sudo vim /etc/hostssudo vim /etc/hostnamesudo shutdown -r nowOne the host has finished rebooting, check to see if your changes applied:
hostnameⓘ Note: You might ask, why not use
sudo hostnamectl set-hostname <your-hostname>? If you are curious, you can run this and thencatthe mentioned files, and see what has changed.
ⓘ Note: After you set the hostname and before you reboot, it may yell at you about the previous hostname name/service not being known.
Local name resolution
Section titled “Local name resolution”Add a different hostname to /etc/hosts:
sudo vim /etc/hostsThen let’s test the behavior of a few different commands:
host <hostname>nslookup <hostname>dig <hostname>ping <hostname>ⓘ Note: You can test what happens with your own device’s hostname that we configured in the last step too.
Set a static IP
Section titled “Set a static IP”In our case, we will be using NetworkManager to set a static IP.
nmclinmcli connection showTake note of the “Name” of the connection you are using. (NOT the “Device”. Sometimes these will be the same, but not always.)
nmcli con mod "<connection-name>" ipv4.addresses <ip-address>/<subnet-mask>nmcli con mod "<connection-name>" ipv4.gateway <gateway-ip-address>nmcli con mod "<connection-name>" ipv4.method manualnmcli con down "<connection-name>" && nmcli con up "<connection-name>"nmcliⓘ Note: How you set a static IP will greatly vary by OS and OS version.
ⓘ Note: Some parts of these commands can be shortened, such as
connection>con. Some can be shortened even further.
ⓘ Note:
sudo systemctl restart NetworkManagerwill add the new config, but will not remove the old, which is not ideal. This is why we are running theupanddowncommands for the connection. You could also reboot your pi.
ⓘ Note:
<subnet-mask>based on the netmask Subnet mask cheat sheet: https://dnsmadeeasy.com/support/subnet
BIND9 Configuration
Section titled “BIND9 Configuration”Package installation: BIND9
Section titled “Package installation: BIND9”sudo apt install bind9 bind9-utils(optional)
sudo apt install bind9-docⓘ Note: BIND9 is not the only choice, it is just our choice
ⓘ Note:
bind9for the service,bind9-utilsfor ways to check our work,bind9-docsfor documentation.) Additional package info: https://www.kali.org/tools/bind9/
ⓘ Note:
bind9utilsis a transitional package forbind9-utils. So we will not use it here, but you may seebind9utilsused in other guides.)
Configuration files
Section titled “Configuration files”/etc/systemd/system/bind9.service
Section titled “/etc/systemd/system/bind9.service”First, lets look at the systemd service for bind9:
cat /etc/systemd/system/bind9.serviceTake note of:
EnvironmentFile=-/etc/default/named
ExecStart=/usr/sbin/named -f $OPTIONS
Alias=bind9.service
ⓘ Note: In the
bind9.servicefile, the=-, indicates that if the file does not exist, it will not be read and no error or warning message is logged.
Next lets take a look at the current status of the service:
systemctl status bind9systemctl status named/etc/init.d/named
Section titled “/etc/init.d/named”Because of what we have now observed, lets look at the named service script:
cat /etc/init.d/named | lessWe can see that this has a comment about creating/changing /etc/default/named (and this was the EnvironmentFile that we noted earlier), so let’s take a look at that next.
/etc/default/named
Section titled “/etc/default/named”sudo vim /etc/default/namedLets configure this to run only on IPv4 by adding -4 to the options.
Default: OPTIONS="-u bind"
Change to: OPTIONS="-u bind -4"
/etc/bind/named.conf
Section titled “/etc/bind/named.conf”cat /etc/bind/named.confYou should see 3 included conf files, which we are going to begin configuring for different things:
/etc/bind/named.conf.options (ACLs, forwarders, port, etc.)
/etc/bind/named.conf.local (declare our zones)
/etc/bind/named.conf.default-zones (default zone declarations)
Throughout this you may also want to be checking your work, but I am not going to list this command after every step. Validate your changes as you go.
sudo named-checkconfnamed.conf.options
Section titled “named.conf.options”ⓘ Note: If you would like to look at all the options, and installed
bind9-doc, you can take a look at/usr/share/doc/bind9/options.gz.
Add an (A)ccess (C)ontrol (L)ist:
sudo vim /etc/bind/named.conf.optionsPut the following statement above the options {...} statement:
acl trusted { 10.70.50.0/24; localhost; localnets;};ⓘ **Note:**BIND has the following built-in ACLs:
none: Matches no hosts.
any: Matches all hosts.
localhost: 127.0.0.1 and ::1, as well as the IP addresses of all interfaces on the server that runs BIND.
localnets: 127.0.0.1 and ::1, as well as all subnets the server that runs BIND is directly connected to.
ⓘ Note: You could put the ACL directly in
named.conf, however since we are going to use it innamed.conf.optionsI’m putting it here.
Now add some configuration within the options {...} statement:
Allow DNS queries from the ACL we defined:
allow-query { trusted; }; allow-recursion { trusted; }; # allow them to recursively query authoritative DNS servers for the queried domainForward requests for records that this server does not have:
forward only; # don't attempt to contact other NS if forwarders not available forwarders { 1.1.1.1; 1.0.0.1; };Only IPv4. Change the second IP to that of the pi.
listen-on port 53 { 127.0.0.1; 10.70.50.104; }; listen-on-v6 { none; };Others:
auth-nxdomain no; # conform to RFC1035 - yes/no answer authoritative if NXDOMAIN allow-transfer { none; }; # Do not transfer the zone information to the secondary DNSnamed.conf.local
Section titled “named.conf.local”sudo vim /etc/bind/named.conf.localDeclare the zones associated with this server’s domain(s). Replace domain(s) and IP address(s) as appropriate for your setup:
#### Forward zoneszone "homelab.local" { type master; file "/etc/bind/zones/homelab.zone"; allow-update { none; }; # no DDNS by default};
#### Reverse zones## 10.70.50.0/24 subnetzone "50.70.10.in-addr.arpa" { type master; file "/etc/bind/zones/10.70.50.zone"; allow-update { none; }; # no DDNS by default};Other configuration
Section titled “Other configuration”You may want to do additional configuration, such as logging, but we aren’t doing any more for the sake of brevity.
ⓘ Note: https://wiki.debian.org/Bind9
Configure zones
Section titled “Configure zones”cd /etc/bind/sudo mkdir ./zonessudo cp db.local ./zones/homelab.zonesudo cp db.127 ./zones/10.70.50.zonecd zoneshomelab.zone
Section titled “homelab.zone”sudo vim homelab.zoneReplace domain(s) and IP address(s) as appropriate for your setup:
;; BIND data file for forward homelab.local;$TTL 604800@ IN SOA druid.homelab.local. admin.homelab.local. ( 2025030300 ; Serial 604800 ; Refresh 86400 ; Retry 2419200 ; Expire 604800 ) ; Negative Cache TTL;; name servers - NS records IN NS druid.homelab.local.
$ORIGIN homelab.local.
; name servers - A recordsdruid IN A 10.70.50.130
; 10.70.50.0/24paladin IN A 10.70.50.104ⓘ Note: The serial that I’ve configured here is the date plus a two digit integer
YYYYMMDDxx. It needs to be updated / incremented by at least 1 every time you make changes. You could simply make this an integer starting at 1, but that would go against my training.
ⓘ Note:
$ORIGINdefines a base name from which ‘unqualified’ names (those without a terminating dot) substitutions are made when processing the zone file.
10.70.50.zone
Section titled “10.70.50.zone”sudo vim 10.70.50.zoneReplace domain(s) and IP address(s) as appropriate for your setup:
;; BIND data file for reverse 50.70.10.in-addr.arpa;$TTL 604800@ IN SOA druid.homelab.local. admin.homelab.local. ( 2025030300 ; Serial 604800 ; Refresh 86400 ; Retry 2419200 ; Expire 604800 ) ; Negative Cache TTL;; name servers - NS records IN NS druid.homelab.local.
$ORIGIN 50.70.10.in-addr.arpa.
; Name Servers - PTR Records130 IN PTR druid.homelab.local.
; PTR Records104 IN PTR paladin.homelab.local.Does it work?
Section titled “Does it work?”Check your work. Is it OK?
named-checkzone homelab.local homelab.zonenamed-checkzone 50.70.10.in-addr.arpa 10.70.50.zoneRestart the service. Make sure its still running properly.
sudo systemctl restart namedsystemctl status namedNow what happens when we run:
nslookup <hostname>nslookup <hostname> 127.0.0.1nslookup <hostname>.homelab.local 127.0.0.1dig @127.0.0.1 <hostname>.homelab.localUsing our new DNS server
Section titled “Using our new DNS server”Find your current DNS server
Section titled “Find your current DNS server”cat /etc/resolv.confⓘ Note: Don’t overlook the lack of an
einresolv.conf. Tab complete is your friend.)
ⓘ Note: Additionally note the
Generated byline at the top of the file, if there is one. This may indicate that we do not want to edit this file directly.
Setting different DNS servers
Section titled “Setting different DNS servers”In our case, we will be using NetworkManager to modify our DNS servers.
nmclinmcli connection showTake note of the “Name” of the connection you are using. (NOT the “Device”. Sometimes these will be the same, but not always.)
nmcli con mod "<connection-name>" ipv4.dns "<space-separated-dns-ips>"nmcli con mod "<connection-name>" ipv4.ignore-auto-dns yesnmcli con down "<connection-name>" && nmcli con up "<connection-name>"nmcli(Optional) Set a search domain
nmcli con mod "<connection-name>" ipv4.dns-search "<domain>"nmcli con down "<connection-name>" && nmcli con up "<connection-name>"nmcliⓘ Note: How you modify your DNS servers will greatly vary by OS and OS version.
ⓘ Note:
sudo systemctl restart NetworkManagerwill add the new config, but will not remove the old, which is not ideal. This is why we are running theupanddowncommands for the connection. You could also reboot your pi.
Does it work? (pt 2.)
Section titled “Does it work? (pt 2.)”Now what happens when we run:
nslookup <hostname>nslookup <hostname>.homelab.localdig <hostname>dig <hostname>.homelab.localTroubleshooting
Section titled “Troubleshooting”General reminders
Section titled “General reminders”Increment the serial each time you make changes!
Check your work:
named-checkconfnamed-checkzone <zonename> <filename>e.g.
named-checkzone homelab.local homelab.zonenamed-checkzone 50.70.10.in-addr.arpa 10.70.50.zoneCan you connect to port 53?
Section titled “Can you connect to port 53?”telnet <remote-server> 53e.g.
bash-3.2$ telnet 1.1.1.1 53Trying 1.1.1.1...Connected to one.one.one.one.Escape character is '^]'.nc -vz -w 1 \<remote-server\> 53nc -vuz -w 1 \<remote-server\> 53e.g.
bash-3.2$ nc -vz -w 1 1.1.1.1 53Connection to 1.1.1.1 port 53 [tcp/domain] succeeded!bash-3.2$ nc -vuz -w 1 1.1.1.1 53Connection to 1.1.1.1 port 53 [udp/domain] succeeded!bash-3.2$Is port 53 open?
Section titled “Is port 53 open?”sudo netstat -tulpn | grep :53sudo lsof -Pi | grep LISTENsudo nmap -sS localhostsudo nmap -sU localhostHave you configured any variety of firewall? And the follow up question, have you configured it to allow DNS / port 53? (e.g. iptables, nftables, firewalld, ufw)
ⓘ Note: If you are following this guide with the same hardware/software, you should not need to configure this out of the box. I am including it because different OS’es may ship with a firewall already in place, and in practice you would want to configure this further.
named service
Section titled “named service”systemctl status namedsudo journalctl -u namedFinal thoughts
Section titled “Final thoughts”Hopefully you’ve now got DNS up and running! If you ran into any issues not mentioned here, have suggestions for the guide, or have any additional questions, please reach out to us in the Discord.
Home is where the lab is. ~Megan