Running Pi-hole in 2025 — updated best practices (Docker, Unbound, DoH)
A concise 2025 follow-up: how to run Pi-hole reliably today — Docker Compose example, Unbound resolver, DNS-over-HTTPS options, systemd-resolved tips, and troubleshooting checklist.
Pi-hole remains the easiest way to get network-wide adblocking, but the recommended setups have shifted since 2022. This follow-up covers practical, low-maintenance approaches for 2025: running Pi-hole in Docker (recommended for most installs), using a local recursive resolver (Unbound), optionally using DNS-over-HTTPS (DoH) for upstream privacy, and common pitfalls with systemd-resolved or router DHCP.
🛡️ Why run Pi-hole differently in 2025
Hardware and OS expectations have changed: many users now prefer containerised deployments for portability, simplified updates, and better sandboxing. Also, privacy-focused upstreams (DoH/DoT) and local recursive resolvers are now easy to run alongside Pi-hole — giving you both performance and privacy.
Key goals for the modern setup:
- Keep Pi-hole small and easy to update (Docker Compose recommended)
- Avoid leaking DNS to third parties (Unbound or DoH)
- Make sure DHCP and DNS roles are unambiguous (router vs Pi-hole)
- Ensure persistent, restored firewall/iptables or nftables rules across reboot
⚙️ Recommended deployment options
Option A Docker Compose (recommended for most users)
Running Pi-hole in Docker isolates it from OS changes and makes upgrades repeatable. Below is a minimal, ILLUSTRATIVE docker-compose.yml. Adapt the UID/GID, timezone and network interfaces for your host.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
version: "3.8"
services:
pihole:
image: pihole/pihole:latest
container_name: pihole
restart: unless-stopped
ports:
- "53:53/tcp"
- "53:53/udp"
- "80:80/tcp"
- "443:443/tcp" # optional if using DoH proxy inside the container
environment:
TZ: 'Europe/Berlin'
WEBPASSWORD: 'changeme'
DNSMASQ_LISTENING: 'all'
volumes:
- ./etc-pihole:/etc/pihole
- ./etc-dnsmasq.d:/etc/dnsmasq.d
cap_add:
- NET_ADMIN
networks:
- pihole_net
networks:
pihole_net:
driver: bridge
Notes:
- Bind port 53 on the host. If another service (like systemd-resolved) already listens on 53, stop or reconfigure it.
- Persist
/etc/piholeand/etc/dnsmasq.dso updates keep your config and blocklists. -
NET_ADMINcapability is often needed for DNS features; avoid giving more privileges than necessary.
Option B Native install (Pi OS or other Linux)
If you prefer a system package, the official installer still works. Native installs can be slightly leaner on resources and simpler for single-board computers without Docker.
1
curl -sSL https://install.pi-hole.net | bash
Remember to configure a static IP and ensure the Pi is the primary DNS target for clients or the router.
🔍 Upstream resolver choices — privacy vs performance
Unbound (local recursive resolver)
Running Unbound locally gives the best privacy (no upstream logs) and often better response times once cached. Run Unbound on the same host (localhost or container) and point Pi-hole’s upstream to 127.0.0.1#5335 (or the Unbound container address).
Quick Unbound install (Debian/Raspbian):
1
2
3
sudo apt update
sudo apt install unbound
# use the example config from the Unbound docs for stub or full recursive
Then in Pi-hole Admin → Settings → DNS, add 127.0.0.1#5335 as a custom upstream and disable public upstreams if you want full recursion only.
DNS-over-HTTPS (DoH) / DNS-over-TLS (DoT)
If you prefer to use a privacy-preserving upstream instead of Unbound, use a DoH/DoT proxy (cloudflared, doh-proxy, or a small forwarder) and point Pi-hole at localhost:PORT. This keeps upstream queries encrypted to the provider.
Example using cloudflared as an outbound proxy:
1
2
cloudflared proxy-dns --port 5053 --upstream https://1.1.1.1/dns-query
# then set Pi-hole to use 127.0.0.1#5053
Tradeoffs:
- Unbound avoids any third-party resolver entirely (best privacy)
- DoH/DoT keeps upstream private from on-path observers but still trusts a provider
🧩 Common gotchas and system integrations
systemd-resolved and port 53 conflicts
Many modern Linux distributions have systemd-resolved listening on port 53 (loopback). If you see “address already in use” when starting Pi-hole or its container, either:
- Disable or reconfigure
systemd-resolvedto avoid binding port 53, or - Run Pi-hole on a different host interface and update your router/device DNS to point to it.
To disable systemd-resolved (example):
1
2
3
4
sudo systemctl stop systemd-resolved
sudo systemctl disable systemd-resolved
sudo rm /etc/resolv.conf
sudo ln -s /run/systemd/resolve/resolv.conf /etc/resolv.conf
(Adjust carefully for your distro — some use resolvconf or rely on systemd-resolved.)
DHCP: router vs Pi-hole
Decide where DHCP should live. Running Pi-hole as DHCP server gives you integrated client names and grouping, but many prefer the router’s DHCP for network-wide stability. If you keep router DHCP, set the router’s DNS to the Pi-hole IP instead of changing every device.
Persistent firewall rules
If you add NAT or forwarding rules for captive setups (e.g., Wi‑Fi on the Pi), ensure rules persist across reboot (iptables-persistent, nftables’ persistent backend, or systemd service to restore rules).
🗂️ Managing blocklists and false positives
- Use curated sources like Firebog to find reliable lists. Don’t add dozens of overlapping lists — that can slow gravity and increase false positives.
- Use group management in the Pi-hole UI to allow different clients different block policies.
- Whitelist and use regex carefully; log the queries (FTL) to understand blocked domains.
🔁 Maintenance and updates
- For Docker:
docker-compose pull && docker-compose up -dto update. - For native:
pihole -upto update Pi-hole components. - Regularly run
pihole -gafter changing blocklists to refresh gravity. - Backup
/etc/piholeand group configurations (Teleporter) before major upgrades.
🧭 Traffic flow overview
graph LR
client["Client device"] -->|"DNS query"| pihole["Pi-hole (DNS)"]
pihole -->|"Local recursion"| unbound["Unbound (local) | 127.0.0.1:5335"]
pihole -->|"DoH/DoT upstream"| doh["DoH/DoT provider"]
pihole -->|"Blocked (sinkhole)"| null["Blocked / sinkholed domain"]
✅ Quick checklist — post-install verification
- DNS port free:
sudo ss -ltnp | grep :53 - Pi-hole reachable:
curl -I http://<PI_IP>/admin(replace<PI_IP>) - Upstream resolver check:
dig @127.0.0.1 -p 5335 example.com(if using Unbound) - DoH proxy check:
dig @127.0.0.1 -p 5053 example.com(if using cloudflared) - Gravity updated:
pihole -g - FTL status:
pihole-FTL status
🧠 Final Thoughts
Pi-hole in 2025 is flexible: containerised deployments simplify maintenance, and pairing Pi-hole with Unbound or a DoH proxy gives you excellent privacy and performance. Pick the deployment model that matches your comfort level (Docker for portability, native for minimalism) and follow the checklist above to avoid the common pitfalls.
This post is a follow-up to the 2022 overview and focuses on robust, maintainable setups for modern home networks.
