Pihole is DNS based Ad blocking solution. It can also be used to enhance your home network security by filtering out malicious domain and provide privacy protection by preventing unnecessary telemetry data leaking out.
I have mentioned couple times in my previous posts. As pihole project has very good documentation of installation, even with the configuration of unbound recursive DNS server, I don’t feel the need to repeat the normal straightforward installation process. However, there’s not much guide talking about the details of pihole + unbound setup in docker container. And during the migration from straight installation to docker containers, I met lot of unexpected issues and mistakes. I think it worth to share my experience and my configuration to save your time doing the troubleshooting.
Why move pihole and unbound to docker container?
Docker adds another abstraction layer on top of OS.
It increases the complexity of initial configuration, but gives you flexibility of future maintenance and migration.
Neat and clean is the main reason people like docker. No need to install hundreds of library and packages into Operation System which may mess up.
Each service is a container. Configuration can be managed by file and docker-compose.yaml. Very quick to replicate the configuration to different devices.
And by using Raspberry pi, considering the SD card may wear after constant write, to backup the docker container configuration is the easiest way to recovery the configuration.
How?
So how to setup pihole and unbound into docker container on Raspberry pi?
Prerequisite
You need a Raspberry Pi and with OS (Raspbian) installed.
Raspbian has already configured with SSH access and static IP address / DNS resolver.
Raspbian installed docker packages including docker-compose. (search google or docker website if you don’t know how to)
File and Structure
Assume you have logged in Raspbian with user ‘pi
‘, your home directory will be /home/pi/
.
Create below folders and files with the right path with mkdir
command.
mkdir /home/pi/pihole
mkdir /home/pi/pihole/etc-dnsmasq.d
mkdir /home/pi/pihole/etc-pihole
mkdir /home/pi/unbound
mkdir /home/pi/unbound/conf.d
mkdir /home/pi/unbound/log.d
mkdir /home/pi/unbound/zones.d
mkdir /home/pi/unbound/iana.d
Pihole and Docker Compose setup
This is the key part of this guide.
Create docker-compose.yml
in your favourite directory with the content below.
version: '2.4' networks: dns_net: driver: bridge ipam: config: - subnet: 172.20.0.0/16 services: pihole: container_name: pihole hostname: pihole image: pihole/pihole:latest networks: dns_net: ipv4_address: 172.20.0.6 ports: - "53:53/tcp" - "53:53/udp" - "80:80/tcp" - "443:443/tcp" environment: - TZ=Australia/Sydney - WEBPASSWORD=<yourpassword> - PIHOLE_DNS_=172.20.0.7#5335 - CUSTOM_CACHE_SIZE=0 volumes: - '/home/pi/pihole/etc-pihole/:/etc/pihole/' - '/home/pi/pihole/etc-dnsmasq.d/:/etc/dnsmasq.d/' restart: unless-stopped unbound: container_name: unbound image: madnuttah/unbound:latest networks: dns_net: ipv4_address: 172.20.0.7 environment: - TZ=Australia/Sydney - ServerIP=172.20.0.7 volumes: - /home/pi/unbound/unbound.conf:/usr/local/unbound/unbound.conf:rw - /home/pi/unbound/conf.d/:/usr/local/unbound/conf.d/:rw - /home/pi/unbound/log.d/:/usr/local/unbound/log.d/:rw - /home/pi/unbound/zones.d/:/usr/local/unbound/zones.d/:rw - /home/pi/unbound/iana.d/:/usr/local/unbound/iana.d/:rw ports: - "5335:5335/tcp" - "5335:5335/udp" healthcheck: disable: true restart: unless-stopped
Initial configures
For the unbound instance, you will probably need to create initial config files to allow it start up.
You can follow the Madnuttah unbound Github examples to create those config files. Please make sure the path to each files are aligned exactly same with docker-compose file volumes mapping path.
Unbound configurations
– I list all my working configurations here in case you have issues with the initial configurations . Bold configuration files are mandatory, others are optional.
/home/pi/unbound/unbound.conf
include: "/usr/local/unbound/conf.d/*.conf" include: "/usr/local/unbound/zones.d/*.conf" server: module-config: "validator iterator" username: "_unbound" directory: "/usr/local/unbound" chroot: "/usr/local/unbound" do-daemonize: no tls-cert-bundle: /etc/ssl/certs/ca-certificates.crt
/home/pi/unbound/conf.d/access-control.conf
server: access-control: 127.0.0.0/8 allow access-control: 192.168.0.0/16 allow access-control: 172.16.0.0/12 allow
/home/pi/unbound/conf.d/interfaces.conf
server: #interface: 127.0.0.1@5335 #interface: ::1@5335 interface: 0.0.0.0@5335 #interface: ::0@5335 #outgoing-interface: 0.0.0.0 so-reuseport: yes do-ip4: yes do-ip6: no do-tcp: yes do-udp: yes udp-connect: yes prefer-ip4: yes prefer-ip6: no
/home/pi/unbound/conf.d/logging.conf
server: use-syslog: no log-time-ascii: yes logfile: "/usr/local/unbound/log.d/unbound.log" log-local-actions: no log-queries: no log-replies: no log-servfail: yes val-log-level: 2 verbosity: 1
/home/pi/unbound/conf.d/performance.conf – Note: This is just an example of my device hardware, Pls adjust based on your spec.
server: num-threads: 4 num-queries-per-thread: 4096 cache-max-ttl: 86400 cache-min-ttl: 300 edns-buffer-size: 1232 rrset-roundrobin: yes neg-cache-size: 4M delay-close: 10000 rrset-cache-size: 256m rrset-cache-slabs: 4 ratelimit: 1000 unwanted-reply-threshold: 10000 infra-cache-slabs: 4 infra-cache-numhosts: 100000 msg-cache-size: 256m msg-cache-slabs: 4 key-cache-size: 4m key-cache-slabs: 4 prefetch: yes prefetch-key: yes serve-expired: yes max-udp-size: 4096 msg-buffer-size: 65552 stream-wait-size: 4m outgoing-range: 32768 outgoing-port-permit: 32768
/home/pi/unbound/conf.d/remote-control.conf
remote-control: control-enable: yes control-use-cert: no
/home/pi/unbound/conf.d/security.conf
server: do-not-query-localhost: no # unblock-lan-zones: no # insecure-lan-zones: yes # private-domain: "yourdomain.lan." # private-domain: "0.168.192.in-addr.arpa." # domain-insecure: "yourdomain.lan." # domain-insecure: "0.168.192.in-addr.arpa." private-address: 10.0.0.0/8 private-address: 172.16.0.0/12 private-address: 192.168.0.0/16 private-address: 169.254.0.0/16 private-address: fd00::/8 private-address: fe80::/10 private-address: ::ffff:0:0/96 hide-identity: yes identity: "server" hide-version: yes version: "" aggressive-nsec: yes qname-minimisation: yes qname-minimisation-strict: no disable-dnssec-lame-check: no hide-trustanchor: yes harden-algo-downgrade: yes harden-below-nxdomain: yes harden-dnssec-stripped: yes harden-glue: yes harden-large-queries: yes harden-referral-path: yes harden-short-bufsize: yes minimal-responses: yes deny-any: yes use-caps-for-id: yes val-clean-additional: yes val-max-restart: 5 root-key-sentinel: yes zonemd-permissive-mode: no
/home/pi/unbound/conf.d/trust-anchor.conf
server: auto-trust-anchor-file: "/usr/local/unbound/iana.d/root.key" trust-anchor-signaling: yes
/home/pi/unbound/zones.d/auth-zone.conf
auth-zone: name: "." primary: 199.9.14.201 # b.root-servers.net primary: 192.33.4.12 # c.root-servers.net primary: 199.7.91.13 # d.root-servers.net primary: 192.5.5.241 # f.root-servers.net primary: 192.112.36.4 # g.root-servers.net primary: 193.0.14.129 # k.root-servers.net primary: 192.0.47.132 # iad.xfr.dns.icann.org primary: 192.0.32.132 # lax.xfr.dns.icann.org primary: 2001:500:200::b # b.root-servers.net primary: 2001:500:2::c # c.root-servers.net primary: 2001:500:2d::d # d.root-servers.net primary: 2001:500:2f::f # f.root-servers.net primary: 2001:500:12::d0d # g.root-servers.net primary: 2001:7fd::1 # k.root-servers.net primary: 2620:0:2830:202::132 # iad.xfr.dns.icann.org primary: 2620:0:2d0:202::132 # lax.xfr.dns.icann.org #url: "https://www.internic.net/domain/root.zone" fallback-enabled: yes for-downstream: no for-upstream: yes zonemd-check: yes zonemd-reject-absence: no zonefile: "/usr/local/unbound/iana.d/root.zone"
/home/pi/unbound/iana.d/root.zone
Download it from https://www.internic.net/domain/root.zone
You can use wget https://www.internic.net/domain/root.zone
to get into /home/pi/unbound/iana.d/
File Permissions
Need to execute sudo chown -R root:1000 /home/pi/unbound/
to make sure the unbound container have the permission to read/write to mapped volume.
Start the services
Let’s start unbound container first
sudo docker-compose up -d unbound
If everything goes right, you will see something says pi_dns_net has been created a new container is up.
You can now test if unbound is working as expected
dig www.google.com @172.20.0.7 -p 5335
dig www.google.com @127.0.0.1 -p 5335
If all return normal results then unbound is up running. Make sure your system iptables allow those 5335 ports.
Then start pihole container.
sudo docker-compose up -d pihole
You can test if pihole is working
dig www.google.com @172.20.0.6 -p 53
dig www.google.com @127.0.0.1 -p 53
You can also try to access http://your-Raspberry-pi-ip/admin/index.php
to configure pihole blocklist and etc.
Override container configuration
If there’s any configuration you need to change, you can first add environment into docker-compose file if the image support.
Otherwise, you can touch the configuration file directly.
For example, if you want to reduce the pihole cache size from 10000 to 0, let unbound do the caching. You can modify /home/pi/pihole/etc-dnsmasq.d/01-pihole.conf
to set cache-size=0
. Save the file then restart dnsmasq service from pihole admin portal.
If you want to add a custom configuration file for pihole or unbound, just add *.conf
file under the mapped volume.
Then restart the container.
sudo docker stop <container name>
sudo docker-compose up -d <container name>
It will usually recreate the container if there’s any configuration change.
Commands to troubleshoot
sudo docker ps
sudo docker inspect <container name, such as pihole or unbound>
sudo docker logs pihole
Maintenance and Update
sudo docker pull pihole/pihole:latest
sudo docker pull mvance/unbound-rpi:latest
sudo docker stop pihole
sudo docker stop unbound
sudo docker rm pihole
sudo docker rm unbound
sudo docker-compose up -d unbound
sudo docker-compose up -d pihole
End
Hope this post help you and save your time. Please leave comment if you have any question.
Leave a Reply