Pihole with Unbound using Docker with Portainer GUI

Hi all,

The issue I am facing:

I have a new pi-hole setup that I am working on inside Docker. I am running into an issue getting pi-hole to work with unbound. It seems like the setup is refusing queries, and I am not certain if this is a template issue or an unbound config issue. I feel like I am very close to getting this to work, but I am missing something. I attached the error i get when i do a DNS lookup from my PC:

C:\Users\Noah>nslookup google.com 192.168.1.164
Server: UnKnown
Address: 192.168.1.164

*** UnKnown can't find google.com: Query refused

Details about my system:

Docker with Portainer. Pi-hole and Unbound are set up as a stack configuration.

OS: Debian GNU/Linux 13 (trixie) x86_64
Host: HP a6000n
Kernel: Linux 6.12.74+deb13+1-amd64
CPU: AMD Athlon(tm) 64 X2 Dual Core 4200+ (2) @ 2.20 GHz
Memory: 1.04 GiB / 7.64 GiB (14%)

What I have changed since installing Pi-hole:

The stack that I am currently running is pasted below.

services:
  pihole:
  container_name: pihole
  image: pihole/pihole:latest
  depends_on:
    - unbound
  ports:
    # DNS Ports
    - "53:53/tcp"
    - "53:53/udp"
    # Default HTTPs Port. FTL will generate a self-signed certificate
    - "443:443/tcp"
  environment:
    # Set the appropriate timezone for your location
    TZ: 'America/New_York'
    # Set a password to access the web interface.
    FTLCONF_webserver_api_password: ${Web_Server_Password}
    # Docker's default `bridge` network setting the dns listening mode should be set to 'ALL'
    FTLCONF_dns_listeningMode: 'ALL'
    # DNS Upstream provider
    FTLCONF_dns_upstreams: '127.0.0.1#53'
    # Volumes store your data between container upgrades
  volumes:
    # For persisting Pi-hole's databases and common configuration file
    - './etc-pihole:/etc/pihole'
  cap_add:
    - SYS_NICE
  restart: unless-stopped

unbound:
  container_name: unbound
  image: klutchell/unbound:latest
  volumes:
    - './etc-unbound:/etc/unbound'
  restart: unless-stopped

You've created a DNS loop, feeding queries back to Pi-hole itself:
A loopback address like 127.0.0.1 points to the machine its attached to.
Within a Docker container, that is the container itself, and within the Pi-hole container, it is Pi-hole that listens on port 53.

You'd have to use your unbound container's IP instead.
As long as your Pi-hole container is using Docker's internal DNS resolver (i.e. no custom dns: section under pihole:), and if unbound and Pi-hole containers both run within the same Docker network (if the docker compose you've shared is complete, that should be the case), you should be able to just replace that line with:

FTLCONF_dns_upstreams: unbound

Hello,

Thanks for the response. That is what I initially did and why I tried the loop before asking for help. When I use the line

FTLCONF_dns_upstreams: unbound

I get the following DNS SERVER FAILURE error inside PiHole:

Cannot create NTP socket (Address family not supported by protocol), IPv6 NTP server not available

This causes the following to happen when I do a lookup from my PC:

C:\Users\Noah>nslookup google.com 192.168.1.164
DNS request timed out.
timeout was 2 seconds.
Server:  UnKnown
Address:  192.168.1.164

DNS request timed out.
timeout was 2 seconds.
DNS request timed out.
timeout was 2 seconds.
DNS request timed out.
timeout was 2 seconds.
DNS request timed out.
timeout was 2 seconds.
*** Request to UnKnown timed-out

I don’t have the NTP client or server running which is why I am stumped that it is trying to create the socket with IPv6. Also, on my host machine, I have disabled IPv6 for hardening which I have just verified.

Edit: I have also spun Pi-hole up in my environment without unbound, and that comes up perfectly.

Very oldschool bro! :smiling_face_with_sunglasses::grimacing::+1::+1:

But the powerdraw is probably way too high for what it does and can probably be replaced by a small Intel N100 NUC-like device with passive cooling so do yourself a favor and look into that :wink:

/Offtopic, but thought I should mention it just in case anyway… :innocent:

lol it’s an old pc i got for free from a coworker :rofl:. I’m surprised by what it can still run

