Back to Blog
tutorialIntermediate15 min read

This post contains affiliate links. If you purchase through these links, I may earn a small commission at no extra cost to you.

Pi-hole + Unbound on Proxmox: Network-Wide Ad Blocking with Recursive DNS (2026)

Deploy Pi-hole v6 with Unbound recursive DNS in a Proxmox LXC container on a dedicated VLAN. Network-wide ad blocking with per-client visibility — no reliance on upstream DNS providers.

Pi-hole v6 admin dashboard showing status active, 29 total queries, 78,788 domains on blocklists, with total queries and client activity graphs

Most Pi-hole guides stop at "install it and point your DNS at it." That works, but you end up relying on Google or Cloudflare as your upstream resolver — which means a third party still sees every domain you look up.

Adding Unbound as a recursive DNS resolver cuts that dependency entirely. Your Pi-hole talks directly to the authoritative DNS servers for each domain, starting from the root servers. No middleman.

In this guide, we'll deploy Pi-hole v6 with Unbound inside a Proxmox LXC container on a dedicated VLAN. The setup gives you network-wide ad blocking across multiple VLANs with full per-client visibility in the Pi-hole dashboard — and zero reliance on upstream DNS providers.

I'm running Proxmox VE 9.1.6 on a Threadripper PRO 3995WX with a UDR 7 handling routing and VLANs. The Pi-hole container lives on VLAN 99 (a dedicated DNS network), and clients on VLANs 10, 20, and 30 send their DNS queries directly to it. VLAN 1 (WiFi guest network) is deliberately excluded.

Prerequisites

  • Proxmox VE 9.x installed and accessible via web UI
  • A managed switch or router with VLAN support — I'm using a UniFi Dream Router 7 (UDR7), but any router/switch that supports 802.1Q VLAN tagging works
  • A VLAN for your DNS server — I use VLAN 99 (10.1.99.0/24). You can use any VLAN you have available, or skip VLANs entirely and put Pi-hole on your main network
  • SSH access to your Proxmox host

If you haven't set up Proxmox yet, start with our Proxmox VE installation guide.

Step 1: Enable VLAN-Aware Bridging

Skip this step if your Pi-hole will be on the same VLAN/subnet as your Proxmox host, or if your bridge is already VLAN-aware.

By default, a Proxmox bridge (vmbr0) doesn't handle VLAN tags. LXC containers assigned to a different VLAN won't be able to communicate. We need to enable VLAN-aware bridging so the bridge can tag and untag 802.1Q frames.

In the Proxmox web UI, go to System → Network → vmbr0 and click Edit. Check the VLAN aware checkbox:

Proxmox Edit Linux Bridge dialog for vmbr0 showing VLAN aware checkbox enabled
Proxmox VLAN-Aware Bridge Configuration

Click OK, then Apply Configuration. This adds two lines to /etc/network/interfaces:

bridge-vlan-aware yes
bridge-vids 2-4094

Your host's own traffic continues untagged — nothing changes for the Proxmox host itself. The bridge now accepts tagged frames from LXC containers and passes them to the upstream switch/router.

Verify the Router Port

If you're using a managed switch or router, make sure the port connected to your Proxmox host allows tagged VLAN traffic. On a UDR 7, check that Tagged VLAN Management is set to Allow All (or includes your Pi-hole VLAN):

UniFi UDR 7 port settings showing Tagged VLAN Management set to Allow All
UDR 7 Port Configuration — Allow All Tagged VLANs

Step 2: Run the Pi-hole Helper Script

We'll use the community helper scripts to create the LXC container and install Pi-hole. These scripts are from the community-scripts/ProxmoxVE project — 5k+ stars, MIT licensed, actively maintained.

SSH into your Proxmox host and run:

bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/pihole.sh)"

The script opens a series of whiptail dialogs. Choose Advanced Install so you can customize the container settings:

Community-Scripts Options dialog with Advanced Install highlighted
Community Scripts — Advanced Install Option

Container Settings

Here are the settings I used. Adjust the IP, VLAN, and resources to fit your network:

