Pi-hole security checklist — admin panel, DNS exposure, and update hygiene
Pi-hole sits in an unusual place on your network. Every DNS query from every device — phones, TVs, laptops, IoT junk — goes through it. That makes it the single highest-leverage box in most homelabs and the one most likely to be running on a Raspberry Pi that hasn't been audited since the day it was flashed. Here are ten hardening checks worth doing this weekend.
For the wider context — what to do before any service becomes reachable from outside the LAN — see before you expose a service to the web. This post is narrower: a Pi-hole that's already running and already in your DNS settings, and the ten things to recheck before you forget about it for another year.
Step 1 — Understand what a compromised Pi-hole actually gives an attacker
Pi-hole's threat model is unusual. Most self-hosted services hold one thing — files, metrics, dashboards. Pi-hole holds the resolver path itself. If someone roots your Pi-hole, they don't just get a Raspberry Pi; they get a full passive view of every DNS lookup on your LAN. Which streaming services you use, which banks, which employer's VPN, which dating apps your kids' phones are talking to. All of it.
That's the passive case. The active case is worse: an attacker
who can flip a record in the Pi-hole database can redirect any
hostname on the LAN to any IP. github.com goes to a
malicious proxy. The OS update server quietly resolves to
attacker infrastructure. There's no certificate warning if the
attacker holds a wildcard cert for a domain they control and
poisons the CNAME to point at it.
The takeaway: Pi-hole isn't "just a DNS server with ad blocking." It's a man-in-the-middle position by design. Treat it accordingly.
Step 2 — Never expose the web admin UI past the LAN
Pi-hole's web interface lives at /admin. The first
and most common compromise vector is finding one of these
reachable from the public internet, usually because someone
port-forwarded port 80 to "check on it from work" and never put
auth in front of it.
The admin password lives in different places depending on how Pi-hole was installed. Bare metal:
# Set or change the admin web password
sudo pihole -a -p
# Disable the admin password entirely (only do this on an
# isolated, single-user LAN — and even then, prefer setting one)
sudo pihole -a -p ''
Docker installs almost always set WEBPASSWORD as an
environment variable in the Compose file. Two things to check
there: that it's set to something non-default, and that the
Compose file itself isn't committed to a public Git repo with the
password in plaintext (it happens more than you'd think).
Pi-hole has no built-in 2FA. If you genuinely need remote access to the admin UI, the right pattern is to put it behind an authenticating reverse proxy — Authelia, Authentik, or Cloudflare Access in front of the Pi-hole web port. Don't port-forward the admin UI even with a password set; the password protects the app, but the network-level exposure means any future Pi-hole admin CVE turns into your CVE the day it lands. The exposed admin surfaces post covers why this category is the single biggest cause of self-host compromise.
Step 3 — Bind the DNS resolver to the LAN interface only
Pi-hole's whole point is to answer DNS on port 53. The question
is where it answers. The default install on a single
interface is fine. The dangerous case is a multi-homed host
where DNS ends up listening on 0.0.0.0 and the
router has a port-forward or the firewall has a hole.
Check what port 53 is bound to:
sudo ss -tulpn | grep ':53 '
# look for 0.0.0.0:53 or [::]:53 — both are wildcard binds
# you want 192.168.x.x:53 or similar (LAN IP only)
In the Pi-hole admin UI, Settings → DNS → Interface listening behaviour controls this. The sensible option is "Allow only local subnets" — Pi-hole will refuse DNS from anything that isn't on a directly-attached subnet. Don't pick "Permit all origins" unless you genuinely intend to run a public resolver, in which case you have a different post to read.
A public Pi-hole isn't just a bad idea for privacy; it's a DNS amplification weapon. Open resolvers are the standard ingredient in reflection DDoS attacks, and most ISPs will eventually notice and shut your line off. Run an external port scan against your public IP and confirm 53 is closed:
nmap -Pn -p 53 -sU -sT your.public.ip.here
Step 4 — Pick a reputable upstream and use encrypted DNS
By default Pi-hole forwards queries to the upstream you picked during install — usually Google, Cloudflare, or your ISP. Two things to fix here. First, get off your ISP if you haven't already; ISPs have a long history of inserting their own answers into DNS responses for "search assistance" and ad tracking. Second, the link between Pi-hole and the upstream should be encrypted, not plain port 53 over the open internet.
The standard pattern is cloudflared or
unbound running locally as a DNS-over-HTTPS or
DNS-over-TLS bridge:
# Pi-hole → cloudflared (localhost:5053) → 1.1.1.1 over HTTPS
# In Pi-hole DNS settings, set custom upstream:
# 127.0.0.1#5053
# cloudflared config (/etc/cloudflared/config.yml)
proxy-dns: true
proxy-dns-port: 5053
proxy-dns-upstream:
- https://1.1.1.1/dns-query
- https://1.0.0.1/dns-query
Quad9 (9.9.9.9) and NextDNS are good alternatives
if you don't want Cloudflare. Whichever you pick, the goal is
the same: your ISP, your transit, and anyone on the path between
your house and the upstream sees encrypted blobs instead of a
log of every domain anyone in the household visits.
Step 5 — Audit blocklist sources before adding them
Blocklists are a supply chain. Every list you subscribe to is a
remote source of rules that Pi-hole pulls down and applies to
every DNS lookup. A malicious or hijacked blocklist could blackhole
auth.bank.example.com for a day, or — more subtly —
inject entries that interfere with security tooling.
Stick to maintainers with public review and active history:
- StevenBlack/hosts on GitHub — the gold-standard aggregated list, public commit history, multiple variant flavours.
- OISD (
oisd.nl) — actively curated, transparent about additions and removals. - The Firebog categorised lists — useful as a directory of reputable sources, not as a blob to import wholesale.
What to avoid: random pastebins, lists copied from forum posts without provenance, and anything that hasn't been updated in a year. The Pi-hole community periodically rediscovers a list that blackholed a major site for weeks before anyone noticed. Pin blocklist URLs to ones whose maintenance you can verify, and review the active list quarterly. Settings → Adlists in the admin UI; weed out anything you don't recognise.
Step 6 — Audit the FTL daemon
pihole-FTL is the engine — the C daemon that
actually answers DNS queries and writes the telemetry database.
It listens on a local Unix socket and a TCP port (default
4711) used by the web UI to read live query stats.
That port should never leave the host.
# Check FTL is only on localhost
sudo ss -tlnp | grep pihole-FTL
# expected: 127.0.0.1:4711 or [::1]:4711, not 0.0.0.0:4711
The FTL config lives at /etc/pihole/pihole-FTL.conf.
Two settings worth looking at: PRIVACYLEVEL
controls how much per-client detail FTL stores (set higher than
the default if multiple humans share the LAN and you'd rather
not have a per-device query log), and the
MAXDBDAYS retention period (default 365 days of
query history sitting on disk — shorten this if the box gets
stolen often or the SD card is small).
If you've ever exposed the Pi-hole host to a wider network for
remote management, run ss -tulpn again and confirm
nothing besides 53 and the LAN-only web port is reachable from
outside the host.
Step 7 — Get Docker port publishing right
Half of all "Pi-hole accidentally public" incidents trace back to one line in a Docker Compose file. The difference between these two:
# BAD — publishes to every interface, including any public IP
ports:
- "53:53/udp"
- "53:53/tcp"
- "80:80/tcp"
# GOOD — publishes only on the LAN IP of the Pi-hole host
ports:
- "192.168.1.10:53:53/udp"
- "192.168.1.10:53:53/tcp"
- "192.168.1.10:80:80/tcp"
Docker's default behaviour is to bind the published port to
0.0.0.0, which on a multi-homed host can mean the
WAN interface. Worse, Docker manages its own iptables rules and
will happily punch holes through ufw without
warning. Even if you ran ufw default deny incoming
from the
pre-exposure checklist, a Docker port-forward bypasses it.
Three checks for any containerised Pi-hole:
- Every
ports:entry has an IP prefix limiting it to the LAN address. docker compose psshows the bind addresses match what you intended (look at the PORTS column).- An external port scan against the public IP returns nothing on 53, 80, or 443 unless you have a public service that needs those ports.
Step 8 — Back up the configuration and lists
Pi-hole stores its state in two places: /etc/pihole
(settings, blocklists, the FTL query DB) and
/etc/dnsmasq.d (DNS forwarding rules and local DNS
records). The built-in backup tool is the Teleporter:
# Export current configuration to a tar.gz
sudo pihole -a -t
# Output: pi-hole-teleporter_YYYY-MM-DD_HH-MM-SS.tar.gz
# Automate via cron (root crontab)
0 3 * * * /usr/local/bin/pihole -a -t && \
mv /root/pi-hole-teleporter_*.tar.gz /backups/pihole/
The Teleporter archive is also what the admin UI's Settings
→ Teleporter page uses for one-click restore. For a
belt-and-braces homelab, also rsync the raw
/etc/pihole and /etc/dnsmasq.d
directories nightly to a NAS or another host. Disk failures
eat Raspberry Pi SD cards on a routine timeline; the day yours
dies is not the day to rediscover that the only copy of the
local DNS records was on it.
Keep the Teleporter exports somewhere encrypted. The archive includes the admin password hash and any custom DNS records that might reveal your internal hostnames.
Step 9 — Patch Pi-hole and FTL on a real cadence
The Pi-hole project ships updates regularly — both the web UI and the FTL daemon — and most installs are running something a year out of date. The fix is trivial:
# Update Pi-hole core, web UI, and FTL
sudo pihole -up
# For Docker installs, pull the new image
docker compose pull pihole
docker compose up -d pihole
Run it monthly at a minimum. Subscribe to the Pi-hole release
notes on GitHub or the project blog so FTL security advisories
don't surprise you. The
patching
gap on self-hosted services covers why this matters more
than people expect — homelab software typically lags upstream
by 6–18 months, and unlike a packaged distro, there's no
unattended-upgrades equivalent watching for you.
Pi-hole runs on top of Linux, so the base system needs the same
attention. If it's a Raspberry Pi, the OS-level openssl and
sshd are usually the bigger CVE source than Pi-hole itself —
see Raspberry Pi
outdated OpenSSL for the canonical example. Run
apt full-upgrade on the same monthly cadence as
pihole -up and reboot when the kernel rolls.
Step 10 — Re-audit after every LAN change
The Pi-hole that was perfectly safe yesterday can be exposed today because something on the network around it changed. The router rebooted and lost its port-forward rules. A new VLAN got added and the firewall didn't get matching ACLs. A guest network was set up that turns out to route DNS through the main subnet. Someone reset the router to factory and the rules vanished.
The audit isn't elaborate. It's the same external scan from Step 3:
# From off-LAN — phone hotspot, $5 VPS, friend's house
nmap -Pn -p 53,80,443 -sU -sT your.public.ip.here
The answer should be the same it was last month. When it isn't, you find out which network change introduced the exposure and reverse it. This is the principle from the monthly homelab security checklist: hardening isn't a state, it's a process. The state regresses on its own — every router firmware update, every new device, every weekend tinkering session is an opportunity for drift.
The 30-minute homelab security baseline is a good companion piece: the Pi-hole audit fits naturally inside the wider monthly sweep.
Where Noxen fits in
Noxen recognises Pi-hole as one of ~70 admin services it
fingerprints, and flags it whenever the web UI is reachable on
any host it's enrolled. Scheduled scans catch the case where the
Pi-hole was safely LAN-only yesterday and a router change
exposed /admin today. The Mac-native control plane
means the scan history and drift diff live on your desk, not in
a SaaS console — important if the whole point of running
Pi-hole was getting query logs off third-party infrastructure.
Try Noxen — $79 one-time, agentless, diff-from-yesterday reports for your homelab fleet.
Scan your Linux fleet from your Mac
Noxen runs nightly agentless audits over SSH and shows only what changed since the last scan — new CVEs, config drift, newly exposed admin services like Pi-hole. Mac-native control plane, no SaaS round-trip.