New releases
Recipe version compared with the latest known upstream release. A version number, not a reading of what changed.
Pi-hole filters DNS requests for every device on a home network, including TVs and phones that cannot use a browser extension. Its web dashboard shows query statistics, client activity, and allow and deny lists. This recipe runs the official container with persistent SQLite data, a local control panel, and DNS bound to localhost until you choose a trusted LAN or VPN address.
Pi-hole is a DNS resolver with blocklists. Put it on a trusted network and set it as the DNS server in your router: requests for advertising and tracking domains are blocked for phones, TVs, and other devices without browser add-ons. It suits a home network or a small private network whose administrator can control DHCP or the DNS settings of each client. It is a self-hosted alternative to services such as NextDNS and AdGuard DNS.
The web interface shows recent queries, blocked requests, clients, and list management. Its interface is currently available in English. Pi-hole can also serve local DNS records. DNS filtering cannot remove ads served from the same domain as desired content, and clients that override the network DNS with their own encrypted resolver can bypass it.
This recipe runs one official container. Its pi-hole-data volume holds
pihole.toml, Gravity and query databases, and list data. The DNS port starts
on 127.0.0.1; choose a specific trusted LAN or VPN address when ready to serve
other devices. The web panel remains on the host’s loopback address.
The container uses Docker’s bridge network without privileged mode, host networking, or access to the Docker socket. Only port 53 is needed for DNS; the built-in DHCP and NTP services are not exposed. The official image’s own healthcheck checks DNS service readiness.
Do not bind DNS to a public interface: an open resolver can be used in
amplification attacks. A Pi-hole administrator can inspect the domains queried
by every client, so keep the web panel behind its password and HTTPS when
accessing it remotely. The .env password and backup archives are sensitive.
See the official Docker guide and
configuration reference.
These are stored results of separate checks, not real-time data. An unknown status means the check has not been performed yet.
Recipe version compared with the latest known upstream release. A version number, not a reading of what changed.
Result of a dedicated Docker image scan, not a Compose configuration check.
A new major version is found by comparing version numbers. A compatibility verdict is entered by hand after reading the release notes and the migration steps.
Upstream Compose changes relative to the verified recipe.
Pinned image tags checked for newer versions.
From observation to action
The recipe already pins the latest known release. The steps below show the evidence retained for that version.
2026.09.02026.09.0Open this release on GitHub ↗ The link is evidence to review, not proof of compatibility.
The version numbers do not cross a major compatibility boundary. No compatibility verdict has been recorded.
No image-tag change is currently planned.
Change the tag and restart. Release-specific migration steps still come from the release notes and installation guide.
No version change is currently planned. The recipe includes a backup procedure. Restore has been tested.
An exact Fully verified check for this target was recorded on 2026-10-09.
The status shows how fully the published recipe meets each level's requirements.
A minimal setup for a local network or access through a VPN.
The recipe covers every requirement at this level.
A public deployment with protected access and backups.
You still need to add: domain, https via reverse proxy, off-server backup.
A setup designed for predictable operations and observability.
You still need to add: separate database, external monitoring, cpu and memory limits.
Read the full guide first: it explains external access, backups, and data locations.
cp .env.example .envdocker compose up -ddocker compose psUse Ubuntu 22.04+ or Debian 12+ with Docker Engine and Docker Compose v2.24+. Allow at least one CPU core, 512 MB RAM, and 2 GB of disk; 1 GB RAM and 4 GB of disk leave room for retained query history and several blocklists. No separate database is needed. Check the tools and whether anything already uses DNS port 53:
docker --version
docker compose version
sudo ss -luntp | grep ':53 ' || true
systemd-resolved often listens on 127.0.0.53, which does not necessarily
conflict with this recipe’s 127.0.0.1 bind. Change the host resolver only if
the actual address conflicts. Do not point the host’s /etc/resolv.conf at
Pi-hole before Pi-hole is running.
Place compose.yaml, .env.example, backup.sh, restore.sh, and proxy/
in a dedicated directory, then create your private environment file:
mkdir -p ~/services/pi-hole
cd ~/services/pi-hole
cp .env.example .env
chmod 600 .env
openssl rand -hex 24
Replace the PIHOLE_WEB_PASSWORD example in .env with the generated value.
Never start a real instance with the sample password. The variables are:
PIHOLE_VERSION: pinned official image release;PIHOLE_WEB_PASSWORD: administrator password;PIHOLE_DNS_BIND: host address for TCP and UDP DNS, initially 127.0.0.1;PIHOLE_DNS_PORT: host DNS port, normally 53;PIHOLE_WEB_PORT: host-only admin port, initially 8080;PIHOLE_VOLUME: Docker volume holding all persistent Pi-hole data;TZ: IANA time zone for charts and schedules.The Compose file sets FTLCONF_dns_listeningMode=ALL because DNS arrives
through Docker’s bridge. It does not publish Pi-hole’s DHCP (67/udp), NTP
(123/udp), or self-signed HTTPS (443/tcp) listeners.
docker compose config --quiet
docker compose pull
docker compose up -d --wait
docker compose ps
With the defaults, DNS and the panel are reachable only from the host. On a VPS,
keep DNS on a private VPN address, such as WireGuard or Tailscale, by setting
PIHOLE_DNS_BIND to that interface address. Never use 0.0.0.0 or a public
address: public DNS resolvers are abused for amplification. The panel can be
accessed with an SSH tunnel:
ssh -L 8080:127.0.0.1:8080 user@server.example
Open http://localhost:8080/admin/ and sign in with the password from .env.
The web interface is currently English-only. Test the DNS server on the host:
dig @127.0.0.1 pi.hole
Set PIHOLE_DNS_BIND in .env to this server’s specific LAN address, for
example 192.168.1.10, and recreate the container:
docker compose up -d --wait
docker compose port pi-hole 53/udp
Permit UDP and TCP 53 only from the trusted subnet in your firewall. For UFW:
sudo ufw allow from 192.168.1.0/24 to 192.168.1.10 port 53 proto udp
sudo ufw allow from 192.168.1.0/24 to 192.168.1.10 port 53 proto tcp
Then set 192.168.1.10 as the DNS server in your router’s DHCP settings, or
configure clients individually. Test from another device with
nslookup pi.hole 192.168.1.10. Do not enable Pi-hole’s DHCP service with this
recipe: bridge networking does not carry the broadcasts it needs.
The proxy/Caddyfile, proxy/nginx.conf, and proxy/traefik.yaml examples
publish only the web panel; substitute your own dns.example.com. Caddy obtains
certificates automatically; Nginx expects a Certbot certificate; Traefik uses
the configured letsencrypt resolver. If Traefik runs in a container, replace
127.0.0.1 with a host gateway address that container can reach. Keep the
Pi-hole login enabled and restrict panel access to trusted users. An HTTP proxy
does not carry ordinary DNS traffic on port 53.
All persistent settings, blocklists, Gravity database, and query history live
in the pi-hole-data Docker volume (or the name in PIHOLE_VOLUME). The script
stops Pi-hole briefly so its SQLite files are consistent:
chmod +x backup.sh restore.sh
./backup.sh
Copy the resulting ./backups/pi-hole-*.tar.gz off the server and protect it;
it contains DNS history and configuration. The .env password is separate and
must be stored securely too. The web interface’s Teleporter export is useful
for selected settings, but this full-volume archive is needed for a complete
restore.
Restoring replaces the entire PIHOLE_VOLUME contents. The script creates a
safety backup of the current state first:
./restore.sh ./backups/pi-hole-YYYYMMDDTHHMMSS.NNNNNNNNNZ.tar.gz
docker compose ps
Restore the matching .env separately if the password or volume name changed.
Read the Docker release notes
and create a backup. Change PIHOLE_VERSION in .env to a tested, dated tag;
never use latest. Then:
./backup.sh
docker compose pull
docker compose up -d --wait
docker compose logs --tail=100 pi-hole
Restore the previous pinned tag in .env, pull it, and recreate the container.
If the new version changed pihole.toml or the SQLite databases, restore the
pre-update archive too; an older image may not read newer data:
docker compose pull
docker compose up -d --wait
./restore.sh ./backups/pi-hole-YYYYMMDDTHHMMSS.NNNNNNNNNZ.tar.gz
The restore script first saves the current data, then replaces the volume. Check the panel and a DNS query after the rollback.
docker compose down stops and removes the container while retaining its data.
After moving the router and clients to another DNS server, permanently remove
the stored data with docker compose down --volumes (or remove the volume named
by PIHOLE_VOLUME). Keep any off-server backups you still need. Removing the
resolver while clients still point to it interrupts name resolution.
Sources: official Docker setup, Docker configuration and capabilities, the hardware prerequisites, and Pi-hole v6 configuration.
Tell us how it went on your server
Opens a prefilled GitHub issue. A GitHub account is required; submitting the issue makes it public.
Check the exact listening address, not just the port:
sudo ss -luntp | grep ':53 '
docker compose logs --tail=100 pi-hole
127.0.0.53 used by systemd-resolved is distinct from the default
127.0.0.1 bind. If a service holds 127.0.0.1:53 or all interfaces, choose
a specific free LAN/VPN address or reconfigure the conflicting service.
Avoid changing the host resolver until a working alternative is configured.
The official image’s healthcheck makes a DNS request to its own FTL process:
docker compose ps
docker inspect --format '{{json .State.Health}}' "$(docker compose ps -q pi-hole)"
docker compose logs --tail=150 pi-hole
If the logs show a corrupted database, restore a known good volume archive. If they show a permissions or disk error, inspect the volume and free space.
docker compose port pi-hole 53/udp
nslookup pi.hole 192.168.1.10
If Compose reports 127.0.0.1, set PIHOLE_DNS_BIND to the actual LAN address
and recreate the container. Permit both TCP and UDP 53 in the LAN firewall.
Make sure the router hands out the same server address over DHCP.
Check the query log: if the client’s requests are absent, it may use its own DNS-over-HTTPS or a different DNS server. Network filtering cannot remove content served from the same domain as the page itself. For a broken site, find its blocked domain in Query Log and add only that domain to the allowlist. Avoid disabling all blocking to fix one false positive.
Verify that .env contains the intended PIHOLE_WEB_PASSWORD and recreate the
container with docker compose up -d. The FTLCONF_webserver_api_password
environment setting controls the password on every start. Avoid pasting a
password into shell history or posting docker inspect output publicly.
Check that PIHOLE_VOLUME names the same volume as the backup and that the
archive contains pihole.toml and gravity.db:
docker compose config
docker volume inspect pi-hole-data
tar -tzf ./backups/pi-hole-YYYYMMDDTHHMMSS.NNNNNNNNNZ.tar.gz | grep -E 'pihole.toml|gravity.db'
Do not create new lists before finding the correct volume or restoring the pre-update archive.
Fossary is an independent informational catalog and is not affiliated with the developers of the listed applications. We collect information from public sources and verify published recipes, but we do not develop, distribute, or control these applications.
A listing or validation status is not a requirement, endorsement, or personal recommendation to use an application. You decide whether it is suitable and use it at your own risk. We do not warrant an application's security, availability, or fitness for purpose and, to the extent permitted by law, are not liable for resulting loss or damage. Review its license, security settings, data processing, and backup procedures before installation.