# Verified compose for Pi-hole v6, tested 2026-10-19 by the SelfHostBench lab (3 runs; the figures are the median). # Lab host: AMD EPYC 9V74 80-Core Processor, 2 vCPU, 7.8 GiB RAM, x86_64, Docker 28.0.4. # Floating image tags below were replaced by the version resolved at test time; # the registry digest is in the comment on each image line. # Measurements: pihole.json (schema 3). # Lab difference: The lab run publishes no host ports (53, 80 and 443 of the published file are not mapped): the runner's systemd-resolved listens on 127.0.0.53:53 and Docker's port 53 publishing can clash with it, and the setup and load steps reach Pi-hole by service name over the Compose network instead. Image, environment, volume and capabilities are the published file's. The published file has not been run with its ports mapped in the lab. # Companion env file: pihole.env (save it as .env next to this file). # Secrets are not published: the lab's values are public. In pihole.env: # PIHOLE_PASSWORD is commented out. Uncomment it and set your own value (the comment above it explains how). # Pi-hole v6 (official image pihole/pihole) in Docker Compose: a network-wide DNS ad blocker. # Follows the Quick Start of https://github.com/pi-hole/docker-pi-hole (README, tag 2026.10.0) and # https://docs.pi-hole.net/docker/configuration/ . Differences from the README example, each marked below: # the image tag is pinned, the password comes from .env, the data lives in a named volume, and only # the capability Pi-hole needs without DHCP or NTP is added. # There is no TLS in front of the web interface except the self-signed certificate Pi-hole makes for port 443. # Do not expose ports 53 or 80 to the internet: an open resolver is used for DDoS reflection. name: pihole services: pihole: image: pihole/pihole:2026.10.0 # sha256:d34134716fc32038b052a8674c7c6879e0ff21f0e6d8af345166fffa2c27c113 (version 2026.10.0, from label:org.opencontainers.image.version) restart: unless-stopped ports: # DNS, for the devices on your network. If the host's own DNS (systemd-resolved on Ubuntu and Debian) # already listens on port 53, Docker cannot bind it: see "Port 53 is already in use" in the article. - "53:53/tcp" - "53:53/udp" # Web interface and API over HTTP, and over HTTPS (FTL generates a self-signed certificate). # Change the left-hand numbers if another service on the host already uses 80 or 443. - "80:80/tcp" - "443:443/tcp" # Add these only if you use Pi-hole as the DHCP server (it also needs NET_ADMIN below) or as an NTP server: #- "67:67/udp" #- "123:123/udp" environment: # Your time zone (https://en.wikipedia.org/wiki/List_of_tz_database_time_zones): logs rotate at local midnight. TZ: "America/New_York" # Password for the web interface and the API. Set in .env; without one Pi-hole would assign a random password. FTLCONF_webserver_api_password: "${PIHOLE_PASSWORD:?set PIHOLE_PASSWORD in .env}" # Docker's default bridge network: Pi-hole must answer queries that arrive from outside its own subnet. # Without this, v6 ignores them. (README: "should be set to 'ALL'".) FTLCONF_dns_listeningMode: "ALL" volumes: # Pi-hole's databases, lists and settings (pihole.toml). Back this volume up before an upgrade. - pihole-etc:/etc/pihole cap_add: # Optional: lets Pi-hole get more processing time. The README example also lists NET_ADMIN (only for DHCP) # and SYS_TIME (only if Pi-hole should set the host's clock over NTP); they are left out here. - SYS_NICE volumes: pihole-etc: