How to Install Pi-hole on a Linux Server (Block Ads for Every Device at Home)

Ad blockers work per browser. Pi-hole works per house: it becomes the DNS server for your network, and when any device asks "where is ads.tracker.com?", Pi-hole answers "nowhere". Smart TVs, phone apps, games consoles — all cleaner, with no software installed on any of them. It also gives your server a proper name on the network.
Quick reference
| Task | What |
|---|---|
| Install | Docker Compose file below |
| Admin page | http://server-ip/admin |
| Make the whole network use it | Router → DHCP → DNS server = your server's IP |
| Give your server a name | Local DNS → DNS Records |
| A site stopped working | Query Log → Whitelist |
| Update block lists | Tools → Update Gravity |
Before you start
Pi-hole needs port 53. Ubuntu runs its own small DNS resolver on that port, so switch it off:
sudo sed -i 's/#DNSStubListener=yes/DNSStubListener=no/' /etc/systemd/resolved.conf
sudo systemctl restart systemd-resolved
sudo rm /etc/resolv.conf && echo "nameserver 1.1.1.1" | sudo tee /etc/resolv.conf
1. The compose file
mkdir -p ~/docker/pihole && cd ~/docker/pihole
nano compose.yaml
services:
pihole:
image: pihole/pihole:latest
container_name: pihole
ports:
- "53:53/tcp"
- "53:53/udp"
- "80:80/tcp"
environment:
TZ: Europe/London
FTLCONF_webserver_api_password: change-this-password
FTLCONF_dns_listeningMode: all
volumes:
- ./etc-pihole:/etc/pihole
restart: unless-stopped
If Nginx is already on port 80, change the mapping to "8053:80/tcp" and put Pi-hole behind the reverse proxy later.
docker compose up -d
sudo ufw allow 53
sudo ufw allow 80/tcp
Open http://192.168.1.50/admin and log in with the password.
2. Test it on one device first
On your laptop, set the DNS server manually to 192.168.1.50 (Windows: adapter settings → IPv4 properties; Mac: Network → DNS). Browse a few sites. Then open the Pi-hole dashboard — you should see queries arriving and some marked blocked.
3. Switch the whole network
Log in to your router, find the DHCP settings, and set the DNS server to 192.168.1.50. Devices pick it up when they reconnect (toggle Wi-Fi off and on, or wait a few hours).
If your router won't let you change DNS (some ISP ones don't), turn the router's DHCP off and switch on Pi-hole's own under Settings → DHCP.
4. Name your server
Local DNS → DNS Records: add homeserver.lan → 192.168.1.50, cloud.home.example.com → 192.168.1.50, and so on. Now everything in the house can reach your apps by name, and your reverse proxy (tutorial 28) works inside the network without going out to the internet and back.
5. Block lists
The default list blocks most ads. For more, Lists lets you add others — the Hagezi or OISD lists are popular and well-maintained. After adding, Tools → Update Gravity.
Troubleshooting
"Address already in use" for port 53. The stub resolver is still running — repeat the "Before you start" commands and check with sudo ss -ulnp | grep :53.
A site or app stopped working. Open Query Log, find the red (blocked) entry, click Whitelist. Common ones: tracking links in emails, some banking apps, Spotify.
Devices still see ads. They're using a hard-coded DNS (Google's 8.8.8.8). Smart TVs and Chromecasts do this. Block outbound port 53 to anything but Pi-hole on the router, if it supports it.
Everything stops working when the server is off. That's the trade-off. Set the router's secondary DNS to 1.1.1.1 as a fallback (you'll get ads when Pi-hole's down, but the internet works).
Browser says "DNS over HTTPS" and bypasses Pi-hole. Turn off secure DNS in the browser's privacy settings, or let Pi-hole handle it — it blocks the canary domain so Firefox and Chrome fall back automatically.
Wrapping up
Free port 53, one compose file, point the router at the server. Every device in the house loses its ads, and your server finally has a name. Keep 1.1.1.1 as the router's second DNS so a reboot never takes the house offline.
