Back to blog
Blog

Run Pi-hole on a VPS as a Private DNS Filter

Run Pi-hole on a VPS to block ads and malware via private DNS, with Docker, WireGuard, HTTPS, backups, and safe updates.

By Anurag Singh
Updated on Nov 07, 2026
Category: Blog
Share article
Run Pi-hole on a VPS as a Private DNS Filter

Browser extensions only protect the device you install them on. DNS filtering protects anything that does a lookup: phones, smart TVs, laptops, IoT gear, and even servers.

If you run Pi-hole on a VPS, you get another perk. The same filtering works outside your home network, without sending every domain request to a third-party “secure DNS” provider.

This post is an open-source spotlight on Pi-hole. You’ll learn what it does, who it fits, what it can replace, and how to operate it on real infrastructure.

The walkthrough uses Ubuntu with Docker Compose and a WireGuard tunnel for private access. It also adds HTTPS on the admin UI and a boring maintenance routine (backups and updates).

Pi-hole, in one paragraph (and why operators care)

Pi-hole is an open-source DNS sinkhole that blocks tracking and ad domains at the resolver layer. Clients keep using DNS as usual. Blocked domains return a dead-end response instead of an IP.

Pages load with less junk. Apps make fewer background calls. You also get a clearer picture of what your devices try to contact.

It’s a good fit if you run websites, apps, or servers and want a network-wide policy without touching every browser.

For small teams, it also works well on dev machines, staging boxes, and remote laptops—especially once you put it behind a VPN.

Licensing and what you actually get

Pi-hole is open source (core components are published under open-source licenses; the project includes multiple components and repositories). In practical terms, you get:

  • DNS filtering with blocklists, allowlists, regex rules, and per-client control.
  • Visibility into queries: which clients are calling what, and what’s being blocked.
  • DHCP (optional) if you run it inside a LAN. On a VPS you typically skip this.
  • Upstream DNS choice: Unbound, Cloudflare, Quad9, your own resolver, or a corporate DNS.

Pi-hole isn’t a full “secure web gateway,” and it doesn’t decrypt HTTPS. It works at DNS, which is why it stays fast and manageable on a VPS.

What Pi-hole replaces (and when a hosted DNS service is still better)

If you’ve used NextDNS, Control D, AdGuard DNS, or a corporate filtering DNS, the appeal is familiar: one policy, everywhere. Pi-hole is the self-hosted version. You keep the logs and the rules on infrastructure you control.

  • Hosted/proprietary DNS filtering usually offers easier roaming, polished apps, and sometimes threat intel feeds.
  • Pi-hole on your VPS gives you control over logging, retention, upstream resolvers, and access. No account lockouts, no provider-side outages, and no “free plan changed again” surprises.

Hosted services still have an edge if you need per-user identity with SSO, device management at scale, or enterprise reporting without running a VPN.

For many operators, Pi-hole lands in the practical middle. You get predictable behavior, inspectable configs, and low monthly cost.

Where to run it: VPS sizing and network requirements

Pi-hole is lightweight. Size your VPS around client count, how chatty they are, and how much logging you keep.

  • Small (single user, few devices): 1 vCPU, 1 GB RAM, 15–20 GB SSD.
  • Family/team (10–30 devices): 1–2 vCPU, 2 GB RAM, 25+ GB SSD.
  • Busy office / lots of roaming clients: 2 vCPU, 2–4 GB RAM, NVMe preferred.

Network basics:

  • Expose WireGuard UDP (commonly 51820/udp) to the internet.
  • Keep DNS (53/tcp, 53/udp) private to the VPN. Don’t open it publicly unless you know exactly why and how you’ll prevent abuse.
  • Expose the admin UI only over HTTPS, ideally only to the VPN.

If you don’t have a VPS yet, a general-purpose HostMyCode VPS is the right starting point. If you want patching help and someone to sanity-check firewall rules, managed VPS hosting fits this exact use case.

Deployment plan: Docker Compose + WireGuard + HTTPS reverse proxy

There are a few sane ways to deploy Pi-hole. On a VPS, Docker Compose keeps the footprint tidy. You get one directory, one stack, and straightforward updates.

The pattern below uses:

  • Pi-hole for DNS filtering
  • WireGuard so clients can securely reach DNS wherever they are
  • Caddy (or Nginx) as a reverse proxy for HTTPS on the admin UI

If you already run Caddy for other containers, this drops in cleanly. If you’re setting it up for the first time, HostMyCode has a good primer on setting up Caddy as a reverse proxy for Docker.

Base server prep (Ubuntu) before you touch Pi-hole

Start with a fresh Ubuntu LTS VPS. Do the basics first:

  • Update packages
  • Enable automatic security updates
  • Install Docker + Compose plugin
sudo apt update && sudo apt -y upgrade
sudo apt -y install ca-certificates curl gnupg ufw

# Optional but recommended
# See: https://www.hostmycode.com/tutorials/set-up-automated-security-updates-ubuntu

