This Docker deployment runs both Pi-Hole and Unbound in a single container.
The base image for the container is the official Pi-Hole container, with an extra build step added to install the Unbound resolver directly into to the container based on instructions provided directly by the Pi-Hole team.
Image | Tag | Build | Latest |
---|---|---|---|
ghcr.io/lizenzfass78851/docker-pihole-unbound | stable | 📌 | |
ghcr.io/lizenzfass78851/docker-pihole-unbound | oldstable | ||
ghcr.io/lizenzfass78851/docker-pihole-unbound | beta |
- Matrix Build State
First create a .env
file to substitute variables for your deployment.
docker run -d \
--name='pihole' \
-e TZ="Europe/Berlin" \
-e 'TCP_PORT_53'='53' -e 'UDP_PORT_53'='53' -e 'UDP_PORT_67'='67' -e 'TCP_PORT_80'='80' -e 'TCP_PORT_443'='443' \
-e 'TZ'='Europe/Berlin' \
-e 'FTLCONF_webserver_api_password'='******' \
-v "$PWD/pihole/pihole/":'/etc/pihole/':'rw' \
-v "$PWD/pihole/dnsmasq.d/":'/etc/dnsmasq.d/':'rw' \
--cap-add=NET_ADMIN \
--hostname=pihole \
'ghcr.io/lizenzfass78851/docker-pihole-unbound:latest'
Vars and descriptions replicated from the official pihole container:
Docker Environment Var | Description |
---|---|
TZ: <Timezone> |
Set your timezone to make sure logs rotate at local midnight instead of at UTC midnight. |
FTLCONF_webserver_api_password: <Admin password> |
http://pi.hole/admin password. Run docker logs pihole | grep random to find your random pass. |
FTLCONF_dns_revServers: <enabled>,<ip-address>[/<prefix-len>],<server>[#<port>],<domain> |
Enable Reverse server (former also called "conditional forwarding") feature |
USE_IPV6: <"true"|"false"> |
Set to true if ipv6 is needed for unbound (not required in most use-cases) |
Example .env
file in the same directory as your docker-compose.yaml
file:
TZ=America/Los_Angeles
FTLCONF_webserver_api_password=QWERTY123456asdfASDF
FTLCONF_dns_revServers=true,192.168.0.0/16,192.168.1.1,local
HOSTNAME=pihole
DOMAIN_NAME=pihole.local
Portainer stacks are a little weird and don't want you to declare your named volumes, so remove this block from the top of the docker-compose.yaml
file before copy/pasting into Portainer's stack editor:
volumes:
etc_pihole-unbound:
etc_pihole_dnsmasq-unbound:
docker-compose up -d
If using Portainer, just paste the
docker-compose.yaml
contents into the stack config and add your environment variables directly in the UI.
There is also a variant of docker-pihole-unbound that works with separate containers
docker-compose.yml
version: '2'
services:
pihole:
container_name: pihole
image: pihole/pihole:2025.03.0 # <- update image version here, see: https://github.com/pi-hole/docker-pi-hole/releases
ports:
- 53:53/tcp # DNS
- 53:53/udp # DNS
- 80:80/tcp # HTTP
- 443:443/tcp # HTTPS
environment:
- TZ=${TZ}
- FTLCONF_webserver_api_password=${FTLCONF_webserver_api_password}
- FTLCONF_dns_revServers=${FTLCONF_dns_revServers}
- FTLCONF_dns_upstreams=unbound # Hardcoded to our Unbound server
- FTLCONF_dns_dnssec=true # Enable DNSSEC
volumes:
- etc_pihole:/etc/pihole:rw
- etc_pihole_dnsmasq:/etc/dnsmasq.d:rw
networks:
- pihole-unbound
restart: unless-stopped
depends_on:
- unbound
unbound:
container_name: unbound
image: mvance/unbound:latest
networks:
- pihole-unbound
restart: unless-stopped
networks:
pihole-unbound:
volumes:
etc_pihole:
etc_pihole_dnsmasq:
The variant with the docker-compose.yml
example there is a 2-container solution, which is also shown on the pihole discourse forum with the type and white connection between the containers (without manual IP's between the containers) declared has also been confirmed.