AdGuard Home + Local DNS on Proxmox

By LK Wood IV · 2026-06-13 · ~12 min read · St. Louis County, MO

Every device on your home network hits a DNS resolver for every connection it makes. By default that resolver is whatever your ISP assigns, or whatever the router forwards to (usually 8.8.8.8). That means your ISP — or Google — sees every domain lookup from every device in your house, 24/7.

Running AdGuard Home on Proxmox fixes this: DNS ad-blocking at the network level (no browser extension needed), local hostname resolution so homelab.lan resolves without editing /etc/hosts, and an optional Unbound backend that cuts out upstream resolvers entirely.

What you’ll have at the end

  • AdGuard Home running in an LXC container, handling DNS for your whole network
  • All ad/tracker/malware domains blocked before they hit your devices
  • Local A records: nas.lan, pve.lan, portainer.lan all resolve correctly
  • DHCP served from AdGuard Home (optional) so new devices automatically use it
  • Optional: Unbound recursive resolver so no upstream DNS provider sees your queries

Step 1: Create the LXC container

In the Proxmox web UI → pve node → Create CT:

Hostname: adguard
Template: Debian 12 (bookworm)
Storage: 4GB disk
RAM: 256MB (512MB with Unbound)
vCPU: 1
Network: DHCP during setup, static IP after
Unprivileged: yes

Once created, set a static IP in the container’s network settings — this is the IP every device will use as its DNS. Example: 192.168.1.5/24, gateway 192.168.1.1. Restart the container after changing the IP.

Port 53 in an unprivileged container:

Unprivileged containers can’t bind to ports ≤1024 by default. The cleanest fix is adding this to the container’s config file on the PVE host:

# On the PVE host, not inside the container
nano /etc/pve/lxc/<CTID>.conf

# Add these lines:
lxc.cap.drop:
lxc.cap.keep: net_bind_service

Restart the container after this change. Alternatively, run a privileged container — the security difference is minimal for an isolated LXC with no internet exposure.

Step 2: Install AdGuard Home

# Inside the container
apt update && apt install -y curl

# Download and install AdGuard Home
curl -s -S -L https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scripts/install.sh | sh -s -- -v

# Verify it's running
systemctl status AdGuardHome

AdGuard Home installs itself as a systemd service. The web UI starts on port 3000 for initial setup.

Step 3: Initial setup via web UI

Open http://192.168.1.5:3000 (your container’s IP) in a browser. The setup wizard runs once:

  1. Admin interface: Listen on All Interfaces, port 3000 (or 80 if you want cleaner access)
  2. DNS server: Listen on All Interfaces, port 53
  3. Create admin credentials — note these, there’s no password recovery
  4. Click Next through to completion

After setup, the web UI moves to port 80 (or whatever you chose) and the DNS service listens on port 53.

Step 4: Configure upstream DNS

Settings → DNS Settings → Upstream DNS Servers:

https://dns10.quad9.net/dns-query
https://cloudflare-dns.com/dns-query

Quad9 blocks known malicious domains in addition to AdGuard’s blocklists. Cloudflare is the fallback. Both use DNS-over-HTTPS for encrypted upstream resolution.

Fallback DNS servers (used if upstreams fail):

1.1.1.1
9.9.9.9

Under DNS cache configuration, set cache size to 4000000 (4MB) — the default 4MB is fine for a homelab.

Step 5: Add blocklists

Filters → DNS Blocklists → Add blocklist:

The three lists that cover the vast majority of ad/tracker/malware domains without excessive false positives:

ListURLWhat it covers
AdGuard DNS filter(built-in)Ads, trackers, analytics
OISD Fullhttps://oisd.nl/blocklists/Comprehensive malware/ads
Steven Black Hostshttps://raw.githubusercontent.com/StevenBlack/hosts/master/hostsAds + social

Start with AdGuard DNS filter + OISD. Add Steven Black if you want more aggressive blocking. The more blocklists you add, the more false positives you’ll encounter — most homelab users find 2–3 quality lists better than 20.

Step 6: Add local DNS records

Filters → DNS Rewrites:

Add an A record for each service in your homelab:

DomainIP
pve.lan192.168.1.2
nas.lan192.168.1.3
portainer.lan192.168.1.4
adguard.lan192.168.1.5
uptime.lan192.168.1.10

You can also add a wildcard for a subdomain: *.homelab.lan → 192.168.1.10 if you’re running Nginx Proxy Manager and want all services under one IP.

Step 7: Point your network at AdGuard Home

If you control your router’s DHCP settings (OPNsense, pfSense, or most router firmware):

