Page 1 of 1

Docker compose: PiHole and Unbound [arm64]

Posted: 2026 Jan 17, 14:58
by Mihai
PiHole and Unbound docker compose install and configuration:
  • 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
:!: 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.
  • 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
PiHole UI Configuration
  • 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
Master / Principal router DNS Settings (Connected to the ISP)
  • LAN > DHCP Server
Un-check Assign IspDNS
DNS Server1 IP Address:

Code: Select all

192.168.1.50
DNS Server2 IP Address (In case of PiHole DNS Fail)

Code: Select all

1.1.1.1
DNS Server3 IP Address (In case CloudFlare DNS Fail)

Code: Select all

192.168.1.1
  • Network configuration where above docker containers are running
Set eth0 to promisc mode ON

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
Configure the "Proxy NDP" (The "Announcer" Fix)
  • 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
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:

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
To apply the settings immediately without rebooting

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
For NDP proxy service, create service file path: /etc/systemd/system/ndp-proxy.service and add the following configuration:

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
Start and enable the ndp-proxy.service

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:
Install radvd on the Raspberry Pi as a service, outside DOCKER:

Code: Select all

sudo apt update && sudo apt install radvd -y
Create the configuration file for radvd

Code: Select all

touch /etc/radvd.conf
Add the following configuration in the radvd.conf file

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;
    };
};
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:

Code: Select all

sudo systemctl restart radvd
Verify NDP Proxy Is Active. The Pi must respond to Neighbor Solicitations for the Pi-hole container address:

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
Check the "Neighbor" (ARP) Table

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
Windows check: Wait ~30–60 seconds to receive the new RA, then:

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
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