How to Set Up Pi-hole v6 with Unbound on Raspberry Pi Using Docker

· Travis Rodgers  · 3 min read

Pi-hole is a powerful network-wide ad blocker that runs on your local network. Pairing it with Unbound—a recursive DNS resolver—gives you complete control over DNS queries without relying on third-party DNS providers like Google or Cloudflare.

This guide shows you how to deploy Pi-hole v6 (which is very much different from v5) with Unbound on a Raspberry Pi using Docker Compose.

Prerequisites

Raspberry Pi (3, 4, or 5) running Ubuntu Server LTS with Docker/Docker Compose installed.

Step 1: Disable systemd-resolved

Pi-hole needs port 53, which is used by systemd-resolved by default:

sudo systemctl disable systemd-resolved
sudo systemctl stop systemd-resolved

Step 2: Create Directory Structure

It’s good practice to deploy applications in the /srv folder (on your Pi).

mkdir -p /srv/docker/pihole/unbound
cd /srv/docker/pihole

Step 3: Create Unbound Configuration

Create /srv/docker/pihole/unbound/unbound.conf:

server:
    # Port to listen on (non-standard to avoid conflict with Pi-hole)
    port: 5335
    
    # Listen on all interfaces (accessible within shared network namespace)
    interface: 0.0.0.0
    
    # Use IPv4 only
    do-ip4: yes
    do-ip6: no
    do-udp: yes
    do-tcp: yes
    
    # Trust glue only if it is within the servers authority
    harden-glue: yes
    
    # Require DNSSEC data for trust-anchored zones
    harden-dnssec-stripped: yes
    
    # Don't use Capitalization randomization
    use-caps-for-id: no
    
    # Reduce EDNS reassembly buffer size
    edns-buffer-size: 1232
    
    # Perform prefetching of close to expired message cache entries
    prefetch: yes
    
    # Number of threads to create
    num-threads: 1
    
    # Power of 2 close to num-threads
    so-rcvbuf: 1m
    
    # Ensure privacy of local IP ranges
    private-address: 192.168.0.0/16
    private-address: 169.254.0.0/16
    private-address: 172.16.0.0/12
    private-address: 10.0.0.0/8
    private-address: fd00::/8
    private-address: fe80::/10

These values ☝️, if you’re wondering where they came from, are straight from the docs.

Step 4: Create Docker Compose File

Create /srv/docker/pihole/docker-compose.yml:

services:
  pihole:
    image: pihole/pihole:2025.11.1
    container_name: pihole
    restart: unless-stopped

    # Use host networking for DNS (port 53)
    network_mode: host

    environment:
      - TZ=America/New_York
      - FTLCONF_webserver_api_password=changeme123
      # - FTLCONF_webserver_domain=pihole.yourdomain.com -- If you plan to have a custom domain for the web interface
      - FTLCONF_LOCAL_IPV4=192.168.1.92 # change me
      - FTLCONF_dns_upstreams=127.0.0.1#5335;127.0.0.1#5335

    volumes:
      - ./etc-pihole:/etc/pihole
      - ./etc-dnsmasq.d:/etc/dnsmasq.d

    cap_add:
      - NET_ADMIN

  unbound:
    image: alpinelinux/unbound:latest
    container_name: unbound
    restart: unless-stopped

    # Share Pi-hole's network stack
    network_mode: service:pihole

    volumes:
      - ./unbound/unbound.conf:/etc/unbound/unbound.conf:ro

Important: Update these values:

  • TZ: Your timezone (list here)
  • FTLCONF_webserver_api_password: Your desired admin password
  • FTLCONF_LOCAL_IPV4: Your Raspberry Pi’s static IP address

Step 5: Deploy the Stack

cd /srv/docker/pihole
sudo docker compose up -d

Step 6: Verify Unbound Integration

Check that Pi-hole is forwarding queries to Unbound:

# Query a domain
dig google.com @127.0.0.1

# Watch live query log
sudo docker exec pihole pihole -t

You should see queries being forwarded to 127.0.0.1#5335 (Unbound) in the logs.

Step 7: Configure Your Network

Point your router’s DNS settings to your Pi’s IP address (e.g., 192.168.1.92), or configure individual devices to use it as their DNS server.

Key Configuration Notes

Pi-hole v6 Environment Variables

Pi-hole v6 uses FTLCONF_* environment variables instead of the old v5 variables:

  • PIHOLE_DNS_ (deprecated)
  • FTLCONF_dns_upstreams (use this)

Network Mode

We use network_mode: host for Pi-hole so it can bind to port 53, and network_mode: service:pihole for Unbound so it shares Pi-hole’s network namespace. This allows them to communicate via 127.0.0.1.

Persistent Data

Your Pi-hole configuration is stored in ./etc-pihole, which persists across container restarts and updates.

Troubleshooting

DNS Not Working

Verify systemd-resolved is disabled:

sudo systemctl status systemd-resolved

Unbound Not Connecting

Check if Unbound is running:

sudo docker logs unbound

Web Interface Not Accessible

Access Pi-hole’s web interface at http://YOUR_PI_IP/admin (e.g., http://192.168.1.92/admin)

Updating

To update Pi-hole and Unbound:

cd /srv/docker/pihole
sudo docker compose pull
sudo docker compose up -d

Your configuration and data will persist across updates.

Conclusion

You now have a privacy-focused, ad-blocking DNS server running on your Raspberry Pi. All DNS queries are resolved recursively via Unbound without relying on third-party DNS providers, giving you complete control and privacy over your network’s DNS traffic.

This page may contain affiliate links. Please see my affiliate disclaimer for more info.

Related Posts

View All Posts »