Pi-hole Setup: Block Ads on Your Whole Network

Pi-hole is a self-hosted DNS server that blocks ads and trackers for every device on your network — no software installed on each phone or laptop. Install it on a Raspberry Pi or in Docker with one command, set your router to hand out the Pi-hole's IP as the DNS server, and every device querying it stops resolving ad domains.

That's the whole idea. A Pi-hole setup swaps your router's DNS for a box you control, and that box refuses to answer for domains on its blocklists. The browser asks "where is ads.example.com?", Pi-hole says "nowhere" (returns 0.0.0.0), and the ad never loads. Because it works at the DNS layer, it covers smart TVs, phones, and IoT gadgets you can't install an ad blocker on.

It's not a replacement for uBlock Origin — DNS blocking can't strip an ad that's served from the same domain as the content (YouTube being the classic example). But for network-wide ad blocking of the thousands of third-party tracker and ad domains, it's the cleanest tool there is, and it's been my always-on DNS for years.

What you need before you start

Pi-hole is light. It runs happily on a Raspberry Pi Zero 2 W, an old Pi 3, a spare mini PC, a VM, or an LXC container. The official minimum is tiny — a few hundred MB of RAM and a couple hundred MB of disk. If you already have a home server or NAS running Docker, use that and skip the dedicated hardware.

The one hard requirement: the Pi-hole needs a static IP. Your whole network is about to depend on this machine for DNS, so its address can't change. Either set a static IP on the device itself or (easier) reserve its MAC address to a fixed IP in your router's DHCP settings. If the Pi-hole's IP moves and your router is still handing out the old one, every device loses name resolution and it looks like the internet is down.

How to do a Pi-hole setup on a Raspberry Pi or Linux box

On a fresh Debian, Ubuntu, or Raspberry Pi OS install, the official one-step installer is a single command:

curl -sSL https://install.pi-hole.net | bash

Piping a script straight into bash makes some people (reasonably) nervous. If you'd rather read the code first, clone it and run the installer yourself:

git clone --depth 1 https://github.com/pi-hole/pi-hole.git Pi-hole
cd "Pi-hole/automated install/"
sudo bash basic-install.sh

The installer walks you through a text menu: pick an upstream DNS provider (Cloudflare, Google, Quad9 — any is fine, you can change it later), choose your blocklists, and confirm the static IP. Let it finish. At the end it prints a randomly generated admin password. Write that down — it's shown once.

Since Pi-hole v6 (released February 2025), the whole thing got a rewrite. The web interface is now embedded directly in the pihole-FTL binary, so there's no more lighttpd or PHP. All settings live in one commented file at /etc/pihole/pihole.toml instead of the old setupVars.conf. With lighttpd gone, FTL binds ports 80 and 443 itself, falling back to 8080 if something else already owns port 80.

If you missed the password or want to change it:

pihole setpassword

Run it with no argument and it prompts you privately (nothing echoes to the screen). Older guides tell you to use pihole -a -p — that still works in v6 as a compatibility shim, but pihole setpassword is the current command, so use that. Now open the dashboard at http://pi.hole/admin or http://YOUR-PI-IP/admin and log in.

How to run Pi-hole in Docker (pihole docker)

If you'd rather not manage a bare-metal install, the official Docker image is the way I'd go — updates are a pull away and you can nuke and recreate the container without touching the host. Create a folder, drop in this compose.yaml, and edit the timezone and password:

services:
  pihole:
    container_name: pihole
    image: pihole/pihole:latest
    ports:
      - "53:53/tcp"
      - "53:53/udp"
      - "80:80/tcp"
      - "443:443/tcp"
      # Uncomment if using Pi-hole as your DHCP server:
      #- "67:67/udp"
    environment:
      TZ: 'America/New_York'
      FTLCONF_webserver_api_password: 'choose-a-strong-password'
      FTLCONF_dns_listeningMode: 'ALL'
    volumes:
      - './etc-pihole:/etc/pihole'
    cap_add:
      - NET_ADMIN
    restart: unless-stopped

Then bring it up:

docker compose up -d

A few things worth knowing here. The admin password is set with FTLCONF_webserver_api_password — the old v5 WEBPASSWORD variable does nothing in v6. If you leave it empty, Pi-hole generates a random one and prints it to the logs on first start (and only then), so docker compose logs pihole is where you'll find it. Don't want the password sitting in plaintext in your compose file? Use WEBPASSWORD_FILE with a Docker secret instead.

FTLCONF_dns_listeningMode: 'ALL' matters on Docker's default bridge network. Without it, FTL only accepts queries from the container's own subnet and ignores the rest of your LAN — a very common "why won't anything resolve?" mistake. The NET_ADMIN capability is only needed if you'll use Pi-hole as your DHCP server; otherwise you can drop it.

One quirk of the Docker config model: any setting you pass as an FTLCONF_ environment variable becomes read-only in the web interface. Pi-hole treats the env vars as the single source of truth, so you can't override them from the dashboard. If you want to tweak settings from the GUI, leave them out of the compose file. To reset the password on a running container:

docker exec -it pihole pihole setpassword

The port 53 conflict on Ubuntu and Fedora