Set the primary DNS to your AdGuard Home IP (192.168.1.5). Leave secondary DNS blank or set it to 9.9.9.9 as a fallback. Every DHCP client that renews their lease will start using AdGuard Home.

If you’re running VLANs (see the VLAN homelab guide), set the DNS per VLAN interface in your firewall. This lets you serve different upstream resolvers to different VLANs — useful for IoT VLANs that you want to block more aggressively.

If you’re using AdGuard Home as the DHCP server (alternative to router DHCP):

Settings → DHCP Settings: Enable DHCP, set the range and gateway. AdGuard Home will handle both DNS and DHCP in one service. Disable DHCP on your router first — running two DHCP servers on the same subnet causes address conflicts.

Step 8: Verify everything works

From any device on your network:

# Should return your container's IP (192.168.1.5)
nslookup pve.lan
# → 192.168.1.2

# Should be blocked (returns 0.0.0.0 or NXDOMAIN)
nslookup doubleclick.net
# → 0.0.0.0

# Should resolve (test a legitimate domain)
nslookup github.com
# → 140.82.112.4 or similar

The AdGuard Home dashboard shows live query stats — you’ll see devices by name, blocked query count, and which domains are being hit most.

Optional: Unbound recursive resolver

Unbound is a validating recursive resolver. Instead of forwarding queries to Cloudflare or Quad9, it queries the DNS root servers directly and resolves domains itself. This eliminates upstream DNS providers seeing your query patterns.

# Inside the LXC container
apt install -y unbound

# Minimal Unbound config for recursive resolution
cat > /etc/unbound/unbound.conf << 'EOF'
server:
    verbosity: 0
    interface: 127.0.0.1
    port: 5335
    do-ip4: yes
    do-ip6: no
    do-udp: yes
    do-tcp: yes
    root-hints: "/etc/unbound/root.hints"
    harden-glue: yes
    harden-dnssec-stripped: yes
    use-caps-for-id: no
    edns-buffer-size: 1232
    prefetch: yes
    num-threads: 1
    cache-min-ttl: 0
    cache-max-ttl: 86400
EOF

# Download root hints (the list of DNS root servers)
curl -o /etc/unbound/root.hints https://www.internic.net/domain/named.cache

systemctl enable --now unbound

Then in AdGuard Home → DNS Settings → Upstream DNS Servers, replace the upstream with:

127.0.0.1:5335

AdGuard Home now forwards to Unbound on localhost, and Unbound resolves directly from root servers. The Pi-hole + Unbound guide by the Pi-hole project has the same concept and covers DNSSEC validation if you want to go further.

Integrating with Tailscale

If you’re using Tailscale for remote access, you can enable AdGuard Home’s DNS from anywhere on your Tailscale network. In Tailscale’s admin console → DNS → Add nameserver → Custom, enter your AdGuard Home LXC’s Tailscale IP. All connected devices then use your homelab’s DNS even when they’re on remote networks.

For .lan domains specifically, set it as a “Split DNS” nameserver in Tailscale: restrict it to the .lan domain so remote devices only hit your home DNS for local names, not for all queries.

Common issues

Clients not using AdGuard Home despite DHCP change: Renew their DHCP lease (ipconfig /release && /renew on Windows, dhclient -r && dhclient on Linux). Devices remember their old lease until it expires.

iOS/Android ignoring custom DNS: Modern mobile OS versions use their own encrypted DNS settings and bypass DHCP-provided DNS. Disable Private DNS on Android (Settings → Connections → More Connection Settings → Private DNS → Off). On iOS, go to Settings → Wi-Fi → (your network) → Configure DNS → Manual, add your AdGuard Home IP.

Port 53 already in use: On Ubuntu/Debian hosts, systemd-resolved binds to port 53. Disable it: systemctl disable --now systemd-resolved and delete /etc/resolv.conf, then echo "nameserver 192.168.1.5" > /etc/resolv.conf. This affects the container itself — only needed if you’re running AdGuard Home directly on the host rather than in LXC.

High CPU from Unbound: Root-based resolution does more work than forwarding. The first query to each domain takes longer; subsequent queries come from Unbound’s cache. If the container’s 1 vCPU is hitting 100% during initial warm-up, give it a few minutes — it settles once the most-queried domains are cached.


Running Docker Compose for your services? The Docker Compose starter stack includes Portainer for container management and Uptime Kuma for monitoring — AdGuard Home handles DNS for all of them. For VLAN-segmented networks, the VLANs homelab guide covers how to assign per-VLAN DNS servers so your IoT network can’t query your homelab hosts.