Setting Up Pi-hole on a Raspberry Pi: DNS Ad Blocking, automation, secure and with global management.
Pi-hole is one of the most satisfying home network projects you can run. A small always-on device quietly blocks ads and trackers for every device on your network — no per-device configuration needed. This guide walks through a complete setup: hardware choices, installation, useful automation via cron, and securing the admin interface with a real Let’s Encrypt certificate on a domain you own.
Hardware
For this setup I’m running a Raspberry Pi 4 connected via Ethernet directly into the router — Wi-Fi introduces unnecessary latency and failure points for something acting as your DNS resolver. The Pi has a static DHCP assignment in the router, keeping it permanently at 192.168.1.56.
One important hardware note: I run the OS off an NVMe drive rather than an SD card. SD cards are not well suited to the constant small reads and writes that Pi-hole generates — they degrade and fail over time. An NVMe drive via a USB 3.0 adapter or a dedicated HAT is significantly faster and will outlast any SD card in this workload. If you’re setting up a Pi-hole you intend to keep running for years, this is worth doing from the start.
Installing Pi-hole
The standard one-line installer covers the basics:
curl -sSL https://install.pi-hole.net | bash
Follow the prompts, point it at your preferred upstream DNS resolver (I use 9.9.9.11 / 9.9.9.9), and note the admin password it generates at the end.
Once installed, go into your router’s DHCP settings and set the Pi’s IP (192.168.1.56) as the primary DNS server handed out to all clients. Every device on your network will now route DNS through Pi-hole automatically.
You may want to set the secondary DNS on the router to your upstream DNS provider (or another such as your ISPs alternative Public resolver) incase the pihole ever goes down.
Automating Maintenance with Cron
Pi-hole needs occasional upkeep — software updates, blocklist refreshes, and general system hygiene. Cron handles all of this automatically. Open the root crontab (Pi-hole needs root for most operations):
sudo crontab -e
Add the following:
# ── Pi-hole ────────────────────────────────────────────────────────────────
# Update Pi-hole weekly (Sunday 3am)
0 3 * * 0 /usr/local/bin/pihole -up >> /var/log/pihole-update.log 2>&1
# Update gravity (blocklists) daily (2am)
# This is the most important one — new ad domains appear constantly
0 2 * * * /usr/local/bin/pihole -g >> /var/log/pihole-gravity.log 2>&1
# Flush DNS logs weekly to keep the database size sensible (Sunday 4am)
0 4 * * 0 /usr/local/bin/pihole flush
# ── System hygiene ─────────────────────────────────────────────────────────
# apt updates daily (1am)
0 1 * * * apt-get update -q && apt-get upgrade -y -q >> /var/log/apt-auto.log 2>&1
# Clean up old packages monthly
0 5 1 * * apt-get autoremove -y -q && apt-get autoclean -q
# ── Raspberry Pi health ────────────────────────────────────────────────────
# Log CPU temperature hourly — useful for spotting thermal issues
0 * * * * vcgencmd measure_temp >> /var/log/pi-temp.log
# Monthly reboot (1st of month, 5am)
0 5 1 * * /sbin/reboot
# ── Log housekeeping ───────────────────────────────────────────────────────
# Truncate Pi-hole logs every 3 months to prevent disk bloat
0 6 1 1,4,7,10 * truncate -s 0 /var/log/pihole-update.log /var/log/pihole-gravity.log
A few notes on the above:
- Always use full paths in cron jobs — cron runs with a minimal
PATHand won’t find commands the way your shell does - The gravity update (blocklist refresh) runs daily because new ad and tracker domains are registered constantly. Keeping gravity fresh is what actually makes Pi-hole effective day-to-day
- The
2>&1at the end of commands redirects stderr into the log file too, so failures are captured - The comma-separated months (
1,4,7,10) in the log truncation job means “January, April, July, October” — quarterly
Securing the Admin Interface with HTTPS and a Real Domain
By default Pi-hole serves its admin interface over HTTP. That’s fine on a purely local network, but if you want your partner (or anyone else) to access it from their phone without browser security warnings, you need a real TLS certificate.
If you try HTTPS without, the browser will complain about the cert.
What we’re aiming for
- Admin accessible at
https://pihole.yourdomain.com - Valid Let’s Encrypt certificate with automatic renewal
- Pi-hole listening on port 443 only (no plain HTTP)
Prerequisites
- A domain you own (this guide uses
pihole.alpagot.netas the example) - Port forwarding set up on your router:
- Either a static IP on your home connection or use of a dynamicDNS service (like noip.com) to have a constant target for your domain to get home to your rapsbery pi.
| External Port | Internal IP | Internal Port | Protocol |
|---|---|---|---|
| 80 | 192.168.1.56 | 80 | TCP |
| 443 | 192.168.1.56 | 443 | TCP |
Port 80 needs to stay open permanently — Let’s Encrypt uses it to verify domain ownership at each renewal. Nothing sensitive listens on port 80 day-to-day.
Set the domain in pihole.toml
Pi-hole v6 uses /etc/pihole/pihole.toml for configuration. Find the domain setting in the [webserver] block and update it with your domain you’ve purchased.
Here i’ve configured the DNS of the domain in my registrar to give the domain a subdomain: pihole. for usage in this file:
[webserver]
domain = "pihole.yourdomain.com"
Install Certbot and obtain a certificate
sudo apt update
sudo apt install certbot -y
Stop Pi-hole FTL so Certbot can briefly use port 80:
sudo systemctl stop pihole-FTL
Obtain the certificate:
sudo certbot certonly --standalone -d pihole.yourdomain.com
Let’s Encrypt will verify your domain ownership and drop the certificate at:
/etc/letsencrypt/live/pihole.yourdomain.com/fullchain.pem
/etc/letsencrypt/live/pihole.yourdomain.com/privkey.pem
Create a combined PEM file
Pi-hole v6 expects a single PEM file containing both the certificate chain and the private key. Create it with:
sudo cat /etc/letsencrypt/live/pihole.yourdomain.com/fullchain.pem \
/etc/letsencrypt/live/pihole.yourdomain.com/privkey.pem \
| sudo tee /etc/pihole/tls-combined.pem > /dev/null
sudo chmod 640 /etc/pihole/tls-combined.pem
sudo chown root:pihole /etc/pihole/tls-combined.pem
Why
teeinstead of a redirect? Thesudoinsudo cat ... > fileonly elevatescat— the shell redirect runs as your user, which doesn’t have write permission to/etc/pihole. Piping throughsudo teesolves this.
Fix Let’s Encrypt directory permissions
The pihole user needs read access to the certificate files:
sudo chmod 755 /etc/letsencrypt/live /etc/letsencrypt/archive
sudo chmod 644 /etc/letsencrypt/archive/pihole.yourdomain.com/fullchain*.pem
sudo chmod 640 /etc/letsencrypt/archive/pihole.yourdomain.com/privkey*.pem
sudo chown root:pihole /etc/letsencrypt/archive/pihole.yourdomain.com/privkey*.pem
Configure Pi-hole to use the cert and listen on 443 only
Stop FTL before editing the toml — Pi-hole v6 rewrites pihole.toml on startup and will overwrite unsaved changes:
sudo systemctl stop pihole-FTL
sudo nano /etc/pihole/pihole.toml
Find and update the port and TLS settings:
[webserver]
domain = "pihole.yourdomain.com"
port = "443s,[::]:443s"
[webserver.tls]
cert = "/etc/pihole/tls-combined.pem"
Start FTL back up:
sudo systemctl start pihole-FTL
Verify it’s listening on 443:
sudo ss -tlnp | grep ':443'
You should see pihole-FTL bound on both 0.0.0.0:443 and [::]:443.
Automatic certificate renewal
Add this to your root crontab (sudo crontab -e). It handles stopping FTL, running Certbot, rebuilding the combined PEM, and restarting FTL:
# Renew Let's Encrypt cert (Certbot best practice is twice daily)
# Certbot only acts when expiry is within 30 days
0 2,14 * * * certbot renew --pre-hook "systemctl stop pihole-FTL" --post-hook "cat /etc/letsencrypt/live/pihole.yourdomain.com/fullchain.pem /etc/letsencrypt/live/pihole.yourdomain.com/privkey.pem > /etc/pihole/tls-combined.pem && systemctl start pihole-FTL" --quiet
Note there are other ways to do verification you can explore without having to keep port forwarding to port 80 open on your router.
I just took the path of least resistance here as the server for Lets Encrypt wil only temporarily spin up and i’m not doing anything else with the port.
Verify the certificate
sudo certbot certificates
Then navigate to https://pihole.yourdomain.com/admin — you should have a clean padlock and a fully working admin interface accessible from anywhere.
A Note on Security
Exposing your Pi-hole admin to the public internet means anyone can reach the login page. Make sure your Pi-hole admin password is strong. If you want to go further, your router may support IP allowlisting on the port forwarding rules — restricting access to known IP ranges (your home, your partner’s mobile carrier range) adds another layer without much overhead.
Summary
| What | How |
|---|---|
| DNS blocking | Pi-hole on a static LAN IP, set as router DHCP DNS |
| Reliability | Ethernet connection, NVMe OS drive |
| Automation | Cron for updates, gravity refresh, system hygiene, log rotation |
| HTTPS | Let’s Encrypt cert via Certbot, combined PEM for Pi-hole v6 |
| External access | Router port forwarding 80+443, subdomain on owned domain |
| Renewal | Certbot cron with pre/post hooks to manage FTL and rebuild PEM |
The whole setup runs unattended once it’s in place. The only manual touchpoint is if Let’s Encrypt changes something fundamental — which is rare.