SettingValueNotes
Container TypeUnprivilegedMore secure — the default
Root Password(set one)For console access
Container ID100I use VMID = IP last octet
Hostnamepihole
Disk Size4 GBDefault 2 GB is tight with logs + Unbound
CPU Cores1Pi-hole is lightweight
RAM512 MBEnough for Pi-hole + Unbound
Bridgevmbr0Your VLAN-aware bridge
IPv4StaticDNS needs a predictable address
IP Address10.1.99.100/24Your chosen IP in CIDR notation
Gateway10.1.99.1Your VLAN's gateway
VLAN Tag99Your DNS VLAN (leave blank if no VLAN)
DNS Server(leave blank)We'll fix this after install

A few of the key dialogs:

Static IPv4 Address dialog with 10.1.99.100/24 entered
Static IPv4 Address — 10.1.99.100/24 on VLAN 99

VLAN Tag dialog with 99 entered
VLAN Tag — Set to 99 (DNS VLAN)

Enable Nesting (Important for Debian 13)

When the script asks about Nesting Support, make sure it's set to Yes:

Nesting Support dialog with Yes highlighted
Nesting Support — Required for Debian 13

Warning: Debian 13 (trixie) ships systemd 256+, which requires nesting enabled on unprivileged LXC containers. Without it, the container starts in a degraded state — systemd can't create namespaces for service isolation, resulting in credential errors and failed services. The script defaults to Yes, but double-check it.

For the remaining prompts (FUSE, TUN/TAP, GPU passthrough, keyctl, APT cacher, container protection, device nodes, mount filesystems, verbose mode), accept the defaults — none of these are needed for Pi-hole.

Review the summary and confirm:

Creation summary showing all container settings
LXC Creation Summary — All Settings Confirmed

Step 3: Install Unbound

After the container is created, the script installs Pi-hole and then asks if you want to add Unbound:

Terminal showing Pi-hole installed and Unbound prompt
Pi-hole Installed — Unbound Prompt

Type y to install Unbound. The next prompt asks whether to configure it as a DNS-over-TLS forwarder instead of recursive:

Unbound DoT forwarding prompt — answer No to keep recursive
Unbound — Choose Recursive DNS (Not DoT Forwarding)

Type n (or press Enter) to keep the default recursive mode. This is the whole point — Unbound queries root servers directly instead of forwarding to a third-party DNS provider.

The script finishes and shows the Pi-hole admin URL:

Setup complete with URL http://10.1.99.100/admin
Pi-hole Setup Complete