# Docker (official repo install is preferred for current builds)
# If you already have Docker, skip this.

Firewall: allow SSH and WireGuard. Only open HTTP/HTTPS if you plan to serve the admin UI publicly.

Many operators keep the UI VPN-only. Even then, you may still need 80/443 for certificate issuance.

sudo ufw allow OpenSSH
sudo ufw allow 51820/udp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable

If you’ve ever lost time to “why is my reverse proxy returning 502,” keep HostMyCode’s quick diagnostic close: Nginx 502 Bad Gateway fix on Ubuntu VPS.

The same usual suspects apply to Caddy. Check the upstream, the port, and the Docker network.

Docker Compose: Pi-hole + WireGuard (practical stack)

Create a working directory. Keep secrets out of Git. Treat it like production, even if it’s “just DNS.”

sudo mkdir -p /opt/pihole
cd /opt/pihole

Here’s a Compose file you can adapt. It keeps DNS private to the VPN by not publishing 53/tcp+udp publicly.

services:
  pihole:
    image: pihole/pihole:latest
    container_name: pihole
    hostname: pihole
    environment:
      TZ: "Etc/UTC"
      # Set a strong password or use a random one and store it safely.
      WEBPASSWORD: "change-this-now"
      # Optional: point to a resolver you trust
      DNS1: "1.1.1.1"
      DNS2: "1.0.0.1"
    volumes:
      - ./etc-pihole:/etc/pihole
      - ./etc-dnsmasq.d:/etc/dnsmasq.d
    restart: unless-stopped
    networks:
      - internal

  wireguard:
    image: lscr.io/linuxserver/wireguard:latest
    container_name: wireguard
    cap_add:
      - NET_ADMIN
      - SYS_MODULE
    environment:
      TZ: "Etc/UTC"
      PUID: "1000"
      PGID: "1000"
      # Change this: your VPS public endpoint or DNS name
      SERVERURL: "vpn.example.com"
      SERVERPORT: "51820"
      PEERS: "laptop,phone"
      PEERDNS: "10.13.13.1"  # We'll route clients to Pi-hole via the WG subnet
      INTERNAL_SUBNET: "10.13.13.0"
      ALLOWEDIPS: "0.0.0.0/0, ::/0"
    volumes:
      - ./wireguard:/config
      - /lib/modules:/lib/modules:ro
    ports:
      - "51820:51820/udp"
    sysctls:
      - net.ipv4.conf.all.src_valid_mark=1
    restart: unless-stopped
    networks:
      - internal

  caddy:
    image: caddy:latest
    container_name: caddy
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - ./caddy-data:/data
      - ./caddy-config:/config
    restart: unless-stopped
    networks:
      - internal

networks:
  internal:
    name: pihole_internal

A few notes that prevent avoidable mistakes:

  • Pi-hole’s web UI listens on port 80 inside the container. The reverse proxy reaches it over the Docker network.
  • WireGuard here is for roaming clients. After they connect, they’ll use Pi-hole for DNS.
  • If you already run WireGuard on the host, don’t run a second WireGuard container. Choose one approach and keep it simple.

HTTPS admin UI with Caddy (clean and low-maintenance)

Create /opt/pihole/Caddyfile. Use a DNS name you control.

If you need a domain, get one via HostMyCode Domains and point it to your VPS.

pihole-admin.example.com {
  encode zstd gzip

  # If you want VPN-only access, restrict by WireGuard subnet.
  @allowed remote_ip 10.13.13.0/24
  respond !@allowed "Forbidden" 403

  reverse_proxy pihole:80
}

Keep the remote_ip gate unless you have a strong reason not to. It turns “admin on the internet” into “admin only over the tunnel.”

If you need access from a fixed office IP without VPN, add another allow rule for that IP.

Bring the stack up:

sudo docker compose up -d

If Caddy can’t issue certificates, check the basics first. DNS may point to the wrong IP. Ports 80/443 may be blocked. Another service may already be using those ports.

WireGuard client setup: route DNS through Pi-hole

The LinuxServer WireGuard container will generate client configs under /opt/pihole/wireguard/peer_*. Each client config needs two things right:

  • DNS set to Pi-hole (reachable over the tunnel)
  • AllowedIPs that match your intent (full tunnel vs split tunnel)

Full tunnel is often the simplest choice for phones and laptops. It keeps DNS consistent and reduces risk on hostile Wi-Fi.

If you prefer split tunnel (only DNS and admin), tighten AllowedIPs. Route only what you need.

Quick operational check after connecting from a client:

  • Run nslookup example.com 10.13.13.1 (or whatever your Pi-hole tunnel IP is).
  • Open https://pihole-admin.example.com/admin and confirm the UI loads.
  • Verify Pi-hole shows queries from your WireGuard client IP.

