Docker compose: PiHole and Unbound [arm64]
Posted: 2026 Jan 17, 14:58
PiHole and Unbound docker compose install and configuration:
Uncomment the cloudflare_tunnel network settings if you want to expose the PiHole UI over the internet via a cloudflare tunnel. Adjust the settings based on your configuration.
PiHole UI Configuration
Master / Principal router DNS Settings (Connected to the ISP)
DNS Server1 IP Address:
DNS Server2 IP Address (In case of PiHole DNS Fail)
DNS Server3 IP Address (In case CloudFlare DNS Fail)
Configure the "Proxy NDP" (The "Announcer" Fix)
For Debian GNU/Linux 13 \n \l or above create the new file path /etc/sysctl.d/99-pihole-ipv6.conf and add the following configuration to make it permanent:
To apply the settings immediately without rebooting
For NDP proxy service, create service file path: /etc/systemd/system/ndp-proxy.service and add the following configuration:
Start and enable the ndp-proxy.service
Create the configuration file for radvd
Add the following configuration in the radvd.conf file
AdvOnLink on — tells clients that fd53:50:50::/64 is directly reachable on the local link, so they'll use NDP to find fd53:50:50::50 instead of looking for a route.
AdvAutonomous off — clients will not generate their own addresses in that range (keeps it clean).
Restart radvd:
Verify NDP Proxy Is Active. The Pi must respond to Neighbor Solicitations for the Pi-hole container address:
Windows check: Wait ~30–60 seconds to receive the new RA, then:
PiHole Lists
Migrated from /etc/pihole/adlists.list
Multi
Blocks pop-up ads
Threat intelligence (malware, phishing, cryptojacking)
Fake and scam sites
Extended protection including telemetry and free hosts
Extended protection including telemetry and free hosts
URLhaus Malware Blocklist
scams, fraud, and phishing
- Docker network inteface creation
Code: Select all
docker network create -d macvlan \
--ipv6 \
--subnet=192.168.1.0/24 \
--gateway=192.168.1.1 \
--subnet=fd53:50:50::/64 \
--gateway=fd53:50:50::1 \
-o parent=eth0 \
lan_ipv6
- docker-compose.yml
Code: Select all
services:
unbound:
container_name: unbound
image: crazymax/unbound:latest
platform: linux/arm64
mac_address: F6:30:0F:89:B5:9E
networks:
lan_ipv6:
ipv4_address: 192.168.1.51
ipv6_address: fd53:50:50::51
restart: unless-stopped
volumes:
# CrazyMax uses the /config folder for custom settings
- /apps/docker-containers/pi-hole/unbound:/config
pihole:
container_name: pihole
image: pihole/pihole:latest
hostname: dns-pole.local
mac_address: F6:30:0F:89:B5:9F
ports:
- "53/tcp"
- "53/udp"
- "192.168.1.50:8081:80/tcp" # Allow HTTP UI connection to PiHole
- "192.168.1.50:443:443/tcp" # Allow HTTPs UI connection to PiHole
environment:
TZ: 'Europe/Bucharest'
# FTLCONF_webserver_api_password: 'correct horse battery staple'
FTLCONF_dns_listeningMode: 'ALL'
FTLCONF_webserver_port: '80o,[::]:80o'
FTLCONF_RATE_LIMIT: '5000/0'
volumes:
- '/apps/docker-containers/pi-hole/etc-pihole:/etc/pihole'
- '/apps/docker-containers/pi-hole/etc-dnsmasq.d:/etc/dnsmasq.d'
cap_add:
- NET_ADMIN
- SYS_TIME
- SYS_NICE
restart: unless-stopped
depends_on:
- unbound
networks:
lan_ipv6:
ipv4_address: 192.168.1.50
ipv6_address: fd53:50:50::50
# cloudflare_tunnel:
# ipv4_address: 172.70.70.3
networks:
lan_ipv6:
external: true
# cloudflare_tunnel:
# external: true
- Unbound configuration file path /apps/docker-containers/pi-hole/unbound/unbound.conf
Code: Select all
server:
interface: 0.0.0.0
interface: ::0
port: 5335
do-ip4: yes
do-ip6: yes
do-udp: yes
do-tcp: yes
prefer-ip6: no
# ... other settings ...
so-rcvbuf: 0
so-sndbuf: 0
# Allow queries from your local network subnets
access-control: 127.0.0.0/8 allow
access-control: ::1/128 allow
access-control: fe80::/10 allow
access-control: 192.168.1.0/24 allow
access-control: fd53:50:50::/64 allow
# Privacy and Security
hide-identity: yes
hide-version: yes
harden-glue: yes
harden-dnssec-stripped: yes
# ... Custom ...
incoming-num-tcp: 40
outgoing-num-tcp: 40
outgoing-range: 1024
num-queries-per-thread: 4096
use-caps-for-id: no
prefetch: yes
rrset-roundrobin: yes
num-threads: 2
msg-cache-size: 256m
rrset-cache-size: 512m
- Login into PiHole via web on one of the following IPs setup previously
Code: Select all
ipv4_address: 192.168.1.50
ipv6_address: fd53:50:50::50
- Go to Settings > DNS > Uncheck all Upstream DNS Servers
- On Custom DNS servers add the IPs v4 and v6 from Unbound
Code: Select all
192.168.1.51#5335
fd53:50:50::51#5335
- LAN > DHCP Server
DNS Server1 IP Address:
Code: Select all
192.168.1.50
Code: Select all
1.1.1.1
Code: Select all
192.168.1.1
- Network configuration where above docker containers are running
Code: Select all
sudo ip link set eth0 promisc on
- Network enable ip v4 and v6 Routing
Code: Select all
sudo sysctl -w net.ipv4.ip_forward=1
sudo sysctl -w net.ipv6.conf.all.forwarding=1
sudo sysctl -w net.ipv6.conf.eth0.accept_ra=2
- Enable NDP Proxying in the kernel
Code: Select all
sudo sysctl -w net.ipv6.conf.all.proxy_ndp=1
sudo sysctl -w net.ipv6.conf.eth0.proxy_ndp=1
Code: Select all
net.ipv4.ip_forward=1
net.ipv6.conf.all.forwarding=1
net.ipv6.conf.eth0.accept_ra=2
net.ipv6.conf.all.proxy_ndp=1
net.ipv6.conf.eth0.proxy_ndp=1
Code: Select all
sudo sysctl --system
- Add Proxy NDP
Code: Select all
sudo ip -6 neigh add proxy fd53:50:50::50 dev eth0
sudo ip -6 neigh add proxy fd53:50:50::51 dev eth0
Code: Select all
nano /etc/systemd/system/ndp-proxy.service
Code: Select all
[Unit]
Description=IPv6 NDP Proxy for Pi-hole and Unbound
After=network.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/sbin/ip -6 neigh add proxy fd53:50:50::50 dev eth0
ExecStart=/sbin/ip -6 neigh add proxy fd53:50:50::51 dev eth0
[Install]
WantedBy=multi-user.target
Code: Select all
sudo systemctl daemon-reload
sudo systemctl enable --now ndp-proxy.service
- Check echo reply from docker containers ip
Code: Select all
ping6 fd53:50:50::50
ping6 fd53:50:50::51
- In case your ISP router does not expose DHCPv6 or RA RDNSS settings:
Code: Select all
sudo apt update && sudo apt install radvd -y
Code: Select all
touch /etc/radvd.conf
Code: Select all
nano /etc/radvd.conf
Code: Select all
interface eth0 {
AdvSendAdvert on;
AdvManagedFlag off;
AdvOtherConfigFlag off;
MinRtrAdvInterval 30;
MaxRtrAdvInterval 100;
AdvDefaultLifetime 0;
prefix fd53:50:50::/64 {
AdvOnLink on;
AdvAutonomous off;
AdvRouterAddr off;
};
RDNSS fd53:50:50::50 {
AdvRDNSSLifetime 600;
};
};
AdvAutonomous off — clients will not generate their own addresses in that range (keeps it clean).
Restart radvd:
Code: Select all
sudo systemctl restart radvd
Code: Select all
# Check existing proxy entries
ip -6 neigh show proxy
Code: Select all
# OUTPUT #
fd53:50:50::50 dev eth0 proxy
fd53:50:50::51 dev eth0 proxy
- Check functionality from a different linux machine
Code: Select all
ip -6 neigh show
Code: Select all
# OUTPUT SHOULD BE SOMETHING LIKE THIS #
fe80::1 dev enp1s0 lladdr c0:9f:e1:91:08:9c router REACHABLE
Code: Select all
#Check routing table for the new prefix (powershell as admin)
Get-NetRoute -AddressFamily IPv6 | Where-Object { $_.DestinationPrefix -like "fd53*" }
Code: Select all
# OUTPUT #
PS C:\WINDOWS\system32> Get-NetRoute -AddressFamily IPv6 | Where-Object { $_.DestinationPrefix -like "fd53*" }
ifIndex DestinationPrefix NextHop RouteMetric ifMetric PolicyStore
------- ----------------- ------- ----------- -------- -----------
6 fd53:50:50::/64 :: 256 25 ActiveStore
PS C:\WINDOWS\system32>
Code: Select all
dig @fd53:50:50::50 google.com AAAA
Code: Select all
# OUTPUT #
; <<>> DiG 9.11.9 <<>> @fd53:42:42::80 google.com AAAA
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 42070
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
; OPT=15: 00 03 ("..")
;; QUESTION SECTION:
;google.com. IN AAAA
;; ANSWER SECTION:
google.com. 0 IN AAAA 2a00:1450:400d:811::200e
;; Query time: 1 msec
;; SERVER: fd53:50:50::50#53(fd53:50:50::50)
;; WHEN: Tue Apr 14 13:27:01 GTBDT 2026
;; MSG SIZE rcvd: 73
Migrated from /etc/pihole/adlists.list
Multi
Blocks pop-up ads
Threat intelligence (malware, phishing, cryptojacking)
Fake and scam sites
Extended protection including telemetry and free hosts
Extended protection including telemetry and free hosts
URLhaus Malware Blocklist
scams, fraud, and phishing