This one bites almost everyone running Docker on Ubuntu 17.10+ or Fedora 33+. Those systems ship systemd-resolved, which already listens on port 53, so your Pi-hole container can't bind it. You'll see this on startup:

Error starting userland proxy: listen tcp4 0.0.0.0:53: bind: address already in use

Free the port by disabling the stub listener. Edit /etc/systemd/resolved.conf, find the DNSStubListener line, and set it to no:

DNSStubListener=no

Then fix the resolver symlink so the host itself can still resolve names, and restart the service:

sudo rm -f /etc/resolv.conf
sudo ln -s /run/systemd/resolve/resolv.conf /etc/resolv.conf
sudo systemctl restart systemd-resolved

Now port 53 is free and the container starts cleanly. Bare-metal installs handle this for you during setup, so this gotcha is Docker-specific.

How to point your whole network at Pi-hole

Installing Pi-hole does nothing until your devices actually use it for DNS. There are two ways, and one is much better than the other.

The right way — set it at the router. Log into your router, find the DHCP or LAN settings, and change the DNS server it advertises to your Pi-hole's static IP. Every device that renews its DHCP lease will start using Pi-hole automatically. This is the "set it once, covers everything" approach and it's what you want for real network-wide ad blocking. After changing it, reboot a device or reconnect its Wi-Fi to force a lease renewal.

The trap to avoid: do not put a public resolver like 8.8.8.8 in the router's secondary DNS slot as a "backup." Clients don't fail over in a strict primary-then-secondary order — they'll happily use whichever answers first, which means half your traffic quietly bypasses Pi-hole and you'll wonder why ads still show up. Either point only at the Pi-hole, or run a second Pi-hole as the secondary for real redundancy.

Some devices ignore your router's DNS entirely. Google/Android gear and plenty of smart TVs hardcode 8.8.8.8, and browsers with DNS-over-HTTPS (DoH) enabled tunnel their lookups past Pi-hole completely. To catch the hardcoded ones, add a firewall rule on your router that redirects all outbound port 53 traffic to the Pi-hole IP. For DoH, you'll need to block the known DoH endpoints — that's a deeper rabbit hole than most people need.

Pi-hole on bare metal vs Docker: which should you pick?

Aspect Bare metal (Pi / Linux) Docker
Install One-line installer script compose.yaml + docker compose up
Updates pihole -up Pull new image, recreate container
Port 53 conflicts Handled during setup Must free systemd-resolved manually
Config location /etc/pihole/pihole.toml Env vars + mounted volume
Best for A dedicated Pi with nothing else on it An existing server/NAS already running Docker

My rule of thumb: if the machine's only job is DNS, bare metal on a Raspberry Pi is dead simple and rock solid. If you already have a Docker host humming along, run it there and enjoy the throwaway containers.

How do I verify Pi-hole is actually blocking?

Don't trust the dashboard alone — test resolution directly. From another machine on the network, query a known ad domain against your Pi-hole and confirm it returns 0.0.0.0:

dig doubleclick.net @192.168.1.10

Replace the IP with your Pi-hole's. A blocked domain comes back as 0.0.0.0; a normal domain like github.com returns a real address. Then open the Query Log in the admin interface — if it's filling up with entries from your devices, they're using Pi-hole. If the log is empty, your clients aren't pointed at it yet, so recheck the router DNS setting and force a lease renewal.

Check your installed version any time with:

pihole -v

FAQ

Does Pi-hole block YouTube and Twitch ads?

Mostly no. Those ads are served from the same domains as the video, so DNS blocking can't separate them without breaking playback. Pi-hole shines against third-party trackers and display ads across the rest of the web. Pair it with a browser-level blocker for YouTube.

Will Pi-hole slow down my internet?

No — if anything it feels snappier, because Pi-hole caches DNS responses locally and blocked lookups return instantly. The real risk is availability: if it's your only DNS server and it goes down, the whole network loses name resolution. Run a second Pi-hole, or be ready to switch the router back to a public resolver.

Do I need a Raspberry Pi for a Pi-hole setup?

No. Any always-on Linux machine works — an old laptop, a mini PC, a VM, an LXC container, or a Docker host. The Pi is just cheap, silent, and low-power, which is why it's the popular choice.

Pi-hole vs AdGuard Home — which is better?

Both do network-wide DNS ad blocking well. AdGuard Home bundles DoH/DoT upstream support and parental controls in a single binary out of the box. Pi-hole has the larger community, a longer track record, and more third-party guides. Either is a solid pick; if you're unsure, Pi-hole has more answers online when you hit a wall.

Can I use my Pi-hole when I'm away from home?

Yes, but do it over a VPN back into your network (WireGuard or Tailscale), not by exposing the admin interface to the public internet. An open Pi-hole dashboard on the internet is an invitation for trouble.

Once it's running, the maintenance is close to zero: pihole -up for bare metal or a fresh image pull for Docker every so often, and an occasional glance at the query log when a site misbehaves so you can allowlist a false positive. Set it, point your router at it, and forget about it.

Official docs worth bookmarking: the Pi-hole installation guide and the Docker documentation.