PiHole + BIND + Ubuntu Linux + secondary address mess resolved

How to configure pihole and BIND on the same Ubuntu linux server.

5 minute de lecture
Par Stéphane
PiHole + BIND + Ubuntu Linux + secondary address mess resolved
Le Verrou (Fragonard)(1770/76)

I want PiHole and BIND on the same host, and I'm not alone, and everyone seems to be asking the same question : how the fuck the configuration of pihole should be.

Préliminary information

  • ubuntu version : 24.something (server).
  • pihole version : 6.1.something.
  • Both services run on a host named : lenopi.

I. The desired behavior

Pihole's IP is the default DNS serveur for my home network. DHCP advertise its address. Every host which needs to resolve a name must query the pihole.

The host machine (lenopi) runs a BIND server.

Pihole uses the BIND resolver as upstream.

Meaning that :

  1. The Pihole does caching and filtering. When queried, if the domain is in block list, it'll return 0.0.0.0. If it's in its cache, it'll return the host address. If it's unknown it'll forward the request to BIND.
  2. BIND works in a recursive manner. Either it knows about the domain (locally defined), and it can reply with the Authoritative flag (aa) or it'll ask somewhere else (recursively) until it gets the answer, or fails.

II. The IP config on the host

Host Lenopi has 1 physical interface, with its IP address. I suppose you're SSHing to it. Which, btw, is not a very good idea when you want to mess with its interfaces. You've been warned.

A secondary address must be added.

For example : 192.168.0.26 and 192.168.0.27 as secondary.
192.168.0.254 is the gateway (router & default route).

III. The setup

III.a Define the secondary address
Content of /etc/netplan/50-cloud-init.yaml :

network:
  version: 2
  ethernets:
    eno1:
      dhcp4: no
      addresses:
        - 192.168.0.26/24
        - 192.168.0.27/24
      routes:
        - to: default
          via: 192.168.0.254
      nameservers:
        addresses:
          - 127.0.0.1
      dhcp6: no

dhcp is off per pihole installation rules.

Apply the config :

sudo netplan generate
sudo netplan apply

Test :

ip addr

Output :

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 ...
...
2: eno1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 ...
    inet 192.168.0.26/24 brd 192.168.0.255 scope global eno1
    inet 192.168.0.27/24 brd 192.168.0.255 scope global secondary eno1

Both addresses should be pingable.

III.b Install BIND

I won't explain how to install BIND, it's documented everywhere. Just don't forget that your server listens to the secondary address defined above, port 53 (default). So the conf file looks like this :

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

// Listen (bind) on the secondary
  listen-on { 192.168.0.27; };
  
// 127.0.0.1, the pihole IP and all the hosts on the network can query
  allow-query { localhost; 192.168.0.26; 192.168.0.0/24; };

  recursion yes;

// Recommended
  dnssec-validation auto;

// Cache negative answers for performance
  auth-nxdomain no;

// don't need to listen to an IPv6 address
  listen-on-v6 { none; };
};

Nota bene : I allow all my hosts to bypass filtering and use BIND as a nameserver. Maybe you don't want this. It's useful for debugging from a PC though (dig @bind or dig @pihole).

Please test the BIND process. Check if a firewall is not messing with it for example, and that it can resolv addresses. You can use dig for that purpose :

dig www.mit.edu @192.168.0.27
; <<>> DiG 9.18.30-0ubuntu0.24.04.2-Ubuntu <<>> www.mit.edu @192.168.0.27
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 46623
;; flags: qr rd ra; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
; COOKIE: 093c1af4416dcc1001000000682d2dcd354fef0344a4cd75 (good)
;; QUESTION SECTION:
;www.mit.edu.                   IN      A

;; ANSWER SECTION:
www.mit.edu.            1800    IN      CNAME   www.mit.edu.edgekey.net.
www.mit.edu.edgekey.net. 60     IN      CNAME   e9566.dscb.akamaiedge.net.
e9566.dscb.akamaiedge.net. 20   IN      A       104.85.29.3

;; Query time: 32 msec
;; SERVER: 192.168.0.27#53(192.168.0.27) (UDP)
;; WHEN: Wed May 21 03:35:09 CEST 2025
;; MSG SIZE  rcvd: 160

Query time should become smaller if you run the same query 2 or 3 times because it'll be cached. When cached the time will be 0. Try several hosts. If you have defined some zones, check the hosts in those domains.
Let's assume that your BIND is up and running, and can survive reboots.

III.c Pihole config

Create the config file for dnsmasq :

/etc/dnsmasq.d/50-slt-custom.conf

With content :

listen-address=127.0.0.1
listen-address=192.168.0.26
bind-interfaces

The bind-interfaces MUST be the LAST statement.

Now, login to the pihole dashboard.
On the left pane, under SYSTEM open Settings > All settings
On top of the page click on the [Miscellaneous] button.

That opens the "misc." settings.

Scroll down until : misc.etc_dnsmasq_d

Check Enable.

Now, from the left, select DNS (still in the Settings). And configure as follow :

  • No upstream selected from the list.
  • 192.168.0.26, or your secondary IP address where BIND listens.
  • Note that my pihole is not accessible from the internet, so it uses the default Allow only local requests.
sudo systemctl restart pihole-FTL.service && sudo pihole -t

Check that pihole works :

dig www.mit.edu @192.168.0.26
; <<>> DiG 9.18.30-0ubuntu0.24.04.2-Ubuntu <<>> www.mit.edu @192.168.0.26
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 14873
;; flags: qr rd ra; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
; EDE: 3 (Stale Answer)
;; QUESTION SECTION:
;www.mit.edu.                   IN      A

;; ANSWER SECTION:
www.mit.edu.            1217    IN      CNAME   www.mit.edu.edgekey.net.
www.mit.edu.edgekey.net. 35     IN      CNAME   e9566.dscb.akamaiedge.net.
e9566.dscb.akamaiedge.net. 0    IN      A       104.85.29.3

;; Query time: 0 msec
;; SERVER: 192.168.0.26#53(192.168.0.26) (UDP)
;; WHEN: Wed May 21 03:44:52 CEST 2025
;; MSG SIZE  rcvd: 138

You can follow what's happening with :

sudo tail -f /var/log/pihole/pihole.log /var/log/named/bind.log

For example :

dig www.sdf.org @192.168.0.26
==> /var/log/pihole/pihole.log <==
May 21 03:53:36 dnsmasq[24278]: query[A] www.sdf.org from 192.168.0.2
May 21 03:53:36 dnsmasq[24278]: forwarded www.sdf.org to 192.168.0.27

==> /var/log/named/bind.log <==
21-May-2025 03:53:36.474 queries: info: client @0x71e05402cad8 192.168.0.26#45150 (www.sdf.org): query: www.sdf.org IN A +E(0)K (192.168.0.27)

==> /var/log/pihole/pihole.log <==
May 21 03:53:36 dnsmasq[24278]: reply www.sdf.org is 209.160.32.194

Good luck.

From there, it might be interesting to play with the cache size and ttl on both pihole and BIND. Which one should forget the entry faster ?