Pi-hole is a DNS sinkhole — instead of blocking ads in the browser one tab at a time, it lies to every device on your network when they ask "what's the IP for ads.example.com?" The result: ad and tracker domains never resolve, the requests never go anywhere, and the device gets back nothing. It originally ran on Raspberry Pis at home, hence the name. But running it on a VPS (and routing your devices through a WireGuard tunnel to reach it) means you get the same protection on cellular data, in coffee shops, and on networks where you can't change the DNS — which is most networks. About twenty-five minutes from blank VPS to global ad-blocking.

📋
Before you start: A small VPS — Pi-hole barely uses any resources, the 512 MB starter plan is plenty. Ubuntu 22.04 or 24.04 LTS, root access, and ports 51820/UDP (WireGuard) plus 80/443 (Pi-hole admin) reachable. We'll combine this with our WireGuard VPN guide — Pi-hole + WireGuard is the canonical pairing.

Step 1: Why a VPS, not a Raspberry Pi

The original Pi-hole pitch is a $35 Pi sitting on your home network. That works great when you're at home. The moment you walk out the door with your phone — to a café, to work, to a hotel — your phone is no longer using your home DNS. Ads come back. The mobile carrier serves whatever DNS it wants. The hotel injects banner ads into HTTP traffic.

A VPS-hosted Pi-hole sits on the public internet. Combined with a WireGuard tunnel from your devices, every DNS query (and every other packet) routes through the VPS no matter where you are. You get the same ad blocking on the train as in your living room.

Step 2: Install Pi-hole

Pi-hole's official installer handles 95% of the setup automatically. Run as root:

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

The installer will walk through a series of dialogs:

At the end of the install, the script prints a randomly generated admin password. Save it. You can change it later with pihole -a -p.

Step 3: Configure upstream DNS resolvers

By default, Pi-hole forwards queries to Cloudflare or whatever upstream you picked. That works, but it means Cloudflare sees every domain you visit. For maximum privacy, run your own recursive resolver — unbound — locally. Pi-hole then queries unbound, and unbound queries the root DNS servers directly. No third party in the loop.

sudo apt install -y unbound

# Pi-hole's official unbound config
sudo tee /etc/unbound/unbound.conf.d/pi-hole.conf > /dev/null <<'EOF'
server:
    verbosity: 0
    interface: 127.0.0.1
    port: 5335
    do-ip4: yes
    do-udp: yes
    do-tcp: yes
    do-ip6: no
    root-hints: "/var/lib/unbound/root.hints"
    harden-glue: yes
    harden-dnssec-stripped: yes
    use-caps-for-id: no
    edns-buffer-size: 1232
    prefetch: yes
    num-threads: 1
    so-rcvbuf: 1m
    private-address: 192.168.0.0/16
    private-address: 172.16.0.0/12
    private-address: 10.0.0.0/8
EOF

sudo wget -O /var/lib/unbound/root.hints https://www.internic.net/domain/named.root
sudo systemctl enable --now unbound
dig @127.0.0.1 -p 5335 cloudflare.com   # should return an answer

Then in the Pi-hole web UI: Settings → DNS. Uncheck all the upstream DNS provider boxes. In "Custom 1 (IPv4)" enter 127.0.0.1#5335. Save. Pi-hole now resolves everything itself.

Step 4: Add quality blocklists

The default StevenBlack list blocks ~50,000 domains. That's a great start, but specific categories (malware, smart-TV trackers, mobile telemetry) need additional lists. Don't go overboard — adding 50 lists with massive overlap doesn't add coverage, it just makes Pi-hole slower.

In the web UI: Group Management → Adlists. Add these (one at a time, paste URL, click Add):

https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts                          (default — already added)
https://adaway.org/hosts.txt                                                              (mobile ads)
https://v.firebog.net/hosts/AdguardDNS.txt                                                (AdGuard's curated list)
https://v.firebog.net/hosts/Easyprivacy.txt                                               (privacy/tracking)
https://raw.githubusercontent.com/perflyst/PiHoleBlocklist/master/SmartTV-AGH.txt         (smart TV telemetry)
https://raw.githubusercontent.com/Perflyst/PiHoleBlocklist/master/android-tracking.txt    (Android telemetry)

Then update the gravity (the merged blocklist):

pihole -g

You should now have around 200,000–400,000 domains blocked total.

Step 5: Lock down the admin interface

Pi-hole's admin runs on port 80 by default. On a public-facing VPS, that's a problem — anyone who finds the IP can hit the login page. Lock it down so only the WireGuard interface (or your home IP) can reach it.

sudo apt install -y ufw
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
sudo ufw allow 51820/udp comment 'WireGuard'
# Allow Pi-hole admin only from WireGuard subnet
sudo ufw allow from 10.8.0.0/24 to any port 80
sudo ufw allow from 10.8.0.0/24 to any port 53
sudo ufw enable

That assumes you're using 10.8.0.0/24 as the WireGuard tunnel subnet (the default in our WireGuard guide). Adjust to match yours.

