Why Pi-hole?
Every device on your network — phones, smart TVs, IoT gadgets, even your fridge — is constantly phoning home to ad servers and trackers. You can’t install an ad blocker on a Roku. You can’t uBlock Origin your thermostat.
That’s where Pi-hole comes in. It’s a DNS sinkhole that sits between your devices and the internet, blocking ads and trackers at the network level before they ever reach your screen. One setup, every device protected.
And when you run it in Docker, it’s portable, easy to back up, and won’t leave a mess if you ever want to move it.
What You’ll Need
- A Linux machine, NAS, or Raspberry Pi running Docker and Docker Compose
- Basic comfort with the terminal
- Access to your router’s admin panel (to change DNS settings later)
- About 15 minutes
Step 1: Create the Project Directory
Let’s keep things tidy. Create a directory for your Pi-hole configuration so it persists across container restarts:
mkdir -p ~/docker/pihole
cd ~/docker/pihole
Step 2: Write the Docker Compose File
Create docker-compose.yml with the following content:
version: "3.8"
services:
pihole:
image: pihole/pihole:latest
container_name: pihole
restart: unless-stopped
ports:
- "53:53/tcp"
- "53:53/udp"
- "8080:80/tcp"
environment:
TZ: "America/New_York"
WEBPASSWORD: "your-secure-password-here"
DNSMASQ_LISTENING: "all"
volumes:
- ./etc-pihole:/etc/pihole
- ./etc-dnsmasq.d:/etc/dnsmasq.d
cap_add:
- NET_ADMIN
A few things to note here:
- Port 53 (TCP and UDP) is the DNS port. Pi-hole needs this to answer DNS queries from your devices.
- Port 8080 maps the web admin interface. We use 8080 instead of 80 to avoid conflicts with other web servers you might be running.
- WEBPASSWORD is the password you’ll use to log into the Pi-hole dashboard. Change this to something strong. Don’t leave the default.
- TZ sets your timezone. Adjust it to yours — you can find your timezone with
timedatectlor by checking/usr/share/zoneinfo/. - NET_ADMIN capability is needed for Pi-hole to manage its own network settings properly.
- The volumes ensure your blocklists, whitelist, and settings survive container recreation.
Step 3: Launch It
docker compose up -d
Check that it’s running:
docker compose logs -f pihole
You’ll see Pi-hole initialize, set up its blocklists, and start the DNS service. After about 30 seconds, you should see something like:
[✓] DNS service is running
[✓] Pi-hole blocking is enabled
Press Ctrl+C to stop following the logs.
Step 4: Test It Locally
Before pointing your whole network at Pi-hole, let’s verify it works from the host machine:
dig doubleclick.net @127.0.0.1
If Pi-hole is working, you’ll see the query resolved to 0.0.0.0 — that’s the sinkhole. The ad domain is blocked.
Now try a real domain:
dig example.com @127.0.0.1
This should resolve to the real IP address. Pi-hole only blocks what’s on its lists; everything else passes through normally.
Step 5: Point Your Router at Pi-hole
This is the step that makes Pi-hole protect your entire network. Log into your router’s admin panel (usually at 192.168.1.1 or 192.168.0.1) and find the DNS settings.
Set the primary DNS to the IP address of the machine running Pi-hole. For example, if your Docker host is at 192.168.1.50, that’s your DNS server.
Leave the secondary DNS blank, or set it to a public DNS like 1.1.1.1 as a fallback (though a fallback means some queries could bypass Pi-hole if it goes down).
Router interfaces vary wildly. Look under WAN settings, DHCP settings, or Network settings. If you can’t find it, search for your router model + “change DNS”.
After saving, your router will hand out Pi-hole’s IP as the DNS server to every device that connects via DHCP. Existing devices might need to reconnect or renew their DHCP lease:
# On a Linux/Mac client, you can renew with:
sudo dhclient -r && sudo dhclient
# Or just toggle Wi-Fi off and on.
Step 6: Explore the Dashboard
Open your browser and go to:
http://YOUR-SERVER-IP:8080/admin
Log in with the password you set in WEBPASSWORD. The dashboard is where Pi-hole really shines — you’ll see:
- Queries blocked today — the number is usually startling the first time
- Percentage blocked — typically 20-40% of all DNS queries are ads and trackers
- Top domains — see who’s trying to phone home the most
- Top blocked — the usual suspects (doubleclick, google-analytics, etc.)
- Query log — real-time view of every DNS request on your network
Step 7: Customize Your Blocklists
Pi-hole ships with a solid default blocklist, but you can add more. In the admin panel, go to Group Management → Adlists and add URLs like:
https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts https://mirror1.malwaredomains.com/files/justdomains https://s3.amazonaws.com/lists.disconnect.me/simple_malvertising.txtAfter adding lists, go to Tools → Update Gravity or run from the terminal:
docker exec pihole pihole -gThis downloads and compiles all your blocklists. More lists = more blocking, but also more memory usage and a higher chance of false positives. Start with the defaults and add gradually.
Common Pitfalls
"Port 53 is already in use"
Linux systems often run a local DNS resolver (systemd-resolved) that occupies port 53. You'll need to disable it:
sudo systemctl disable --now systemd-resolved sudo rm /etc/resolv.conf echo "nameserver 1.1.1.1" | sudo tee /etc/resolv.confOr, if you'd rather keep systemd-resolved, you can run Pi-hole on a non-standard port and forward to it — but that's more complex and less reliable.
"Some sites are broken"
Overly aggressive blocklists can break legitimate sites. If something isn't loading:
- Check the Pi-hole query log to see what was blocked
- Find the blocked domain
- Add it to the Whitelist (Admin panel → Whitelist)
Common culprits:
fonts.googleapis.com,www.googletagmanager.com, and various CDN domains."My devices aren't using Pi-hole"
Some devices (especially Android and IoT) use hardcoded DNS like Google's
8.8.8.8, bypassing your router's DHCP settings. To force all DNS through Pi-hole, add these iptables rules on your Docker host:sudo iptables -t nat -A PREROUTING -p udp --dport 53 -j DNAT --to-destination YOUR-PIHOLE-IP:53 sudo iptables -t nat -A PREROUTING -p tcp --dport 53 -j DNAT --to-destination YOUR-PIHOLE-IP:53This intercepts all DNS traffic on port 53 and redirects it to Pi-hole, regardless of what DNS server the device thinks it's using.
"I want to run a web server on port 80 too"
Change the Pi-hole web interface port in the Compose file. Replace
"8080:80"with"8081:80"(or any free port), then recreate the container:docker compose up -d --force-recreateBacking Up Your Configuration
All your Pi-hole settings, blocklists, and whitelist live in the two volumes we created:
~/docker/pihole/etc-pihole/ ~/docker/pihole/etc-dnsmasq.d/Back them up like any other data:
tar czf pihole-backup-$(date +%Y%m%d).tar.gz ~/docker/pihole/etc-pihole ~/docker/pihole/etc-dnsmasq.dTo restore on a new machine, extract these directories and run
docker compose up -d. Pi-hole will pick up right where it left off.Why This Matters
Running Pi-hole isn't just about blocking ads. It's about understanding that DNS is the nervous system of your network — every connection your devices make starts with a DNS query. By controlling DNS, you control visibility into what your network is doing.
You'll be surprised how much chatter your devices produce. Smart TVs pinging analytics servers every few seconds. Phones reporting app usage. IoT devices checking in with manufacturers. Pi-hole makes all of that visible, and gives you the power to say no.
It's one of the highest-impact, lowest-cost things you can do for your home network. And once you see that dashboard light up with thousands of blocked queries on day one, you'll never want to go back.
Happy blocking. 🛡️
Leave a Reply