Setting Up Pi-hole at home: DNS Ad Blocking, automation and secure global management.

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 PATH and 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>&1 at 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.net as 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 PortInternal IPInternal PortProtocol
80192.168.1.5680TCP
443192.168.1.56443TCP

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 tee instead of a redirect? The sudo in sudo cat ... > file only elevates cat — the shell redirect runs as your user, which doesn’t have write permission to /etc/pihole. Piping through sudo tee solves 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

WhatHow
DNS blockingPi-hole on a static LAN IP, set as router DHCP DNS
ReliabilityEthernet connection, NVMe OS drive
AutomationCron for updates, gravity refresh, system hygiene, log rotation
HTTPSLet’s Encrypt cert via Certbot, combined PEM for Pi-hole v6
External accessRouter port forwarding 80+443, subdomain on owned domain
RenewalCertbot 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.