Pi-hole On Raspberry Pi 4B
Deploy Pi-hole on a dedicated Raspberry Pi 4B with Pi OS Lite, SSH hardening, backups, and the Pi-side steps for active-passive DNS redundancy.
Published December 4, 2025 · Updated May 8, 2026
Pi-hole On Raspberry Pi 4B
This page owns the Raspberry Pi side of the Pi-hole story.
It does not try to explain why Pi-hole belongs in the homelab at all, and it does not repeat the Proxmox container build. Those already have canonical homes.
- For the networking role, redundancy model, and upstream DNS choices, start with Pi-hole In A Homelab.
- For the Proxmox-backed secondary node, use Pi-hole LXC On Proxmox.
What this page covers is the dedicated-Pi path: Raspberry Pi OS Lite, SSH access, security hardening, backup, and the Pi-side work needed when this node becomes the primary DNS server in an active-passive pair.
Hardware And Storage
Hardware Required
✅ Raspberry Pi 4B
✅ 8 GB or larger microSD card for Raspberry Pi OS Lite
✅ Wired Ethernet connection when this Pi will be primary DNS
✅ Power supply: 5 V at 3 A for Raspberry Pi 4B
✅ Case with basic cooling
✅ Another computer to prepare the microSD cardWhy 32 GB Or More
Pi-hole Data Growth Per Day:
├─ Small homelab (50 devices): ~80 MB/day of query logs
├─ Medium homelab (150 devices): ~400 MB/day
├─ Large homelab (300+ devices): ~800 MB/day
│
Storage Usage:
├─ OS (Raspberry Pi OS Lite): ~2 GB after updates
├─ Pi-hole application: ~500 MB
├─ Log retention (24 hours): 80-800 MB
├─ Total needed: 5-10 GB minimum
└─ Recommended: 32 GB (leaves room for updates, backups)If reliability matters more than cost, an SSD is still better than microSD. The optional SSD notes are at the end of this page.
Prepare Raspberry Pi OS Lite
Use Raspberry Pi Imager
Download it from https://www.raspberrypi.com/software/.[^3]
In the imager:
- Choose device: Raspberry Pi 4.
- Choose OS: Raspberry Pi OS Lite (64-bit).
- Choose storage: your microSD card.
Then configure the advanced options before writing. Imager can preconfigure hostname, the local admin user, Wi-Fi, and SSH before first boot, which is the cleanest way to build a headless Pi-hole node.12
Recommended Imager Settings
- Hostname:
pihole - Enable SSH: yes
- Prefer public-key authentication if you already use SSH keys
- Username: choose the admin user you actually plan to keep using; current Raspberry Pi OS images are meant to be customised at install time rather than relying on an assumed default account.2
- Wi-Fi: leave unconfigured if Ethernet is the plan
- Timezone and keyboard: set correctly now so logs and cron behave sanely later
The source notes were explicit here for good reason: wired Ethernet is the right answer for a DNS server. Do not build core DNS on flaky Wi-Fi unless you enjoy avoidable ambiguity.
First Boot And SSH Access
Find the Pi's IP address from the router or with a subnet scan, then connect:
ssh <admin-user>@<pihole-ip>Update the system before installing Pi-hole:
sudo apt update
sudo apt upgrade -yInstall Pi-hole
Use the official installer:
curl -sSL https://install.pi-hole.net | bashAfter installation, add your normal admin user to the pihole group so Pi-hole v6 CLI commands can authenticate cleanly without prompting every time in a fresh shell.34
sudo usermod -aG pihole $USERDuring the installer:
- choose a reliable upstream provider unless you already know you want Unbound somewhere else in the design
- enable the web interface
- keep query logging on unless you have a specific privacy reason to reduce visibility
Privacy Mode Guidance
The installer asks about Pi-hole privacy mode. For a personal homelab, the sensible default is still the most useful one.
| Mode | Best For | Recommendation |
|---|---|---|
| Show Everything | personal homelab, troubleshooting, visibility | Recommended |
| Hide Domains | partial privacy with some device visibility | Rarely the best final choice |
| Hide Domains and Clients | shared household where visibility is sensitive | Reasonable compromise |
| Anonymous Mode | maximum privacy, minimal diagnostics | Avoid unless you really mean it |
You can change it later in the web UI under Settings -> Privacy.5
At the end of installation, save the admin password the installer prints.
Network Configuration
Recommended: Let The Router Keep DHCP
The cleanest setup is still:
- router handles DHCP
- Pi-hole handles DNS
- router advertises the Pi-hole IP to clients
Use DHCP reservation or MAC binding in the router so the Pi always gets the same address. Raspberry Pi's own networking guidance recommends router-side DHCP reservation over manually pinning a static IP on the device for service hosts.6
Then set the router DNS values to:
- primary DNS: the Pi-hole IP
- secondary DNS: the fallback or second Pi-hole IP for your final design
Verify from another machine:
nslookup google.comAdvanced: Pi-hole As DHCP Server
Only do this if the router cannot hand out the DNS settings you need. Disable DHCP on the router first, then configure Pi-hole's built-in DHCP from the web UI or in /etc/pihole/pihole.toml.35
[dhcp]
active = true
start = "192.168.50.100"
end = "192.168.50.199"
router = "192.168.50.1"
netmask = "255.255.255.0"
leaseTime = "24h"Pi-hole's FTL docs prefer the web UI, API, or CLI when possible because those paths validate settings before writing them. Manual edits still work, but treat them like infrastructure changes and restart FTL afterward.5
Security Hardening
Move The Web UI To A Non-Default Port
Changing the port reduces noise, not exposure. Treat it as housekeeping, not as a substitute for firewall rules.
Edit the Pi-hole config:
ssh <admin-user>@<pihole-ip>
sudo nano /etc/pihole/pihole.tomlSet:
[webserver]
port = "31415s"Then restart FTL:
sudo systemctl restart pihole-FTL
ss -tulnp | grep ':8080'
sudo tail -50 /var/log/pihole/FTL.log
sudo pihole statusChange The Admin Password
Do it immediately after installation:
sudo pihole setpasswordEnable UFW
# Install UFW
sudo apt install ufw -y
# Initialize firewall
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Allow specific ports
sudo ufw allow 22/tcp # SSH (enable before activating!)
sudo ufw allow 53/tcp # DNS TCP
sudo ufw allow 53/udp # DNS UDP
sudo ufw allow 67/udp # DHCP (if using Pi-hole DHCP)
sudo ufw allow 8080/tcp # Web UI for the example above
# Enable firewall
sudo ufw enable
# Verify
sudo ufw status verboseMove SSH To Keys
If you are imaging from scratch, Imager can preload your public key and avoid a password-authenticated first login entirely. If you are converting an existing host, move to keys before you disable password auth.27
From your computer:
# macOS/Linux
ssh-keygen -t ed25519 -C "pihole@home" -f ~/.ssh/piholeCopy the key over:
ssh-copy-id -i ~/.ssh/pihole.pub pi@<pihole-ip>Then on the Pi:
ssh pi@<pihole-ip>
# Edit SSH config
sudo nano /etc/ssh/sshd_config
# Find and change these lines:
PubkeyAuthentication yes
PasswordAuthentication no
PermitRootLogin no
# Restart SSH
sudo systemctl restart sshAdd fail2ban
# Install fail2ban
sudo apt install fail2ban -y
# Copy default config
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
# Edit configuration
sudo nano /etc/fail2ban/jail.localIn the sshd section:
enabled = true
maxretry = 3
findtime = 600
bantime = 3600Then:
sudo systemctl restart fail2ban
sudo systemctl enable fail2ban
# Check status
sudo fail2ban-client status sshdKeep Raspberry Pi OS Updated
sudo apt update
sudo apt full-upgrade -yRaspberry Pi's security guidance is simple here: stay current on the latest security fixes, and keep SSH and firewall rules explicit instead of assuming the defaults are "close enough."7
Performance And Storage Hygiene
Check disk usage and database growth periodically:
# Check current disk usage
df -h
# Monitor Pi-hole database growth
du -sh /etc/pihole/
# Check logs
tail -f /var/log/pihole/pihole.logIf you want to tune the DNS cache:
sudo nano /etc/pihole/pihole.toml[dns.cache]
size = 10000sudo systemctl restart pihole-FTL
sudo pihole statusPi-hole v6 keeps its primary DNS settings in /etc/pihole/pihole.toml. Avoid legacy /etc/dnsmasq.d drop-ins unless you intentionally enable misc.etc_dnsmasq_d, because that path is off by default in v6. If you truly need raw dnsmasq directives, use misc.dnsmasq_lines and keep the change set small.5
Watch resource usage directly:
# Check CPU usage
top -o %CPU
# Check memory
free -h
# Check temperature
vcgencmd measure_tempBackup And Recovery
Create a simple backup script on the Pi:
mkdir -p "$HOME/pihole-backups"
nano "$HOME/backup-pihole.sh"Use this script:
#!/bin/sh
set -eu
STAMP=$(date +%Y%m%d_%H%M%S)
DEST="$HOME/pihole-backups/$STAMP"
mkdir -p "$DEST"
sudo sqlite3 /etc/pihole/pihole-FTL.db ".backup '$DEST/pihole-FTL.db'"
sudo cp /etc/pihole/pihole.toml /etc/pihole/gravity.db "$DEST"/
cp -a "$HOME/.ssh" "$DEST/ssh-home"The important difference in Pi-hole v6 is that the long-term query database should be backed up with SQLite's online backup method while FTL is still running. pihole.toml and gravity.db are regular file copies.89
Make it executable and schedule it:
chmod +x "$HOME/backup-pihole.sh"
# Edit crontab
crontab -e
# Add this line (backs up at 2 AM daily):
0 2 * * * /home/<admin-user>/backup-pihole.shTo restore:
STAMP=YYYYMMDD_HHMMSS
sudo cp "$HOME/pihole-backups/$STAMP/pihole.toml" /etc/pihole/pihole.toml
sudo cp "$HOME/pihole-backups/$STAMP/gravity.db" /etc/pihole/gravity.db
sudo cp "$HOME/pihole-backups/$STAMP/pihole-FTL.db" /etc/pihole/pihole-FTL.db
sudo systemctl restart pihole-FTL
sudo pihole statusIf gravity.db is actually damaged rather than just stale, use Pi-hole's built-in recovery path instead of trying to hand-repair it:
sudo pihole -g -r recover
# or, as a last resort:
sudo pihole -g -r recreateHigh Availability From The Raspberry Pi Side
This is where the attachment had the most overlap with the rest of the wiki, so this page stays disciplined.
It does not repeat the Proxmox secondary deployment or Docker alternative. Those already have their own canonical homes.
What stays here is the Pi-side active-passive model: the Pi as a dedicated primary node, gravity-sync, DNS TTL choices, and failover testing.
Router DNS For An Active-Passive Pair
Configure the router to advertise both DNS servers:
- primary DNS: the Raspberry Pi node
- secondary DNS: the secondary node, often the Proxmox-backed instance
Keep DHCP on the router. That remains the cleanest model for this lab.
Reduce TTL So Failover Is Not Glacial
On both Pi-hole instances:
sudo nano /etc/pihole/pihole.toml[misc]
dnsmasq_lines = [
"local-ttl=300",
"min-cache-ttl=300",
"max-cache-ttl=3600"
]sudo systemctl restart pihole-FTLThat keeps cache behavior sane while still allowing clients to recover within a few minutes when the primary disappears. These are advanced dnsmasq lines in Pi-hole v6, so keep them deliberate and identical across both nodes.5
Do Not Start New Pi-hole 6 Pairs On gravity-sync
Do not build a new Pi-hole 6 redundancy plan around gravity-sync. The project was retired in July 2024, archived by its maintainer, and the final release notes explicitly say version 4.0.7 should continue to work with Pi-hole 5.x, but not with Pi-hole 6+ because of architecture changes.10
For Pi-hole 6 pairs, keep the sync story explicit:
- document local DNS records, DHCP scopes, and upstream resolvers in one source of truth
- reproduce configuration changes on the secondary node immediately after changing the primary
- if you automate sync, use tooling you understand and test against Pi-hole 6 instead of inheriting an archived project
Failover Test
To verify the active-passive model really works:
# On the Raspberry Pi primary
sudo systemctl stop pihole-FTL
# From a client, DNS should still resolve via the secondary
nslookup google.comThen restore the primary and confirm it is healthy again:
sudo systemctl start pihole-FTL
sudo pihole statuspihole disable only turns blocking off; it does not take DNS down. For an actual failover test, stop pihole-FTL and force the clients to use the secondary resolver.48
Monitor HA Health
Create a simple local health check:
# Save as /home/pi/pihole-health-check.sh
sudo nano /home/pi/pihole-health-check.shUse:
#!/bin/bash
# Pi-hole Health Check
# Check local DNS
LOCAL_DNS=$(dig @127.0.0.1 google.com +short)
if [ -z "$LOCAL_DNS" ]; then
echo "❌ Local DNS not responding!"
exit 1
else
echo "✅ Local DNS responding"
fi
# Check disk space
DISK_USAGE=$(df /etc/pihole | awk 'NR==2 {print $5}' | sed 's/%//')
if [ $DISK_USAGE -gt 80 ]; then
echo "⚠️ Disk usage high: ${DISK_USAGE}%"
fi
echo "Health check complete: $(date)"Schedule it hourly:
0 * * * * /home/pi/pihole-health-check.sh >> /var/log/health-check.log 2>&1Optional: Move Pi-hole Data To SSD
If you want to reduce dependence on microSD wear:
# Connect SSD via USB 3.0
# Create mount point
sudo mkdir -p /mnt/pihole-ssd
# Identify USB device
lsblk
# Format device (WARNING: destroys data!)
sudo mkfs.ext4 /dev/sda1
# Mount permanently
sudo nano /etc/fstab
# Add this line:
/dev/sda1 /mnt/pihole-ssd ext4 defaults 0 2
# Apply
sudo mount -a
# Move Pi-hole data to SSD
sudo cp -r /etc/pihole /mnt/pihole-ssd/
sudo mount --bind /mnt/pihole-ssd/pihole /etc/piholeRelated Topics
- Pi-hole In A Homelab — the networking role, upstream DNS strategy, and redundancy model.
- Pi-hole LXC On Proxmox — the Proxmox-backed secondary node instead of a second Pi-based deployment.
- Backup And Recovery — the broader backup discipline that should still shape how you think about DNS infrastructure.
Footnotes
-
Raspberry Pi Imager is the official path for writing Raspberry Pi OS images and preconfiguring a headless system: Raspberry Pi Software, Raspberry Pi Imager docs. ↩
-
Imager's OS customisation flow can preconfigure hostname, localisation, the admin user, Wi-Fi, and SSH, including public-key authentication: Raspberry Pi Imager docs. ↩ ↩2 ↩3
-
Pi-hole's official post-install docs say the preferred model is to have the router hand out Pi-hole as DNS, using Pi-hole's built-in DHCP only when the router cannot advertise the DNS server you want: Pi-hole Post-Install. ↩ ↩2
-
Pi-hole v6 CLI authentication can be simplified by adding your local user to the
piholegroup: Pi-hole Post-Install. ↩ ↩2 -
Pi-hole v6 stores its main configuration in
/etc/pihole/pihole.toml; the docs prefer web UI/API/CLI changes when possible, document the[dhcp]and[webserver]sections, and warn thatmisc.etc_dnsmasq_ddefaults tofalsewhilemisc.dnsmasq_linesis the advanced path for raw dnsmasq directives: Pi-hole FTL Configuration Reference. ↩ ↩2 ↩3 ↩4 ↩5 -
Raspberry Pi networking guidance recommends DHCP reservation on the router for hosts that need a stable address, rather than manually pinning a static IP on the device itself: Raspberry Pi Configuration. ↩
-
Raspberry Pi's operational security guidance covers key-based SSH hardening, UFW, and Fail2ban, including the warning to allow SSH before enabling the firewall: Raspberry Pi Security Guidance. ↩ ↩2
-
Pi-hole documents the supported admin commands for status, password changes, repairs, reloads, and updates through the
piholeCLI wrapper: Thepiholecommand. ↩ ↩2 -
Pi-hole's query database docs describe SQLite online backup for
/etc/pihole/pihole-FTL.db, and the database recovery docs documentpihole -g -r recoverandpihole -g -r recreateforgravity.db: Pi-hole Query Database, Pi-hole Database Recovery. ↩ -
gravity-syncwas retired in July 2024, archived by its maintainer, and the maintainer states the final release only continues to work with Pi-hole 5.x, not Pi-hole 6+: vmstan/gravity-sync. ↩