Step 4: Fix the Configuration (Don't Skip This)

The helper script gets you 90% of the way there, but it leaves a few things misconfigured. I hit all three of these during my deployment — here's how to fix them.

Remove the Leftover Google DNS Upstream

Check the Pi-hole admin UI at http://<your-pihole-ip>/admin under Settings → DNS. You may see Google DNS (8.8.4.4) listed alongside Unbound:

Pi-hole upstream DNS showing 127.0.0.1#5335 and 8.8.4.4
Pi-hole Upstream DNS — Google DNS Leftover (Bug)

This is a known issue with the helper script. With Google DNS as a second upstream, some queries bypass Unbound entirely and go straight to Google — defeating the purpose of recursive DNS.

Fix it from the Proxmox host:

pct exec 100 -- pihole-FTL --config dns.upstreams '["127.0.0.1#5335"]'

Verify it's gone:

pct exec 100 -- pihole-FTL --config dns.upstreams

Expected output:

[ "127.0.0.1#5335" ]

The admin UI should now show Unbound as the only upstream:

Pi-hole upstream DNS showing only 127.0.0.1#5335
Pi-hole Upstream DNS — Unbound Only (Fixed)

Change the Listening Mode

By default, Pi-hole listens in LOCAL mode — it only responds to queries from the same subnet (10.1.99.0/24). Since we're sending DNS queries from other VLANs (10.1.10.x, 10.1.20.x, etc.), Pi-hole rejects them as non-local.

Why not route DNS through the router? You could have all clients use their gateway as DNS and forward everything to Pi-hole from there. But that has two problems: (1) you lose per-client visibility — every query shows as coming from the router, not the actual device, and (2) you can't exclude specific VLANs since the forwarding is global. Direct client-to-Pi-hole gives you both per-client stats and per-VLAN control.

Change the listening mode to SINGLE (respond on eth0 regardless of source subnet):

pct exec 100 -- pihole-FTL --config dns.listeningMode single

This is safe because Pi-hole only listens on eth0 inside the container — it's not an open resolver. Only traffic routed to VLAN 99 by your router can reach it.

Fix the Container's DNS

The script may have ignored the DNS server setting and inherited your Proxmox host's DNS instead. Check:

pct exec 100 -- cat /etc/resolv.conf

If it shows your router's IP instead of 127.0.0.1, the container is resolving through the router rather than itself. Fix it:

pct set 100 -nameserver 127.0.0.1
pct reboot 100

Note: LXC containers with static IPs don't use DHCP, so they never pick up DNS settings from your DHCP server. You have to set the nameserver explicitly in the Proxmox container config. Proxmox writes /etc/resolv.conf on container start based on this setting.

Step 5: Configure Per-VLAN DNS

Now point your network clients at Pi-hole. The approach depends on your router, but the concept is the same: set the DHCP-distributed DNS server to your Pi-hole IP on each VLAN you want filtered.

On a UniFi router, go to Settings → Networks, select each network, and set the DHCP Name Server to your Pi-hole IP:

UniFi DNS Server setting with 10.1.99.100 entered
UniFi Per-VLAN DNS — Pointing to Pi-hole

Here's how I configured mine:

VLANNetworkDHCP DNS ServerNotes
1WiFiAuto (ISP default)Excluded from Pi-hole
10Production10.1.99.100Pi-hole + Unbound
20Lab10.1.99.100Pi-hole + Unbound
30IoT10.1.99.100Pi-hole + Unbound

Do not change your router's global/WAN DNS setting to Pi-hole — that would force all VLANs through it, including any you want to exclude.

Don't Forget the Proxmox Host

Your Proxmox host also has DNS configured, and it's manual — not managed by DHCP. If you want the host itself filtered by Pi-hole, update /etc/resolv.conf on the host:

# On the Proxmox host (not inside the container)
echo "nameserver 10.1.99.100" > /etc/resolv.conf

Step 6: Verify Everything Works

Test Unbound (Recursive DNS)

From the Proxmox host, query Unbound directly inside the container:

pct exec 100 -- dig @127.0.0.1 -p 5335 google.com +short

You should get an IP address back. If this times out, Unbound isn't resolving — check that the container has internet access and that Unbound is running (pct exec 100 -- systemctl status unbound).

Test Pi-hole

Query Pi-hole from the Proxmox host (cross-VLAN):

dig @10.1.99.100 google.com +short

This should return an IP. If it times out, check the listening mode (should be single) and that your router is routing traffic between VLANs.

Test Ad Blocking

dig @10.1.99.100 ads.google.com +short

Expected: an empty response or 0.0.0.0 — the domain is blocked by Pi-hole's gravity list.

Check Pi-hole Stats

curl -s http://10.1.99.100/api/stats/summary | python3 -m json.tool

You should see queries_blocked, gravity_size (should be ~78,000+ with default lists), and other stats. Note: Pi-hole v6 uses /api/ — the old /admin/api.php endpoint no longer works.

Verify from a Client

On a device connected to one of your filtered VLANs, renew DHCP (or disconnect/reconnect WiFi) and run:

nslookup google.com

The server should show your Pi-hole IP (10.1.99.100). Check the Pi-hole admin dashboard — you should see the query logged with the client's actual IP address, not your router's.

Wrap-Up

Here's what we've built:

Client (any VLAN) → Router (routes to VLAN 99) → Pi-hole → Unbound → Root DNS Servers

Pi-hole handles ad blocking and query logging. Unbound handles recursive resolution — no queries leave your network to a third-party DNS provider. Each client's queries are logged individually (not hidden behind the router IP), and you have per-VLAN control over which networks use Pi-hole.

The whole thing runs in a single LXC container using 1 vCPU, 512 MB of RAM, and 4 GB of disk. It handles DNS for my entire network without breaking a sweat.

A few things to consider going forward:

  • Add more blocklists — the default Pi-hole lists are a solid start, but OISD and Hagezi are worth adding for broader coverage
  • Set up local DNS records — point hostnames like jack.home.lab at your homelab IPs through Pi-hole's Local DNS feature
  • Monitor the dashboard — the Pi-hole v6 admin UI is genuinely useful for understanding your network's DNS traffic patterns

Products Mentioned

10Gtek 10Gb PCIe NIC (Intel 82599EN, Single SFP+)

10Gtek

Cable Matters 10Gbps Cat 6A Ethernet Cable (5-Pack, 7ft, Shielded)

Cable Matters

XikeStor 5-Port 2.5G Managed Switch with 10G SFP+

XikeStor

Related Content