Pi-hole is a DNS server that blocks ads, trackers and malware domains for every device that uses it. It’s usually run on a Raspberry Pi at home — but on a VPS it protects your devices everywhere, on mobile data and public Wi-Fi, when combined with a VPN.
The one rule: never run an open resolver
A DNS server on a public IP that answers anyone’s queries is an open resolver. Attackers abuse open resolvers for DNS amplification DDoS attacks, your provider will receive abuse reports, and the server may be suspended. Port 53 must not be reachable from the internet.
The safe design:
Phone / laptop ──WireGuard or Tailscale──► VPS ──► Pi-hole (listening on the VPN interface only)
Your devices reach Pi-hole through the encrypted tunnel; the public internet can’t reach it at all.
1. Set up the VPN first
Use WireGuard (server at 10.8.0.1) or Tailscale. The examples below assume WireGuard’s 10.8.0.1.
2. Free port 53 on the host
On Ubuntu, systemd-resolved’s stub listener occupies 127.0.0.53:53. Pi-hole binding to the VPN IP doesn’t conflict with it, but if you ever bind to 0.0.0.0, disable the stub:
sudo sed -i 's/^#\?DNSStubListener=.*/DNSStubListener=no/' /etc/systemd/resolved.conf
sudo systemctl restart systemd-resolved
(Then make sure the host itself still has working DNS — /etc/resolv.conf should point to a real resolver.)
3. Run Pi-hole with Docker
mkdir -p ~/pihole && cd ~/pihole
compose.yaml:
services:
pihole:
image: pihole/pihole:latest
container_name: pihole
restart: unless-stopped
ports:
- "10.8.0.1:53:53/tcp"
- "10.8.0.1:53:53/udp"
- "127.0.0.1:8081:80/tcp"
environment:
TZ: "Asia/Hong_Kong"
FTLCONF_webserver_api_password: ${PIHOLE_PASSWORD}
FTLCONF_dns_listeningMode: "all"
FTLCONF_dns_upstreams: "1.1.1.1;9.9.9.9"
volumes:
- ./etc-pihole:/etc/pihole
echo "PIHOLE_PASSWORD=$(openssl rand -base64 18)" > .env
docker compose up -d
Key points:
- DNS ports are published only on the VPN address (
10.8.0.1), never0.0.0.0. - The admin interface is on localhost; reach it with
ssh -L 8081:127.0.0.1:8081 user@vpsand openhttp://localhost:8081/admin. - The environment variable names above follow Pi-hole v6 (
FTLCONF_…). Older v5 images usedWEBPASSWORDandPIHOLE_DNS_— check the image documentation for the version you run. - With
listeningMode: all, Pi-hole answers whatever reaches it — safe here only because Docker publishes the port solely on the VPN IP.
The WireGuard interface must be up before Docker starts the container, otherwise binding to 10.8.0.1 fails. Make Docker start after it, or restart the container after boot.
4. Verify it’s not public
From a machine outside your VPN:
dig @YOUR_VPS_PUBLIC_IP example.com
This must time out. Also check sudo ss -ulnp | grep ':53' — the listener should be on 10.8.0.1, not 0.0.0.0.
5. Point your devices to it
In the WireGuard client config:
[Interface]
DNS = 10.8.0.1
With a split tunnel (AllowedIPs = 10.8.0.0/24), only DNS queries go through the VPN — normal traffic still uses your local connection, so browsing speed isn’t affected. With Tailscale, set the VPS’s Tailscale IP as a global nameserver in the admin console’s DNS settings.
Test on the device: open the Pi-hole dashboard’s Query Log and browse — queries should appear, and ad domains should show as blocked.
6. Blocklists
Pi-hole ships with a default list. Add more under Lists (v6) or Adlists (v5), then update gravity. Be selective — huge combined lists cause more false positives. When something breaks (a shopping link, a login page), check the Query Log for blocked domains and allow (whitelist) them.
7. Encrypted upstream DNS (optional)
Pi-hole forwards to upstream resolvers in plain DNS by default. For privacy from the VPS onward, run Unbound as a local recursive resolver and point Pi-hole at it, or use a DNS-over-HTTPS proxy like cloudflared.
Pi-hole vs AdGuard Home
AdGuard Home offers similar blocking plus built-in DNS-over-HTTPS/TLS serving and a more modern UI — see AdGuard Home on a VPS. If you want encrypted DNS directly to your phone without a VPN, AdGuard Home’s DoH/DoT support is the better fit (with access controls).
Updating
docker compose pull && docker compose up -d
Pi-hole is light — the smallest VPS plan is plenty. Pick a location near you for low DNS latency: Hong Kong or Tokyo for Asia, USA for the Americas.