Table of Contents
Pi-hole & Unbound DNS Docker Setup #
This is a docker compose setup which starts a Pi-hole and nlnetlab’s Unbound as upstream recursive DNS using official (or ready-to-use) images. The main idea here is to add security, privacy and have ad and malware protection, everything hosted locally.
If you want to learn more about why you want to have exactly this setup, read a detailed explanation here.
Understand the Setup #
This setup works on a machine that does not itself already has DNS running (i.e. port 53 is already used). If you have a setup like that (e.g. running on a Synology NAS with a Directory Server), you would need a setup that creates a Mac VLAN so the container appears with a different IP. In this case check out this example here.
It is designed to have 2 containers running next to each other and do not aim to combine both programs in one. The idea is to minimize the work needed to adapt provided containerized versions of Pi-hole and Unbound, i.e. use the official images, therefore making it easier to upgrade each.
First you need a recent version of Docker installed which at least supports Docker compose v2. Further you may want to have a server or IoT device where this stack can run on, since this should be reachable by every other client 24/7. Finally, don’t forget to change your default DNS server to the server IPs address of your server.
The main configuration can be set in the
.env file which overwrites the ENV variables in the
docker-compose.yml - change it to your liking:
WEBPASSWORD= # set the password to use in the Web Admin UI
HOST_IP_V4_ADDRESS= # the IP of the host the Pi-hole runs on - defaults to localhost
TIMEZONE= # set your timezone (used to schedule cron jobs e.g.)
Deploy Containers #
Start the stack with going to the root of the repo and do:
docker compose up --build -d --remove-orphans
To stop everything:
docker compose down
Pro-Tip, if you want to directly deploy to a remote you can do
docker compose -H "ssh://your-remote-host" up --build -d --remove-orphans
Use Web UI #
If you didn’t change anything and start this on your local machine you can access the Pi-hole web ui with
The default password is
Test Setup #
To test if Pi-Hole with unbound is working correctly you can use the test domain
unboundpiholetestdomain.org I set up in Unbound.
In your terminal (you might need to install
nslookup unboundpiholetestdomain.org localhost
This command will use localhost as DNS, if you are running it on a different machine, use the appropriate IP.
This should return the IP
if setup correctly it should also work without forcing DNS
Persistence after Restart #
By default, Pi-hole will forget everything after a restart of the docker container. To change that you need to set
a docker volume to show Pi-hole where to save the configuration. You need to map
a directory on the server. Read here if you want to learn more about volumes.
There is an example in the
# RECOMMENDED: Uncomment and adapt if you want to persist Pi-hole configurations after restart
# - "/var/lib/docker/volumes/pihole/pihole:/etc/pihole/"
# - "/var/lib/docker/volumes/pihole/dnsmasq.d:/etc/dnsmasq.d/"
Pi-hole Configurations #
docker-compose.yml you can add or change the Pi-hole Docker standard configuration variables in
Check out possible configurations here.
Additionally, you can change various settings in your Pi-hole instance (e.g. the used ad-list) through the web ui. I won’t
get into detail here apart from recommending
https://v.firebog.net/hosts/lists.php as a good default starting list.
Upgrade Base Images #
docker-compose.yml change the used Pi-hole version by changing
image: Pi-hole/Pi-hole:2023.03.1 # <- update image version here, see: https://github.com/pi-hole/docker-pi-hole/releases
and Unbound by changing the
# Update the version here, I use the docker build from https://github.com/MatthewVance/unbound-docker
Define Local A-Records #
If you want to resolve certain domains locally you can set A-Records in
./unbound/conf/a-records.conf. There are already examples, but to add a new record do:
# Example: Resolve all *.mysite.com addresses to the same ip of the main reverse proxy / router
local-zone: "mysite.com." redirect
local-data: "mysite.com. 86400 IN A 192.168.1.1"
Unbound, Forwarders and Manual Configuration #
Unbound is set as a recursive DNS, because all forwarders in
./unbound/conf/a-records.conf are commented out. If you prefer to use cloudflare or any other public DNS as upstream instead of having the slight performance impact of directly asking the nameservers, then you can enable the respective server by removing the comment (but then using Unbound at all has little value.
If you want to fine-tune the Unbound configuration, you can add the file
./unbound/conf/unbound.conf (see an example here) and Unbound will use it.
Supported Platforms #
Currently, this setup will only support platform type
amd64, that means it will not run on machines that e.g. have an ARM architecture like the Raspberry Pi. While the official Pi-hole image supports multi-arch, MatthewVance’s unbound image does not. There is, however, a solution: there is a specific build for
arm/v7 which can be found on Docker hub. Just update the
If you use tools like Watchtower to be notified about image updates - this will not work with Unbound here since we re-build it to create a self-contained, stateless image. It is possible to use the image
mvance/unbound directly in the
docker-compose and mount the configuration files to unbound instead of pre-building it. See MatthewVance readme on how to do that.
- nlnetlabs Unbound (BSD license)
- MatthewVance’s Unbound Docker Image (MIT License)
- Pi-hole (European Union Public License)
- Official Pi-hole Docker Image (unknown license)
Similar Projects #
Further Information #
- Pi-hole Documentation
- Unbound Documentation
- Pi-hole + Unbound Details
- How to run docker-compose on remote host?
Copyright 2023 Patrick Favre-Bulle
Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.