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.

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 card

Why 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:

  1. Choose device: Raspberry Pi 4.
  2. Choose OS: Raspberry Pi OS Lite (64-bit).
  3. 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

  • 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 -y

Install Pi-hole

Use the official installer:

curl -sSL https://install.pi-hole.net | bash

After 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 $USER

During 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.

ModeBest ForRecommendation
Show Everythingpersonal homelab, troubleshooting, visibilityRecommended
Hide Domainspartial privacy with some device visibilityRarely the best final choice
Hide Domains and Clientsshared household where visibility is sensitiveReasonable compromise
Anonymous Modemaximum privacy, minimal diagnosticsAvoid 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

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.com

Advanced: 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.toml

Set:

[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 status

Change The Admin Password

Do it immediately after installation:

sudo pihole setpassword

Enable 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 verbose

Move 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/pihole

Copy 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 ssh

Add 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.local

In the sshd section:

enabled = true
maxretry = 3
findtime = 600
bantime = 3600

Then:

sudo systemctl restart fail2ban
sudo systemctl enable fail2ban
 
# Check status
sudo fail2ban-client status sshd

Keep Raspberry Pi OS Updated

sudo apt update
sudo apt full-upgrade -y

Raspberry 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.log

If you want to tune the DNS cache:

sudo nano /etc/pihole/pihole.toml
[dns.cache]
size = 10000
sudo systemctl restart pihole-FTL
sudo pihole status

Pi-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_temp

Backup 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.sh

To 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 status

If 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 recreate

High 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-FTL

That 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.com

Then restore the primary and confirm it is healthy again:

sudo systemctl start pihole-FTL
sudo pihole status

pihole 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.sh

Use:

#!/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>&1

Optional: 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/pihole
  • 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

  1. 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.

  2. 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

  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

  4. Pi-hole v6 CLI authentication can be simplified by adding your local user to the pihole group: Pi-hole Post-Install. 2

  5. 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 that misc.etc_dnsmasq_d defaults to false while misc.dnsmasq_lines is the advanced path for raw dnsmasq directives: Pi-hole FTL Configuration Reference. 2 3 4 5

  6. 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.

  7. 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

  8. Pi-hole documents the supported admin commands for status, password changes, repairs, reloads, and updates through the pihole CLI wrapper: The pihole command. 2

  9. Pi-hole's query database docs describe SQLite online backup for /etc/pihole/pihole-FTL.db, and the database recovery docs document pihole -g -r recover and pihole -g -r recreate for gravity.db: Pi-hole Query Database, Pi-hole Database Recovery.

  10. gravity-sync was 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.

Comments

Sign in with GitHub to leave a comment or reaction.