All posts How-to

How to set up Pi-hole on a VPS

Network-wide DNS ad-blocking that follows you everywhere - even on cellular. Pair Pi-hole with WireGuard on a Linux VPS and every device gets the same clean DNS, anywhere on earth.

ads.tracker.com doubleclick.net analytics.js facebook-px π github.com wikipedia.org your-site.com PI-HOLE

Pi-hole sits between your devices and the rest of the internet, intercepting every DNS query and dropping the ones that resolve to known ad servers and trackers. When it works, ads simply don't load - the browser never gets the IP address, so there's nothing to render and nothing to block visually. It's the closest thing to magic that DNS has to offer.

The catch with running it on a Raspberry Pi at home is that it only protects devices on your home network. As soon as you walk out the door, your phone goes back to the ad-soaked default. The fix is to move Pi-hole to a VPS and tunnel your devices to it - same Pi-hole, same dashboard, but the protection follows you onto coffee-shop Wi-Fi and cellular. This guide walks you through the whole thing.

What you'll have when you're done

  • A working Pi-hole instance on a Linux VPS, with the standard web admin panel.
  • A WireGuard tunnel that routes your devices' DNS (and optionally all traffic) through it.
  • An optional Unbound recursive resolver so you stop leaking queries to Google or Cloudflare.
  • A blocklist setup that kills 90% of ads and trackers without breaking websites.

Prerequisites

  • A Linux VPS with root access. Pulsar Nano at $3/month is plenty - Pi-hole sips RAM and CPU.
  • Ubuntu 22.04 / 24.04 or Debian 12. Pi-hole officially supports both.
  • Comfort with the command line and editing config files.
  • About 20 minutes.

Before you start: never run Pi-hole as a public open DNS resolver. Port 53 (DNS) should never be exposed to the open internet - it gets abused for amplification attacks and your IP will get blacklisted. We'll lock it down to localhost and the WireGuard subnet only.

Step 1: Update the system

Fresh VPS, fresh updates. Always.

sudo apt update && sudo apt upgrade -y

Reboot if a new kernel came down: sudo reboot.

Step 2: Run the Pi-hole installer

