Reading time: ~12 minutes Audience: Homelabbers wanting network-wide ad and tracker blocking


What Is Pi-hole?

Overview

Pi-hole is a DNS sinkhole that blocks ads, trackers, and malicious domains at the network level. By acting as your LAN’s DNS server, it intercepts queries to known ad domains and returns 0.0.0.0 — blocking ads on every device without installing browser extensions or apps.

Key Benefits

Benefit Detail
Network-wide One setup covers phones, TVs, IoT, and guests
Device-agnostic Works on devices that can’t run ad blockers (smart TVs, consoles)
Low resource Runs on a Raspberry Pi or in a Docker container
Privacy Blocks trackers from Google, Facebook, Amazon, and analytics
Malware blocking Filter lists include known malicious domains
Custom filtering Add your own blacklists and whitelists
Statistics See exactly which domains are queried and blocked

Pi-hole v6 (2025) changes: - Built-in web server (no Lighttpd dependency) - FTL v6 DNS engine (replaces dnsmasq) - TOML configuration format - Improved REST API


Prerequisites

Hardware Requirements

  • Any device running Docker (mini PC, Raspberry Pi, NAS, or VM)
  • 512MB RAM minimum (1GB recommended)
  • 2GB storage for logs and databases

Software Requirements

  • Docker Engine 24.x+ and Docker Compose v2+
  • Linux host (Debian 12, Ubuntu 22.04/24.04, or Proxmox LXC)
  • Router access to change DNS settings

Knowledge Prerequisites

  • Basic Docker and Linux commands
  • Router admin panel access
  • Understanding of DNS basics

Step 1: Create the Docker Compose File

Objective

Set up a reproducible Pi-hole deployment with persistent storage and proper network configuration.

Step-by-Step Instructions

  1. Create a directory for Pi-hole:
mkdir -p ~/docker/pihole && cd ~/docker/pihole
  1. Create docker-compose.yml:
services:
  pihole:
    container_name: pihole
    image: pihole/pihole:latest
    ports:
      - "53:53/tcp"
      - "53:53/udp"
      - "8080:80/tcp"
    environment:
      - TZ=Asia/Singapore
      - WEBPASSWORD=ChangeMeStrong123!
      - FTLCONF_LOCAL_IPV4=192.168.1.10
      - PIHOLE_DNS_=1.1.1.1;1.0.0.1
      - FTLCONF_BLOCK_ICLOUD_PR=false
    volumes:
      - ./etc-pihole:/etc/pihole
      - ./etc-dnsmasq.d:/etc/dnsmasq.d
    cap_add:
      - NET_ADMIN
    restart: unless-stopped
    networks:
      - pihole-net

networks:
  pihole-net:
    driver: bridge
  1. Create data directories:
mkdir -p etc-pihole etc-dnsmasq.d
  1. Deploy:
docker compose up -d
  1. Verify it’s running:
docker logs pihole | grep -i "finished"

Step 2: Configure Your Router to Use Pi-hole

Objective

Point all devices on your network to Pi-hole for DNS resolution.

Step-by-Step Instructions

Option A: Router DHCP DNS (Recommended) 1. Log into your router admin panel (usually 192.168.1.1 or 192.168.0.1) 2. Find DHCP Server or LAN Settings 3. Set Primary DNS to your Pi-hole IP (e.g., 192.168.1.10) 4. Set Secondary DNS to a backup (e.g., 1.1.1.1) or leave blank 5. Save and reboot the router

Option B: Pi-hole as DHCP Server If your router doesn’t allow custom DNS, disable its DHCP and let Pi-hole handle it: 1. In Pi-hole admin (Settings → DHCP), enable DHCP server 2. Set range: 192.168.1.100 to 192.168.1.200 3. Set router (gateway) to 192.168.1.1 4. Disable DHCP on your router 5. Renew leases on all devices (reconnect Wi-Fi or reboot)

Option C: Manual Per-Device On each device, set DNS to Pi-hole’s IP. Good for testing before network-wide rollout.


Step 3: Add Blocklists and Whitelists

Objective

Tune blocking to balance ad blocking with website functionality.

Step-by-Step Instructions

  1. Log into Pi-hole admin at http://192.168.1.10:8080/admin
  2. Default blocklists (enabled automatically):
  3. StevenBlack’s unified hosts list
  4. AdAway default blocklist

  5. Add recommended blocklists (Group Management → Adlists):

https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts
https://adaway.org/hosts.txt
https://someonewhocares.org/hosts/zero/hosts
https://raw.githubusercontent.com/Spam404/lists/master/main-blacklist.txt
https://raw.githubusercontent.com/ShadowWhisperer/BlockLists/master/Lists/Tracking
  1. Update gravity:
docker exec pihole pihole -g
  1. Common whitelist entries (to avoid breaking sites):
  2. clients4.google.com (Google Play/Android)
  3. app-api.twitch.tv (Twitch login)
  4. www.msftncsi.com (Windows connectivity check)

Step 4: Enable DNS-over-HTTPS (Optional but Recommended)

Objective

Encrypt DNS queries from Pi-hole to upstream resolvers for privacy.

Step-by-Step Instructions

Use Cloudflare’s cloudflared container as the upstream:

services:
  cloudflared:
    container_name: cloudflared
    image: cloudflare/cloudflared:latest
    command: proxy-dns
    environment:
      - TUNNEL_DNS_UPSTREAM=https://1.1.1.1/dns-query,https://1.0.0.1/dns-query
      - TUNNEL_DNS_PORT=5053
      - TUNNEL_DNS_ADDRESS=0.0.0.0
    restart: unless-stopped
    networks:
      - pihole-net

  pihole:
    container_name: pihole
    image: pihole/pihole:latest
    ports:
      - "53:53/tcp"
      - "53:53/udp"
      - "8080:80/tcp"
    environment:
      - TZ=Asia/Singapore
      - WEBPASSWORD=ChangeMeStrong123!
      - PIHOLE_DNS_=cloudflared#5053
    volumes:
      - ./etc-pihole:/etc/pihole
      - ./etc-dnsmasq.d:/etc/dnsmasq.d
    cap_add:
      - NET_ADMIN
    restart: unless-stopped
    networks:
      - pihole-net
    depends_on:
      - cloudflared

networks:
  pihole-net:
    driver: bridge

Pro Tips

Tip 1: Use a MAC Address Reservation

Assign Pi-hole a static IP in your router’s DHCP reservations. If the IP changes, your entire network loses DNS.

Tip 2: Keep a Backup DNS

Set a secondary DNS (e.g., 1.1.1.1) on your router. If Pi-hole goes down, devices can still resolve. The trade-off is that some ads slip through during the outage.

Tip 3: Schedule Gravity Updates

# Add to crontab (docker host)
0 3 * * 0 cd ~/docker/pihole && docker exec pihole pihole -g

Tip 4: Monitor Block Percentage

A healthy Pi-hole blocks 10–30% of queries. If it’s under 5%, your blocklists are too conservative. If it’s over 50%, you’re probably breaking websites.


Troubleshooting Common Issues

Problem 1: “DNS Resolution Not Working”

Cause: Router not pointing to Pi-hole, or Pi-hole container not running.

Fix:

# Check if Pi-hole is running
docker ps | grep pihole

# Test DNS directly
nslookup google.com 192.168.1.10

# Check Pi-hole logs
docker logs pihole

Problem 2: “Some Websites Won’t Load”

Cause: Overly aggressive blocklist.

Fix: 1. Check Pi-hole Query Log for blocked domains 2. Whitelist the domain blocking the site 3. Use the pihole -t command to tail logs in real time

Problem 3: “Container Won’t Start — Port 53 in Use”

Cause: systemd-resolved or another DNS service is using port 53.

Fix:

# On Debian/Ubuntu, disable systemd-resolved
systemctl stop systemd-resolved
systemctl disable systemd-resolved
rm /etc/resolv.conf
ln -s /run/systemd/resolve/resolv.conf /etc/resolv.conf

Conclusion

Summary

Pi-hole in Docker Compose is the easiest way to deploy network-wide ad blocking. With a single docker-compose.yml file, you can block ads on every device in your home, improve privacy, and reduce bandwidth usage.

Next Steps

  1. Add Unbound for recursive DNS without third-party resolvers
  2. Set up AdGuard Home as a backup DNS
  3. Monitor your network with Grafana and Prometheus

Affiliate Opportunities

  • Raspberry Pi 5: Alternative hardware for Pi-hole
  • Beelink Mini S12 Pro: Mini PC for running Pi-hole + other services
  • TP-Link Omada router: Router with good DNS/DHCP control

Internal Linking Strategy

  • intropihole-vs-adguard-home for DNS blocker comparison
  • step-4pihole-unbound-dns for recursive DNS setup
  • conclusiondocker-compose-for-beginners for Docker basics

CTA

  • [comment] What’s your Pi-hole block percentage? Share your stats and favorite blocklists!
  • [newsletter] Subscribe for weekly homelab Docker tutorials and privacy guides.