Pihole + unbound docker setup on Raspberry Pi

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.


So how to setup pihole and unbound into docker container on Raspberry pi?


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

Unbound Configuration

Let’s get recursive DNS server unbound configuration right first.

Create some config files under /home/pi/unbound/

/home/pi/unbound/a-records.conf this file is mandatory required as we mapped volume of unbound docker image, otherwise the container will fail to start. You can leave it empty or put your local custom a records if you need. Below is example.

# A Record
#local-data: "somecomputer.local. A"

# PTR Record
#local-data-ptr: " somecomputer.local."

/home/pi/unbound/unbound.conf main configuration file unbound service. It will automatically generate a default one if you not create it. For this guide, let’s define the unbound.conf first. Example below, modify by your situation.

# If no logfile is specified, syslog is used
# logfile: "/var/log/unbound/unbound.log"
verbosity: 0

access-control: allow
access-control: allow
access-control: allow
access-control: allow
port: 5053
do-ip4: yes
do-udp: yes
do-tcp: yes

# May be set to yes if you have IPv6 connectivity
do-ip6: no

# You want to leave this to no unless you have *native* IPv6. With 6to4 and
# Terredo tunnels your web browser should favor IPv4 for the same reasons
prefer-ip6: no

# Use this only when you downloaded the list of primary root servers!
# If you use the default dns-root-data package, unbound will find it automatically
#root-hints: "/var/lib/unbound/root.hints"

# Trust glue only if it is within the server's authority
harden-glue: yes

# Require DNSSEC data for trust-anchored zones, if such data is absent, the zone becomes BOGUS
harden-dnssec-stripped: yes

# Don't use Capitalization randomization as it known to cause DNSSEC issues sometimes
# see https://discourse.pi-hole.net/t/unbound-stubby-or-dnscrypt-proxy/9378 for further details
use-caps-for-id: no

# Reduce EDNS reassembly buffer size.
# Suggested by the unbound man page to reduce fragmentation reassembly problems
edns-buffer-size: 1472

# Perform prefetching of close to expired message cache entries
# This only applies to domains that have been frequently queried
prefetch: yes

# One thread should be sufficient, can be increased on beefy machines. In reality for most users running on small networks or on a single machine, it should be unnecessary to seek performance enhancement by increasing num-threads above 1.
num-threads: 1

# Ensure kernel buffer is large enough to not lose messages in traffic spikes
so-rcvbuf: 1m

# Ensure privacy of local IP ranges
private-address: fd00::/8
private-address: fe80::/10

private-domain: plex.direct

And download root.hints to /home/pi/unbound/

wget -O root.hints https://www.internic.net/domain/named.root

Unbound initial configuration is done.

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: '3'

    driver: bridge
        - subnet:

    container_name: pihole
    hostname: pihole
    image: pihole/pihole:latest
    - "53:53/tcp"
    - "53:53/udp"
    - "80:80/tcp"
    - "443:443/tcp"
    - 'TZ=Australia/Sydney'
    - 'WEBPASSWORD=yourpasswd'
    - 'DNS1='
    - 'DNS2=no'
    - '/home/pi/pihole/etc-pihole/:/etc/pihole/'
    - '/home/pi/pihole/etc-dnsmasq.d/:/etc/dnsmasq.d/'
    restart: unless-stopped
    container_name: unbound
    image: mvance/unbound-rpi:latest
    - /home/pi/unbound:/opt/unbound/etc/unbound
    - "5053:5053/tcp"
    - "5053:5053/udp"
      disable: true
    restart: unless-stopped

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 @ -p 5053

dig www.google.com @ -p 5053

If all return normal results then unbound is up running.

Then start pihole container.

sudo docker-compose up -d pihole

You can test if pihole is working

dig www.google.com @ -p 53

dig www.google.com @ -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


Hope this post help you and save your time. Please leave comment if you have any question.


Written by Felix. Licensed under CC BY-NC-SA 3.0 Unported.

Leave a Reply


  • Liju

    Super useful. Thanks!

    4 months ago Reply
  • kevin

    There is an error in your docker-compose file.
    driver: bridge
    – subnet:

    driver: and ipam: should both be at the same level

    3 months ago Reply
  • Jack

    I am getting a warning that rcvbuf was not granted 306448 , what is this and what do I need to do to grant it.

    3 months ago Reply
    • FelixOwner

      @Jack: Which step you get this message?

      3 months ago Reply
      • Jack

        @Felix: I completed your tutorial. But when I check unbounds logs. It gives me that warning. I dont know of this is a bad thing?

        3 months ago Reply
        • Jack

          @Jack: Also dnssec is sometimes not validating signatures. How come? Harden dnssec stripped is on yes.

          Also how do I setup unbound dns over https? I read thats a feature now.

          3 months ago Reply
          • Jack

            @Jack: Sorry I mean doh

            3 months ago
          • Jack

            @Jack: Lol again sorry not doh but dot, u can delete that other comment

            3 months ago
  • ch33baguy

    Thank you for your work!
    Really appreciate what you have done here.

    1 month ago Reply
  • Tno


    Just for your information, you should also add this line to the unbound config to prevent warnings from the containers integrated health check:

    test: [“CMD”, “drill”, “@”, “-p 5053”, “cloudflare.com”]

    There is a healthcheck entry in the docker file which goes to the default port :53 and causes a waring message in the log:

    HEALTHCHECK –interval=5s –timeout=3s –start-period=5s CMD drill @ cloudflare.com || exit 1

    By the way, there are also some log entries during the start of the container (tested with unbound-rpi version 1.13.0):

    unbound[1:0] warning: so-rcvbuf 1048576 was not granted. Got 360448. To fix: start with root permissions(linux) or sysctl bigger net.core.rmem_max(linux) or kern.ipc.maxsockbuf(bsd) values.

    and there are also some udp connect failures to the IPv6 addresses of some root servers.

    error: udp connect failed: Cannot assign requested address for 2001:503:c27::2:30 port 53
    error: udp connect failed: Cannot assign requested address for 2001:500:12::d0d port 53
    error: udp connect failed: Cannot assign requested address for 2001:503:c27::2:30 port 53

    But these errors do not cause any issues (and I have also disabled IPv6 support within docker, that’s why this could not work). I would expect that unbound would not try to use IPv6 if settings in unbound.conf are set to not using IPv6 – anyway, no issue at all.


    4 weeks ago Reply
  • pancho-villa

    Hello, and thank you for this! It’s working great here! I was wondering about the root.hints file tho. Shouldn’t you change the unbound.conf to point at it in your docker volume location, it would look like this in unbound.conf:

    root-hints: “/opt/unbound/etc/unbound/root.hints”

    Otherwise when you download it into /home/pi/unbound/ it will default to the /var/lib/unbound/ location. Right? Or am I off base? Also, you have to update that file about every 6 months to update the root servers.

    Thanks again!

    4 weeks ago Reply
    • FelixOwner

      @pancho-villa: Hi pancho-villa, the root-hints path has been quoted out of this config file. And since you download it manually to the mapped unbound docker folder, unbound will automatically find and load it. No need to specify the actual path.

      4 weeks ago Reply


Pihole + unbound docker setup on Raspberry Pi
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 preven…
Scan QR code to continue reading