The official installer is a one-liner. Yes, "curl-pipe-bash" makes security people twitch - read the script first if it bothers you (curl -sSL https://install.pi-hole.net | less) and then run it for real.

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

You'll get an interactive wizard. The answers that matter:

  • Upstream DNS provider: pick Cloudflare or Quad9 for now. We'll swap this for Unbound later.
  • Static IP address: let it use the current IP. On a VPS this is fine - your IP isn't changing.
  • Install the admin web interface: yes.
  • Install the lighttpd web server: yes (if you don't already have nginx/Caddy running).
  • Log queries: yes (you can turn it off later).
  • Privacy mode: "Show everything" is fine for personal use. Pick higher modes if you want less data retention.

When it finishes, the installer prints your admin password. Write this down. If you lose it: pihole setpassword.

Test the admin panel by visiting http://YOUR_VPS_IP/admin in a browser. You should see the Pi-hole dashboard.

Step 3: Lock down DNS access

By default Pi-hole listens on all interfaces. We want it to only answer queries from localhost and our WireGuard subnet, not the public internet.

Open the Pi-hole admin -> Settings -> DNS. Under "Interface listening behavior," select "Listen only on interface eth0" (or whatever your VPS interface is named). Save.

Then close port 53 to the open internet via firewall. If you're using ufw:

sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow OpenSSH                 # don't lock yourself out
sudo ufw allow 80/tcp                  # admin panel (HTTP)
sudo ufw allow 443/tcp                 # admin panel if you add TLS later
sudo ufw allow 51820/udp               # WireGuard
sudo ufw enable

Notice we never opened port 53. Pi-hole will only be reachable for DNS through the WireGuard tunnel we set up next.

Step 4: Install WireGuard for mobile access

If you haven't already, follow our WireGuard setup guide - it covers installation, key generation, and the first peer in detail. Once that's done, come back here.

Assuming WireGuard is up with the tunnel subnet 10.66.0.0/24, two things need to change to route DNS through Pi-hole:

4a. Allow DNS traffic from the WireGuard subnet to reach Pi-hole. On the VPS:

sudo ufw allow from 10.66.0.0/24 to any port 53

4b. Edit each WireGuard client config to point DNS at the Pi-hole. The DNS line under [Interface] should be:

DNS = 10.66.0.1

(That's the server's IP inside the WireGuard subnet - the same machine running Pi-hole.) Re-import the config on each client, reconnect the tunnel, and you're done.

Verify it's working: turn on the WireGuard tunnel on your phone, then visit dnsleaktest.com. You should see your VPS's IP listed as your DNS server, not your ISP's or your carrier's.

Step 5: Add a better blocklist

Pi-hole ships with a small default blocklist (StevenBlack's). It works, but it's conservative. For a meaningful step up, add Hagezi's Pro list - aggressive enough to block the bulk of trackers and ads, conservative enough not to break sites.

In the admin panel: Group Management -> Adlists. Paste this URL into "Address":

https://raw.githubusercontent.com/hagezi/dns-blocklists/main/dnsmasq/pro.txt

Click "Add." Then update gravity (Pi-hole's blocklist compile step):

pihole -g

You should see the new domains added. You'll go from ~100k blocked domains to ~250-300k.

If you find legitimate sites breaking, the easy fix is to allow them: Domain Management -> Whitelist. Add the domain. No need to restart.

Step 6: Optional - Unbound for recursive DNS

Pi-hole is forwarding your queries to Cloudflare or Google, which means those providers see your DNS lookups. If that's a concern, install Unbound on the same VPS and use it as your upstream.

sudo apt install -y unbound

Create the config:

sudo nano /etc/unbound/unbound.conf.d/pi-hole.conf
server:
    # Listen only on localhost
    interface: 127.0.0.1
    port: 5335
    do-ip4: yes
    do-udp: yes
    do-tcp: yes
    do-ip6: no

    # Trust glue only if it is within the server's authority
    harden-glue: yes
    # Require DNSSEC data for trust-anchored zones
    harden-dnssec-stripped: yes
    # Don't use Capitalization randomization
    use-caps-for-id: no

    # Reduce EDNS reassembly buffer size
    edns-buffer-size: 1232

    # Caching
    cache-min-ttl: 3600
    cache-max-ttl: 86400
    prefetch: yes
    num-threads: 1

    # Privacy
    hide-identity: yes
    hide-version: yes

Start it:

sudo systemctl enable --now unbound

Test:

dig pulsar67.com @127.0.0.1 -p 5335

You should see a response with the correct A record. Now point Pi-hole at Unbound: admin panel -> Settings -> DNS. Uncheck all upstream providers. Under "Custom 1 (IPv4)," add 127.0.0.1#5335. Save.

Your queries now go: your device -> WireGuard -> Pi-hole -> Unbound -> the actual authoritative DNS servers. No Cloudflare, no Google, no logged third-party. You're talking directly to the root and TLD servers.

Step 7: Confirm the whole chain works

From your phone or laptop, with WireGuard active:

  1. Visit a site you know has lots of ads (a news site is a fine test). They should be missing.
  2. Check the Pi-hole admin dashboard - you should see your device's queries flowing in, with the "Blocked" counter ticking up.
  3. Run dnsleaktest.com again - should still show only your VPS's IP, no fallbacks.
  4. Visit d3ward's ad-block test. With Hagezi Pro you should score 80%+.

Common gotchas

"Some apps' notifications stopped working"

Hagezi Pro blocks some push-notification CDN endpoints. If a specific app breaks, the Pi-hole query log will show what it's trying to reach - whitelist that domain in the admin.

"YouTube ads are still there"

DNS can't reliably block YouTube ads because Google serves them from the same CDN as the videos. There's no DNS-based fix for this; you need an in-browser blocker like uBlock Origin or a YouTube-specific client like NewPipe. Pi-hole handles everything else.

"Pi-hole stopped responding"

Most often this is gravity failing during an update. SSH in, run pihole -g, and read the output. If a blocklist URL is dead, remove it from the admin's Adlists page.

"I want HTTPS on the admin panel"

Highly recommended. Install Caddy as a reverse proxy on port 443, point it at lighttpd on localhost:80, and let Caddy handle Let's Encrypt automatically. Your admin panel becomes https://pihole.yourdomain.com with a real cert.

What this combination gives you

Once everything is wired up, the math is hard to beat. Pulsar Nano is $3/month. Pi-hole is free. WireGuard is free. Unbound is free. For the price of a single coffee per month you get:

  • Ad-blocked DNS on every device, on every network, anywhere on earth.
  • A trusted exit IP for hotel and coffee-shop Wi-Fi.
  • A recursive resolver that doesn't leak queries to any third party.
  • A dashboard showing every query your devices make - genuinely educational about how much background telemetry is flying around.

The closest commercial equivalent is NextDNS or Control-D at $20-40/year, and you're still trusting their no-logs claims. Self-hosted you trust yourself.

If you want the marketing-page version with the plan recommendations spelled out, the Pi-hole hosting page covers it. If you just want to spin up a VPS and get started, plans are here.


Stuck? The Pulsar67 contact form goes to engineers who actually run their own Pi-holes. We'll help.

Posted by · May 26, 2026

Run Pi-hole + WireGuard for $3/month.

Pulsar Nano handles a Pi-hole + WireGuard + Unbound stack with room to spare. NVMe SSD, dedicated IPv4, DDoS protection, online in under 60 seconds.