VPS small enough not to break the bank, big enough to actually run things

Pi-hole + WireGuard + unbound runs comfortably on our $3.99/mo Starter plan. NVMe storage means even pihole-FTL log queries return instantly. Pick a region close to home for lowest latency.

See VPS plans →

Step 6: Pair with WireGuard for mobile coverage

Follow our WireGuard setup guide to install and configure WireGuard on the same VPS. The only Pi-hole-specific change: when generating client configs, set the DNS to point at Pi-hole.

[Interface]
PrivateKey = ...
Address = 10.8.0.2/32
DNS = 10.8.0.1   # the WireGuard server's tunnel IP, where Pi-hole is

For Pi-hole to accept queries from WireGuard clients, its DNS listening behavior must allow it. In the Pi-hole admin: Settings → DNS → Interface listening behavior, set to "Permit all origins". (We've already firewalled port 53 to the WireGuard subnet, so this is safe — Pi-hole just won't argue about which interface the query came in on.)

Connect a phone or laptop to the WireGuard tunnel and visit http://10.8.0.1/admin. You should see the Pi-hole dashboard, and the query log should start filling up with your device's DNS lookups.

Step 7: Test that ads are actually blocked

Three quick tests:

1. Check what's getting blocked. Visit a site that's known to be ad-heavy. Open http://10.8.0.1/admin → Query Log in real time. You'll see a torrent of blocked queries to ad-network domains.

2. Test a known-blocked domain. From a device on the WireGuard tunnel:

dig doubleclick.net
# Should return 0.0.0.0 (Pi-hole returns NULL/0.0.0.0 for blocked domains)

3. Check via d3ward's test page. The https://d3ward.github.io/toolz/adblock.html page tests against ~200 known ad and tracker URLs. A working Pi-hole blocks 90%+ of them. Anything below that means a blocklist isn't loading or DNS isn't routing through Pi-hole.

Common issues and fixes

"DNS resolution is currently unavailable" in the dashboard — Pi-hole's FTL service isn't running. sudo systemctl status pihole-FTL. If it's failed, check the log: sudo journalctl -u pihole-FTL -n 50. Most often a port 53 conflict with systemd-resolved.

Port 53 conflict with systemd-resolved — common on fresh Ubuntu installs. Fix:

sudo systemctl disable --now systemd-resolved
sudo rm /etc/resolv.conf
echo "nameserver 1.1.1.1" | sudo tee /etc/resolv.conf
sudo systemctl restart pihole-FTL

Some sites are completely broken — over-aggressive blocklist. Use the Query Log to see what got blocked, then either disable that specific list or add a domain whitelist entry. Common offenders: sites that depend on Google's reCAPTCHA (blocked by privacy lists) or sites that load content from CDNs that some lists treat as trackers.

Some apps stop working on the phone — almost always Apple Push Notification Service or Google's mobile services hitting a blocked domain. Pi-hole's "common whitelist" group has known-good entries for these; add them under Group Management → Domains → Allow.

Slow DNS responses — unbound is missing. Either set up unbound (Step 3) or set Pi-hole's upstream to 1.1.1.1 directly. Don't leave it on a non-existent 127.0.0.1#5335 if you skipped unbound.

FAQ

Won't this make my browsing slower?

Counterintuitively, faster — for most pages. Pi-hole returns blocked domains as 0.0.0.0 instantly, so the page never wastes time loading ad scripts and trackers. Page-weight reductions of 30–60% are typical on news and blog sites. The DNS lookup itself adds a few milliseconds because it goes through your VPS, but cached lookups are essentially free.

Is Pi-hole a replacement for browser ad blockers like uBlock Origin?

It complements, doesn't replace. Pi-hole blocks at the DNS layer — entire ad-network domains. uBlock Origin blocks at the page layer — specific elements, scripts, even rules within a page. The two together are what gives you a near-perfect ad-free experience. Either alone leaves gaps.

What about YouTube ads?

YouTube serves both ads and content from the same Google domains, which means Pi-hole can't block ads without breaking the site. SponsorBlock and YouTube Vanced (RIP) on phones, or uBlock Origin in browsers, handle YouTube ads. Pi-hole won't.

How much bandwidth does Pi-hole use?

DNS queries themselves are tiny (a few hundred bytes each). For a single user with WireGuard routing all traffic through, expect ~50–200 GB/month depending on streaming and downloads. The DNS portion alone is under 1 GB/month. Most VPS plans include 1–10 TB transfer, so this isn't a concern.

Can multiple devices share one Pi-hole?

Yes — generate a separate WireGuard config for each device. Pi-hole shows per-client query logs in the dashboard, so you can see what each device is doing. There's no realistic limit on a small VPS; we've seen single Pi-hole instances handling 30+ devices comfortably.

🐱
The OliveVPS Team

Most of us run Pi-hole + WireGuard for personal use. The dashboard query log is genuinely satisfying — watching Facebook's tracking pixels die in real time never gets old.