👍 What we like
- ✓Blocks ads and trackers at the network level for all devices
- ✓Extremely lightweight, requiring only 512 MB of RAM
- ✓Simple deployment via Docker Compose
- ✓Provides visibility into device network requests
👎 What to watch
- ✕Requires a static IP address to function correctly
- ✕Needs manual configuration to free up port 53 on Ubuntu
- ✕Requires router access to change network DNS settings
📑 Contents ▾
- 01 Prerequisites
- 02 Step 1: Install Docker and Docker Compose
- 03 Step 2: Set the machine’s IP and free up port 53
- 04 Step 3: Pi-hole’s docker-compose.yml
- 05 Step 4: Start Pi-hole
- 06 Step 5: Test DNS resolution
- 07 Step 6: Make the entire network use Pi-hole
- 08 Step 7 (optional): Pi-hole as DHCP server
- 09 Step 8: Add blocklists and exceptions
- 10 Step 9: Secure the interface and backup
- 11 Step 10: Final verification
- 12 Common pitfalls and troubleshooting
- 13 FAQ
- · Does Pi-hole block ads in apps and on TVs?
- · Do I need HTTPS for Pi-hole?
- · What happens if the Pi-hole machine goes down?
- · Does Pi-hole slow down my connection?
- · Should I enable Pi-hole’s DHCP?
- · Pi-hole or AdGuard Home: which one to choose?
- 20 🛒 Recommended hardware
- 21 Related topics
Tired of ads and trackers on all your devices — including your smart TV and smartphone where no blocker can be installed? Pi-hole solves the problem at the source. It is a DNS server that blocks advertising and tracking domains at the network level: once your devices use it as their DNS, every request to an ad domain is intercepted and dropped. Result: fewer ads, fewer trackers, faster page loads, and total visibility into what your devices are contacting in the background.
Pi-hole is one of the most cost-effective self-hosted services in terms of effort vs. benefit: lightweight, simple, and useful for the entire household. In this tutorial, we deploy it via Docker Compose, configure the network so all your devices benefit from it, and cover the optional DHCP server feature. A key characteristic of DNS: unlike a web service, Pi-hole does not need public HTTPS, but its network configuration requires some precautions that we detail below.
Prerequisites
- A machine that stays on permanently on your local network: Raspberry Pi, mini-PC, NAS, or small server. Pi-hole fits in 512 MB of RAM. Many host it at home, but you can also run it on a VPS from Scaleway or OVHcloud coupled with a VPN to block ads while on the go.
- A static IP address for this machine on your local network. This is essential: if the address changes, your devices lose their DNS and consequently lose Internet access.
- Docker and Docker Compose installed (command in Step 1).
- Access to your router/box administration to change the DNS server distributed to the network.
Step 1: Install Docker and Docker Compose
On Ubuntu/Debian:
sudo apt update
sudo apt install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo usermod -aG docker $USER
Log out and log back in, then verify:
docker --version && docker compose version
Step 2: Set the machine’s IP and free up port 53
Pi-hole listens on port 53 (DNS). However, on Ubuntu, this port is often occupied by systemd-resolved, the local DNS resolver. You need to free it up.
First, check what is listening on port 53:
sudo ss -tulpn | grep :53
If systemd-resolved appears, disable its listening on port 53 without breaking the machine’s DNS resolution:
sudo mkdir -p /etc/systemd/resolved.conf.d
sudo nano /etc/systemd/resolved.conf.d/pihole.conf
[Resolve]
DNSStubListener=no
Point /etc/resolv.conf to a manually managed file so the machine itself retains DNS resolution:
sudo rm -f /etc/resolv.conf
echo "nameserver 1.1.1.1" | sudo tee /etc/resolv.conf
sudo systemctl restart systemd-resolved
Verify that port 53 is now free:
sudo ss -tulpn | grep :53
Also ensure the machine has a static IP (configured via Netplan or via a DHCP reservation on your router). We will use 192.168.1.10 as an example.
Step 3: Pi-hole’s docker-compose.yml
mkdir -p ~/pihole && cd ~/pihole
nano docker-compose.yml
services:
pihole:
image: pihole/pihole:latest
container_name: pihole
restart: unless-stopped
# Port 53 must be bound to the host IP for the local network
ports:
- "53:53/tcp"
- "53:53/udp"
# Web administration interface (HTTP locally)
- "8080:80/tcp"
environment:
TZ: "Europe/Paris"
# Administration interface password (change this!)
FTLCONF_webserver_api_password: "replace_with_a_strong_password"
# Upstream DNS (resolvers to which Pi-hole forwards legitimate requests)
FTLCONF_dns_upstreams: "1.1.1.1;9.9.9.9"
# IP of this machine on the local network
FTLCONF_dns_listeningMode: "all"
volumes:
- ./etc-pihole:/etc/pihole
# Allows Pi-hole to manage DHCP if you enable it (Step 7)
cap_add:
- NET_ADMIN
dns:
- 127.0.0.1
- 1.1.1.1
A few explanations. Port 8080 exposes the web interface (we avoid port 80 to prevent conflicts with a potential reverse proxy). FTLCONF_dns_upstreams defines the resolvers to which Pi-hole sends non-blocked requests (here Cloudflare and Quad9). cap_add: NET_ADMIN is only necessary if you enable the integrated DHCP.
Step 4: Start Pi-hole
cd ~/pihole && docker compose up -d
docker compose logs -f pihole
When the logs indicate that the FTL service is ready, open the administration interface: http://192.168.1.10:8080/admin. Log in with the password defined in FTLCONF_webserver_api_password.
Step 5: Test DNS resolution
Before switching the entire network, verify that Pi-hole responds and blocks correctly. From another machine on the network:
# Should return a normal response (legitimate domain)
dig @192.168.1.10 selfhostr.fr +short
# Should return 0.0.0.0 or an empty response (blocked ad domain)
dig @192.168.1.10 doubleclick.net +short
If the first returns an IP and the second 0.0.0.0 (or nothing), Pi-hole is working.
Step 6: Make the entire network use Pi-hole
This is the step that generalizes blocking. Two approaches.
Recommended method: on the router/box. Log in to your router’s administration and change the DNS server distributed via DHCP to point to Pi-hole’s IP (192.168.1.10). All devices obtaining their network configuration from the router will then use Pi-hole automatically. This is the cleanest solution.
Many consumer-grade ISP routers unfortunately do not allow changing the distributed DNS. In this case:
Per-device method. Manually enter 192.168.1.10 as the DNS in the network settings of each device (computer, phone, TV).
Tip: Set only Pi-hole’s IP as the DNS, without a secondary “backup” DNS pointing to the router or a public DNS — otherwise, devices will bypass Pi-hole as soon as it is momentarily unavailable, and blocking will be inconsistent.
Step 7 (optional): Pi-hole as DHCP server
If your router does not allow changing the distributed DNS, the radical solution is to disable the router’s DHCP and let Pi-hole manage it. It will then distribute IP addresses and impose itself as the DNS.
- In your router’s administration, disable the DHCP server.
- In Pi-hole, go to Settings → DHCP, enable the DHCP server, and define an IP range (e.g.,
192.168.1.50to192.168.1.200) consistent with your network, as well as the gateway (your router’s IP).
The cap_add: NET_ADMIN in the compose file allows the container to provide DHCP. Warning: never have two active DHCP servers on the same network simultaneously, as this causes address conflicts.
Step 8: Add blocklists and exceptions
Pi-hole comes with a default blocklist, but you can add others in Lists. After any addition, run the gravity update:
docker exec pihole pihole -g
Think about allowlists: some legitimate sites use domains that are sometimes blocked (payments, useful telemetry). If a service breaks, identify the offending domain in the “Query Log” tab and add it as an exception rather than disabling Pi-hole.
Step 9: Secure the interface and backup
Pi-hole’s administration interface is local HTTP. Never expose it directly to the Internet. If you want to access it remotely, go through a VPN (WireGuard) or an HTTPS reverse proxy with authentication — never in cleartext.
All configuration lives in ~/pihole/etc-pihole. Backup this folder:
nano ~/pihole/backup.sh
#!/bin/bash
set -euo pipefail
BACKUP_DIR="$HOME/pihole/backups"
STAMP=$(date +%Y%m%d-%H%M%S)
mkdir -p "$BACKUP_DIR"
# Native configuration export (Teleporter)
docker exec pihole pihole-FTL --teleporter > "$BACKUP_DIR/pihole-teleporter-$STAMP.zip"
# Keep 14 days
find "$BACKUP_DIR" -name 'pihole-teleporter-*.zip' -mtime +14 -delete
chmod +x ~/pihole/backup.sh
(crontab -l 2>/dev/null; echo "0 3 * * * $HOME/pihole/backup.sh") | crontab -
To replicate these backups off-site, see automatic backup with Restic and Backblaze.
Step 10: Final verification
# Container is running
docker ps --filter "name=pihole"
# Pi-hole is listening on port 53
sudo ss -tulpn | grep :53
# Resolution works from the network
dig @192.168.1.10 selfhostr.fr +short
In the web dashboard, after a few hours of use, you will see the percentage of blocked requests: this is proof that Pi-hole is actively filtering traffic from your entire network.
Common pitfalls and troubleshooting
- “Port 53 already in use” at startup.
systemd-resolvedis still occupying the port. Go back to Step 2 and verifyDNSStubListener=no. - No Internet on the network. Pi-hole went down and was the only DNS. Restart the container; to prevent this, harden the machine (
restart: unless-stopped) or prepare a second instance. - Ads not blocked on some devices. Many devices (Android, smart TV, IoT) enforce their own DNS “hardcoded” (often Google’s). Block unauthorized outbound DNS on your router (forced redirection to Pi-hole) or block known DNS-over-HTTPS.
- DHCP conflicts. Two active DHCP servers. Disable the router’s if you enable Pi-hole’s.
FAQ
Does Pi-hole block ads in apps and on TVs?
Yes, for everything that passes through DNS requests to known advertising/tracking domains. This is its major advantage over a browser extension: it protects smart TVs, consoles, and phones where no blocker can be installed. However, it does not block ads served from the same domain as the content (like YouTube), as cutting that domain would cut the service itself.
Do I need HTTPS for Pi-hole?
No, not for its DNS role, which is purely local. The administration interface is HTTP on the local network and should never be exposed publicly. If you want to access it from outside, do so via a VPN (WireGuard) rather than by opening a port.
What happens if the Pi-hole machine goes down?
If Pi-hole is the only DNS on the network, no resolution works: the Internet appears down. To harden this, configure restart: unless-stopped (already done), host Pi-hole on a stable machine, or deploy a second instance as a secondary DNS. However, avoid putting a public DNS as a secondary, as devices would then bypass blocking.
Does Pi-hole slow down my connection?
On the contrary, it often speeds it up. By blocking requests to ad and tracking servers, pages load fewer unnecessary resources. DNS resolution itself is cached by Pi-hole, which can even make it faster for previously visited domains.
Should I enable Pi-hole’s DHCP?
Only if your router does not allow changing the distributed DNS to the network. In that case, disabling the router’s DHCP and entrusting it to Pi-hole is the most reliable way to impose Pi-hole on all devices. If your router allows changing the DNS, keep its DHCP and simply change the DNS: it is simpler.
Pi-hole or AdGuard Home: which one to choose?
Both block ads at the DNS level and are excellent choices. AdGuard Home natively integrates DNS encryption (DoH/DoT) and has a slightly more modern interface; Pi-hole benefits from a huge community and countless tutorials. Our comparison Pi-hole vs AdGuard Home helps you decide.
🛒 Recommended hardware
For this project, here is the hardware we recommend (Amazon links):
As an Amazon Associate, Selfhostr earns from qualifying purchases — at no extra cost to you.
Related topics
- Pi-hole vs AdGuard Home: which DNS blocker in 2026
- Self-host WireGuard on a VPS
- Install and secure an Ubuntu VPS from A to Z
- Automatic backup with Restic and Backblaze
You now have an ad and tracker blocker that protects your entire network, including TVs and phones, without installing anything on each device. It is one of the most rewarding self-hosted services. To follow Pi-hole updates, up-to-date blocklists, and best practices for self-hosting, subscribe to our Telegram monitoring bot.