this stack deploys a DNS solution with ad-blocking, recursive DNS resolution, caching, metrics exporting and log collection
- unbound log collection can be pushed to Loki via Promtail
- unbound stats can be scraped by Prometheus
- Prometheus and Loki can be used as data sources in Grafana for visualization
For more details, see the PUREstack project page: https://github.com/Virgil-TD/PUREstack
A stack for grafana, prometheus and loki is available here: https://github.com/Virgil-TD/GRAPLstack
For an unbound metrics exporter see https://github.com/ar51an/unbound-exporter
For a grafana dashboard for unbound metrics see: https://github.com/ar51an/unbound-dashboard
You can deploy multiple PUREstacks in your environment (for redundancy) although not on the same host as Pihole requires unique ownership of port 53.
Pay attention to the volume directory permissions as different containers can run under different UIDs and GIDs
the permissions are set accordingly and explained in the relevant sections below
the possibility to edit configuration files as your user via ssh or vscode is preserved
(the usernames may look odd on the host but the ID's are correct)
- a host running either a recent Debian-like OS (Works on RPi as well)
- a user with sudo rights
- these instructions assume an installation in ~/dockerprojects/stacks/pure, change to your own preferences
- pihole/pihole:latest
- klutchell/unbound:latest
- redis:latest
- grafana/promtail:3.5.8 (latest may work as well)
- unbound-exporter (build yourself from provided dockerfile)
sudo apt update && sudo apt upgrade -y
sudo reboot
You can skip this if already installed or use the Docker-approved method as described in Official Docker Install.md
sudo apt install -y docker.io docker-compose
sudo systemctl start docker
sudo systemctl enable docker
Clone PUREstack so you get the right files with the right directory structure; permissions will be updated later
git clone https://github.com/Virgil-TD/PUREstack.git \
~/dockerprojects/stacks/pure
- Pi-hole runs as root inside the container
- It can create and manage its own volume directories without manual chown or mkdir
- Configuration is normally handled via the Pi-hole web GUI
- If you want to enforce specific settings, define them in docker-compose or in an .env file
-
The klutchel/unbound image is distroless --> contains only the necessary binaries and libraries to run Unbound, no shell or package manager
-
The image runs Unbound as a non-privileged user UID 101, GID 102 after startup
-
Configuration file unbound.conf and aditional configuration files *,conf in unbound.conf.d need to be readable by 101:102
-
blocklist, although not used by unbound (pihole is doing the blocking) needs to be there to avoid warnings from the exporter
-
unbound.conf: rw for your user, read for 101:102 (file)
-
unbound.conf.d: rwx for your user, rx for 101:102 (directory)
-
blocklists: rwx for your user, readable by 101:102 (directory)
-
unbound.block.conf rw for your user, readable by 101:102 (file) -->contains 3 dummy lines
sudo chown $USER:102 ~/dockerprojects/stacks/pure/volumes/unbound/unbound.conf
sudo chmod 640 ~/dockerprojects/stacks/pure/volumes/unbound/unbound.conf
sudo chown -R $USER:102 ~/dockerprojects/stacks/pure/volumes/unbound/unbound.conf.d
sudo chmod 750 ~/dockerprojects/stacks/pure/volumes/unbound/unbound.conf.d
sudo chown -R $USER:102 ~/dockerprojects/stacks/pure/volumes/unbound/blocklists
sudo chmod 750 ~/dockerprojects/stacks/pure/volumes/unbound/blocklists
sudo chmod 640 ~/dockerprojects/stacks/pure/volumes/unbound/blocklists/unbound.block.conf
- Redis runs as non-privileged user UID 999, GID 999 inside the container
- The data directory must be writable by 999:999
- the volume mapping ensures persistence of Redis data across container restarts
- there is no need to make the directory writable by your user
mkdir -p ~/dockerprojects/stacks/pure/volumes/redis/data
sudo chown -R 999:999 ~/dockerprojects/stacks/pure/volumes/redis/data
sudo chmod 770 ~/dockerprojects/stacks/pure/volumes/redis/data
- there is no supported image for https://github.com/ar51an/unbound-exporter
- you need to build it locally from the provided Dockerfile in ~/dockerprojects/stacks/pure/exporter
- Unbound-exporter runs as non-privileged user UID 1000, GID 1000 inside the container
- It needs read access to the Unbound control socket and the blocklists directory
- The control socket is created by Unbound in the /run/unbound directory which is mapped via a named volume (compose.yaml)
- The blocklists directory is mapped from the host system
- No special permission changes are needed here as the Unbound volume directories are already set above
build the unbound-exporter image locally, it will be availabe in the image list (docker images) as unbound-exporter:latest
cd ~/dockerprojects/stacks/pure/exporter
docker build -t unbound-exporter:latest .
- Promtail runs as non-privileged user UID 1000, GID 1000 inside the container
- The promtail volume directory must be writable by 1000:1000
positions.yaml:
create positions.yaml to avoid errors on first startup, owned by promtail user 1000:1000 and only readable by own user
promtail-config.yaml:
owned by your user and readable by promtail user 1000:1000
sudo touch ~/dockerprojects/stacks/pure/volumes/promtail/positions.yaml
sudo chown 1000:1000 ~/dockerprojects/stacks/pure/volumes/promtail/positions.yaml
sudo chmod 640 ~/dockerprojects/stacks/pure/volumes/promtail/positions.yaml
sudo chown $USER:1000 ~/dockerprojects/stacks/pure/volumes/promtail/promtail-config.yaml
sudo chmod 660 ~/dockerprojects/stacks/pure/volumes/promtail/promtail-config.yaml
PUREstacks compose.yaml requires 4 environment settings to be set:
- webpassword: used to log into the Pihole webinterface
- hostname: will be used by promtail to label data and will show up in Pihole webinterface as hostname
- IP adress host: wil be used to connect Pihole to Unbound
- IP adress Loki: required for promtail to find Loki
Create default .env file:
cat > ~/dockerprojects/stacks/pure/.env <<EOF
WEBPASSWORD=changeme # choose your own password.
HOSTNAME=PUREstack-010 # reuse it in your prometheus configuration to label your host-data consistantly
HOSTIP=192.168.1.10 # IP adress of the PUREstac host
LOKIIP=192.168.1.22 # IP adress of your LOKI server
EOF
these variables are used in the compose.yaml, so PUREstack will not deploy without them. Edit the .env file to reflect the settings in your environment.
sudo nano ~dockerprojects/stacks/pure/.env
after your edits save with Ctrl+O and close out with Ctrl+X next bring up your stack
cd ~/dockerprojects/stacks/pure
docker compose up -d