That's not a DNS server failure, but an NTP server error.

Do you mean you have disabled Pi-hole's embedded NTP client and server?
The docker compose you've shared does not set the corresponding env vars:

FTLCONF_ntp_ipv6_active: false
FTLCONF_ntp_ipv4_active: false
FTLCONF_ntp_sync_active: false

In any case, Pi-hole's NTP server not starting wouldn't affect DNS resolution, so your DNS timeouts are tied to unbound, or perhaps something may be interfering with DNS on the client that ran the nslookup, as Server: UnKnown wouldn't happen if Pi-hole had been involved.

You could consider to apply klutchell's example configuration for running Pi-hole together with their Docker container, which would explicitly force Pi-hole and unbound on the same Docker internal network.

Thank you! It works now. I used the example configuration that you linked, adjusted it by adding in some of my settings like getting the DNS password from a .env, and tested it. it resolves google, uses unbound, and blocks a website I tested. Also, to answer your question about the NTP, I meant that I noted out the sections that set up the port and user for it. I am going to attach what my final configuration looks like, and mark your response as the answer as I am certain others may run into this issue.

# Config from https://github.com/klutchell/unbound-docker/tree/main/examples
# More info at https://github.com/pi-hole/docker-pi-hole/ and https://docs.pi-hole.net/

volumes:
  pihole:

services:
  pihole:
    container_name: pihole
    image: pihole/pihole:latest
    ports:
      # DNS Ports
      - "53:53/tcp"
      - "53:53/udp"
      # Default HTTP Port
      #- "80:80/tcp"
      # Default HTTPs Port. FTL will generate a self-signed certificate
      - "443:443/tcp"
      # Uncomment the line below if you are using Pi-hole as your DHCP server
      #- "67:67/udp"
      # Uncomment the line below if you are using Pi-hole as your NTP server
      #- "123:123/udp"
    networks:
      - default
    environment:
      # Sets upstream DNS to unbound sibling container
      FTLCONF_dns_upstreams: unbound
      # If using Docker's default `bridge` network setting the dns listening mode should be set to 'all'
      FTLCONF_dns_listeningMode: all
      # Set the appropriate timezone for your location
      TZ: 'America/New_York'
      # Set a password to access the web interface. CREATE A .ENV VARIABLE
      FTLCONF_webserver_api_password: ${Web_Server_Password}
    volumes:
      - './etc-pihole:/etc/pihole'
      # Uncomment the below if you have custom dnsmasq config files that you want to persist. Not needed for most starting fresh with Pi-hole v6. If you're upgrading from v5 you and have used this directory before, you should keep it enabled for the first v6 container start to allow for a complete migration. It can be removed afterwards. Needs environment variable FTLCONF_misc_etc_dnsmasq_d: 'true'
      #- './etc-dnsmasq.d:/etc/dnsmasq.d'
    cap_add:
      # See https://github.com/pi-hole/docker-pi-hole#note-on-capabilities
      # Required if you are using Pi-hole as your DHCP server, else not needed
      #- NET_ADMIN
      # Required if you are using Pi-hole as your NTP client to be able to set the host's system time
      #- SYS_TIME
      # Optional, if Pi-hole should get some more processing time
      - SYS_NICE
    restart: unless-stopped

  unbound:
    image: klutchell/unbound
    networks:
      - default
    healthcheck:
      # Use the drill wrapper binary to reduce the exit codes to 0 or 1 for healthchecks
      test: ['CMD', 'drill-hc', '@127.0.0.1', 'dnssec.works']
      interval: 30s
      timeout: 30s
      retries: 3
      start_period: 30s
    volumes:
      #- /path/to/config:/etc/unbound/custom.conf.d
      - './etc-unbound:/etc/unbound/conf.d'
    restart: unless-stopped

networks:
  default:
    driver: bridge

Fabulous. :slight_smile:

I'd still recommend configuring your env vars to disable Pi-hole's NTP server for both IPv4 and IPv6 if you don't need it.

And you should probably consider dumping the entire healthcheck: section of your unbound container.
dnssec.works works reasonably well for manually verifying that unbound is operational, but it also tends to be inaccessible every now and then, making it an inadequate fit for a health check.