Common pitfalls on a VPS (and how to avoid them)

  • Opening DNS to the public internet: don’t publish 53 unless you’re building a hardened public resolver with rate limits and abuse controls. For Pi-hole, VPN-only is the safer default.
  • Forgetting upstream DNS resilience: configure two upstream resolvers, or run Unbound. A single upstream failure looks like “the internet is down.”
  • Weak admin password: the UI controls your filtering rules. Treat it like admin access to your network.
  • IPv6 confusion: if your clients use IPv6, make sure your VPN plan and DNS settings don’t bypass Pi-hole.

If you want a refresher on TLS and reverse proxy patterns for small VPS services, HostMyCode’s write-ups on containerized tools are good references.

For a comparable “single admin UI behind HTTPS” pattern, see self-hosting Uptime Kuma with HTTPS.

Backups: what to save (and how to prove the backup works)

In this setup, Pi-hole’s important data lives in two places:

  • /opt/pihole/etc-pihole (gravity DB, settings, lists)
  • /opt/pihole/etc-dnsmasq.d (DNSMasq config snippets)

Back up those directories, plus your WireGuard configs and Caddy data:

  • /opt/pihole/wireguard
  • /opt/pihole/caddy-data and /opt/pihole/caddy-config
  • /opt/pihole/Caddyfile and docker-compose.yml

A simple approach that holds up over time is a nightly tarball plus an offsite copy (object storage, another VPS, or a backup service). Example:

sudo tar -C /opt -czf /var/backups/pihole-$(date +%F).tar.gz pihole

Schedule a restore test on a scratch VPS once a quarter. That’s where most “we have backups” plans fail.

Typical problems are wrong paths, missing files, or permissions that don’t survive the move.

Updates: keep it current without breaking DNS at 2 a.m.

With Compose, updates are repeatable:

cd /opt/pihole
sudo docker compose pull
sudo docker compose up -d
sudo docker image prune -f

Two habits reduce surprises:

  • Pin images when you want stability. If you’re change-averse, pin tags instead of tracking latest, then upgrade on your schedule.
  • Watch logs after every update. If DNS breaks, people notice fast. Catch it before they do.
sudo docker logs --since=10m pihole
sudo docker logs --since=10m wireguard
sudo docker logs --since=10m caddy

Operational checks you’ll actually use

When someone says “the internet feels broken,” you want quick, confirmable answers:

  • Is Pi-hole reachable over the VPN? Ping the WireGuard interface IP; check the WireGuard handshake timestamps.
  • Is DNS responding? dig @10.13.13.1 example.com from a connected client.
  • Is upstream DNS failing? Temporarily set upstream to a known-good resolver and retest.
  • Did a blocklist false-positive something? Use the query log, allowlist the specific domain, and keep the change minimal.

If you run multiple services on one VPS, add basic uptime checks for DNS and the admin URL.

Pi-hole won’t page you when it’s down. A monitor will.

You can reuse patterns from HostMyCode’s monitoring content, like Netdata on a VPS or Uptime Kuma.

Security posture: keep it private, keep it boring

Pi-hole ends up in your network control plane. Treat it like infrastructure, not a toy service.

  • VPN-first access for DNS and admin UI. Public DNS resolvers get abused.
  • Strong admin password and rotate it if it ever leaks.
  • Minimal open ports: typically SSH + WireGuard + 80/443 (only for certificate issuance and admin UI).
  • Patch the host: kernel and OpenSSL updates still matter, even if Pi-hole is in a container.

For a stable setup, give Pi-hole enough headroom for your VPN users and logging. Start with a HostMyCode VPS, or choose managed VPS hosting if you want help with patching, backups, and firewall hygiene.

FAQ: running Pi-hole on a VPS

Should I expose port 53 to the internet?

Usually, no. A public DNS service attracts abuse and can become an amplification vector. For Pi-hole, keep DNS private to WireGuard (or a trusted network) unless you’re building a hardened public resolver.

Will Pi-hole slow down my browsing if it’s on a VPS?

Latency matters, but DNS queries are small. If your VPS region is close to you (or your team), you’ll typically see stable performance. The bigger speed gain comes from blocking ad and tracking domains before the browser downloads them.

Can I use Pi-hole for my servers too?

Yes, but be careful. Some server-side software relies on analytics, update endpoints, or third-party APIs that can be blocklisted. Start with logging enabled, then allowlist only what you need.

What’s the simplest backup that’s still useful?

Back up /opt/pihole/etc-pihole and /opt/pihole/etc-dnsmasq.d, plus WireGuard configs. Then do at least one restore test so you know the backup isn’t just a file you can’t use.

Summary: a private DNS filter you can actually operate

Pi-hole earns its keep the first time it blocks a malware domain on a device you forgot existed. If you run Pi-hole on a VPS with WireGuard, you get consistent filtering anywhere you have a tunnel.

You also keep control over logs and policy.

Keep the setup plain: VPN-only DNS, HTTPS for the admin UI, nightly backups you’ve tested, and updates during a quiet window.

If you want a stable home for it, a HostMyCode VPS is a clean fit—small footprint, predictable costs, and enough control to run your DNS your way.