← All Guides
beginner

Pi-hole DNS Ad Blocking: Complete Setup Guide

Set up Pi-hole on Docker to block ads and tracking at the DNS level across your entire network — no browser extensions, no per-device setup.

Budget Homelab ·
networkingdockerprivacy

Pi-hole is a DNS sinkhole. Instead of every device on your network reaching out to ad servers and trackers, they ask Pi-hole first. Pi-hole checks the domain against a blocklist and either returns the real IP or returns nothing. Ads and trackers get nothing. It works for every device on your network — phones, TVs, smart home devices, everything — without installing anything on each device.

I’ve been running it for years. The web dashboard shows you exactly what’s being blocked across your entire household. It’s one of those installs you forget about after setup because it just works.

What Pi-hole Does and Doesn’t Do

Pi-hole blocks at the DNS level. If you request ads.example.com and that domain is on a blocklist, Pi-hole intercepts it and returns nothing. The ad never loads.

What it doesn’t block: ads served from the same domain as the content (YouTube’s ad serving is the main example — it comes from googlevideo.com which also serves the actual videos, so you can’t block one without breaking the other). For those, you still need browser-level blocking.

For everything else — network-level tracking, smart TV data harvesting, IoT phone-home traffic — Pi-hole handles it.

Running Pi-hole on Docker

Create the directory:

mkdir -p /opt/pihole

Create /opt/pihole/docker-compose.yml:

services:
  pihole:
    image: pihole/pihole:latest
    container_name: pihole
    network_mode: host
    environment:
      TZ: "America/Chicago"
      WEBPASSWORD: "changeme"
      PIHOLE_DNS_: "1.1.1.1;1.0.0.1"
      DNSMASQ_LISTENING: "all"
    volumes:
      - /opt/pihole/etc-pihole:/etc/pihole
      - /opt/pihole/etc-dnsmasq.d:/etc/dnsmasq.d
    restart: unless-stopped
    cap_add:
      - NET_ADMIN

network_mode: host — Pi-hole needs to listen on port 53 (DNS) on your actual host IP. Host networking is the cleanest way to accomplish this without complex port mapping.

TZ — Set your timezone. This affects the timestamps in your query log.

WEBPASSWORD — The password for the Pi-hole web admin. Change this to something real before starting.

PIHOLE_DNS_ — The upstream DNS servers Pi-hole uses for non-blocked domains. Cloudflare (1.1.1.1) and its backup (1.0.0.1) are what I use. You can use 8.8.8.8/8.8.4.4 for Google or 9.9.9.9 for Quad9.

cap_add: NET_ADMIN — Required for Pi-hole to manage its DHCP and DNS listener properly.

Start it:

cd /opt/pihole
docker compose up -d

Access the admin interface at http://YOUR_SERVER_IP/admin. Log in with the password you set in WEBPASSWORD.

Pointing Your Network at Pi-hole

Pi-hole only works if your devices actually use it for DNS. Two ways to do this:

Log into your router and find the DHCP settings. Look for “DNS Server” or “Primary DNS.” Change it to your server’s IP address. Every device that gets an IP from your router via DHCP will automatically use Pi-hole for DNS. No per-device configuration needed.

The exact menu path varies by router manufacturer:

Set the secondary DNS to a real DNS server (like 1.1.1.1) as a fallback for when Pi-hole is down. This is a tradeoff — the fallback means Pi-hole goes down silently without ads coming back, but it bypasses ad blocking when Pi-hole isn’t running.

Option 2: Per-device

Set the DNS server on each device manually to your server IP. More work but useful if you only want Pi-hole on specific devices, or if your router doesn’t let you customize DHCP DNS.

On most devices:

Adding Blocklists

Pi-hole comes with a default blocklist that covers the basics. To add more lists, go to Adlists in the admin panel.

Some lists worth adding:

After adding lists, go to Tools > Update Gravity to download them. The number of blocked domains jumps significantly after adding a few good lists.

Don’t add too many lists. More lists means more false positives (legitimate sites getting blocked) and slower gravity updates. Three or four good lists are better than fifteen mediocre ones.

Handling False Positives (Whitelisting)

Something breaks and you suspect Pi-hole. Check Query Log in the admin panel. Find the domain that’s being blocked (it shows in red with the blocklist that caught it). Click the domain and add it to the whitelist.

Common false positives:

The whitelist is per-domain — you’re allowing just that specific domain, not disabling blocking entirely.

Local DNS Records

One underused Pi-hole feature: Local DNS Records under Settings. You can assign domain names to local IPs, so instead of bookmarking 192.168.1.100:3001, you visit uptime.home.arpa or whatever you want to call it.

This is essentially a lightweight local DNS resolver for your homelab. It’s not as flexible as a full local DNS setup (see the Technitium DNS guide if you want more control), but for basic name-to-IP mapping it’s convenient.

Monitoring Your Blocked Queries

The dashboard shows:

I check mine occasionally out of curiosity. A smart TV that sends thousands of telemetry queries per day shows up clearly. That visibility alone is worth the install.

Backups

Back up /opt/pihole/etc-pihole. That directory contains your blocklists, whitelist, local DNS records, and settings. The etc-dnsmasq.d directory is worth backing up too if you’ve added custom dnsmasq config.

tar -czf /backup/pihole-$(date +%Y%m%d).tar.gz /opt/pihole/

Updating

cd /opt/pihole
docker compose pull
docker compose up -d

Pi-hole releases are infrequent but meaningful. The gravity database (blocklists) updates automatically on a weekly schedule by default — you can also update it manually from the admin panel under Tools > Update Gravity.