AdGuard Home runs in an unprivileged Debian 13 LXC on Proxmox VE 9.2 without config edits; root inside can bind port 53. Give it 512 MB, a static IP and Start at boot, install a release archive after checking its publisher SHA-256, confirm the version, finish setup on port 3000, and make that IP the only DNS server.

Contents
By LK Wood IV · 2026-05-26 · ~12 min read · St. Louis County, MO
AdGuard Home runs in an unprivileged LXC using Debian 13 on Proxmox VE 9.2. Port 53 needs nothing extra.
Root inside an unprivileged container holds its capabilities in the container’s own user namespace, and that namespace owns the container’s network, so AdGuard Home binds port 53 as it would on bare metal (user_namespaces(7)). The maintained community-scripts helper builds its AdGuard Home container unprivileged by default.
If you added lxc.cap.drop: and lxc.cap.keep: net_bind_service to the container config for port 53, as an earlier version of this guide said, delete both lines: lxc.cap.keep keeps the capabilities it names and drops every other one (lxc.container.conf(5)).
What follows gets you AdGuard Home answering DNS for the whole network, ads and trackers blocked, pve.lan and nas.lan resolving without anyone editing a hosts file, and, if you want it, Unbound resolving through the DNS hierarchy instead of a public recursive resolver.
Versions were read on September 26, 2026: Proxmox VE 9.2 (released May 21, 2026), the debian-13-standard_13.6-1 template (July 14, 2026 on Proxmox’s download server), AdGuard Home v0.107.79 (August 18, 2026) and Debian 13’s unbound 1.22.0.
Debian 12: use the same release archive with a different template
AdGuard Home publishes Linux builds for several CPU architectures. Step 2 uses the Linux AMD64 release archive for an x86-64 Proxmox host; it does not depend on a script detecting your Debian release. For a Debian 12 AMD64 LXC, use Proxmox’s current Debian 12 template name, then follow the same manual installation.
Do not copy the Debian 13 archive name into a Debian 12 command. On the Proxmox host, list the catalog and use the exact result:
pveam update
pveam available --section system | grep debian-12-standard
The pveam manual documents available, update and download. The resource sizes, package inventory and port-53 observations below are the Debian 13 procedure’s evidence; recheck installed packages and resolver state on a Debian 12 template instead of assuming byte-for-byte parity.
Step 1: Create the LXC container
In the Proxmox web UI, select your node and click Create CT:
Hostname: adguard
Unprivileged: yes (the Proxmox default)
Nesting: yes
Template: debian-13-standard_13.6-1_amd64.tar.zst
Disk: 4 GB
CPU: 1 core
Memory: 512 MB, swap 512 MB
Network: static, 192.168.1.5/24, gateway 192.168.1.1
DNS servers: 192.168.1.1 (your router; see below)
Then open the container’s Options and set Start at boot to Yes. Proxmox leaves it off. A host that reboots and leaves this container stopped takes the whole network’s DNS down with it, and every device in the house loses names while the internet itself is fine. The static IP is the address every device will use.
Pick one outside your router’s DHCP range.
The same container from the Proxmox host shell:
# On the Proxmox host. 105 is any free container ID; use local-zfs instead of local-lvm on a ZFS install.
pveam update
pveam download local debian-13-standard_13.6-1_amd64.tar.zst
pct create 105 local:vztmpl/debian-13-standard_13.6-1_amd64.tar.zst \
--hostname adguard --unprivileged 1 --features nesting=1 \
--cores 1 --memory 512 --swap 512 --rootfs local-lvm:4 \
--net0 name=eth0,bridge=vmbr0,ip=192.168.1.5/24,gw=192.168.1.1 \
--nameserver 192.168.1.1 --onboot 1
pct start 105
pct enter 105
Every option there is in Proxmox’s pct manual — nesting included, because the manual notes systemd also uses it to isolate services. The container’s own DNS server is the router, which works while the router hands AdGuard Home’s address to clients (Step 7).
If instead the router forwards its own lookups to AdGuard Home, put a public resolver such as 9.9.9.9 there: community-scripts issue #8356 is the loop you get when the container asks the router and the router asks AdGuard Home.
Leave the Proxmox host’s own DNS off 192.168.1.5 too, or the host cannot fetch updates while this container is down.
Optional: the community-scripts helper
The tteck helper scripts moved to community-scripts. The original repository is archived (last push November 2, 2024), and its old AdGuard script still downloads, but the build.func it sources only accepts Proxmox VE 8.1 to 8.3.
The maintained helper is on its community-scripts page. It runs as root on the Proxmox host, so it can change the host and its guests. Choosing an unprivileged container does not limit the helper’s host access. Its launcher also fetches shared code; saving or pinning the top-level script alone does not freeze everything it executes. Use the manual route below unless you have reviewed that full download chain and accept the community project’s trust boundary.
It builds an unprivileged Debian 13 container with 1 core, 512 MB and a 2 GB disk and installs AdGuard Home in /opt/AdGuardHome. Two jobs stay yours. Updates happen in AdGuard Home’s own web UI; on a Debian container the script’s update path does not update AdGuard Home and says to use the web UI.
And its service writes /var/log/AdGuardHome.out and /var/log/AdGuardHome.err with no rotation, which filled one reporter’s disk until the container would not boot (issue #17314, closed as not planned on September 17, 2026).
For a DNS server I take the manual route: a few more commands, and nothing in the container I did not put there. The helper-script safety guide covers reviewing the script and its dependencies.
If you use the helper, check the container’s DNS server the same way as in Step 1, then continue at Step 3.
Step 2: Install AdGuard Home
Use a release archive rather than executing the moving master installer. This example pins the same v0.107.79 release used for this guide’s September inventory; the latest release page still names it on October 12, 2026. For a later install, review the current release notes and use that release’s own archive and checksum if you change versions.
For this AMD64 example, the publisher’s release assets list SHA-256 c48f4a43000665484c5ec28177de11a004759b620dae8f77b2aabefc9ef3687f for AdGuardHome_linux_amd64.tar.gz and also provide checksums.txt. The following block stops if the architecture, download or checksum check fails. It refuses an existing installation so it cannot overwrite your DNS configuration.
# Inside a new container (pct enter 105, or its Console), as root.
(
set -eu
if [ "$(dpkg --print-architecture)" != amd64 ]; then
echo 'Stop: this archive and checksum are for Linux AMD64 only.' >&2
exit 1
fi
if [ -e /opt/AdGuardHome ]; then
echo 'Stop: /opt/AdGuardHome already exists; use the update procedure.' >&2
exit 1
fi
download_dir="$(mktemp -d)"
cd "$download_dir"
wget --https-only --no-verbose -O AdGuardHome_linux_amd64.tar.gz \
https://github.com/AdguardTeam/AdGuardHome/releases/download/v0.107.79/AdGuardHome_linux_amd64.tar.gz
printf '%s %s\n' \
c48f4a43000665484c5ec28177de11a004759b620dae8f77b2aabefc9ef3687f \
AdGuardHome_linux_amd64.tar.gz | sha256sum --check --strict
tar -xzf AdGuardHome_linux_amd64.tar.gz -C /opt
chown root:root /opt/AdGuardHome /opt/AdGuardHome/AdGuardHome
chmod 755 /opt/AdGuardHome /opt/AdGuardHome/AdGuardHome
/opt/AdGuardHome/AdGuardHome --version
/opt/AdGuardHome/AdGuardHome -s install
systemctl status AdGuardHome --no-pager
)
The Debian 13 template ships wget but not curl (read from the unpacked 13.6-1 template on September 26, 2026). The archive workflow uses wget. Check that the version output says v0.107.79 before continuing.
Stop on a mismatch.
The archive installs under /opt/AdGuardHome, and -s install registers the AdGuardHome service using the documented manual installation. Keep its directory and binary owned by root and writable only by root. AdGuard also publishes a binary signature inside the archive; its release verification guide explains verification against the publisher’s full key fingerprint.
The checksum ties your download to the selected release. You still trust AdGuard’s code when you run it as root inside the container.
The service starts the setup wizard on port 3000.
Step 3: Initial setup via web UI
Open http://192.168.1.5:3000 (your container’s IP). The wizard runs once:
Admin Web Interface: All interfaces, port 80. AdGuard’s Getting started lists 3000 for the initial installation and 80 for the web interface.
DNS server: All interfaces, port 53.
A username and password. For a later password reset, install the hash utility inside the container with
apt update && apt install -y apache2-utils(Debian’s apache2-utils carrieshtpasswd). The documented syntax ishtpasswd -B -C 10 -n -b <USERNAME> <PASSWORD>: replace both placeholders, including the angle brackets, with your shell-quoted username and new password.Stop the service with
systemctl stop AdGuardHome, then edit/opt/AdGuardHome/AdGuardHome.yaml. Underusers, replace the matching user’spasswordvalue with only the generated bcrypt hash (the part afterusername:). Preserve the YAML indentation, save, and runsystemctl start AdGuardHome.
After the wizard, the web UI is at http://192.168.1.5 and DNS answers on port 53.
Step 4: Configure upstream DNS
Settings → DNS settings → Upstream DNS servers:
https://dns.quad9.net/dns-query
https://cloudflare-dns.com/dns-query
dns.quad9.net is Quad9’s threat-blocking service, a second filter behind your blocklists. Cloudflare is the other upstream. Both use DNS-over-HTTPS, which encrypts the lookups sent to those endpoints.
Fallback DNS servers, used only when the upstreams fail (these plain IP addresses use unencrypted DNS):
1.1.1.1
9.9.9.9
Leave DNS cache configuration alone. The default cache is 4 MiB (v0.107.79 source).
Step 5: Add blocklists
Filters → DNS blocklists. A fresh install already has the AdGuard DNS filter turned on (same source). Add the second list with Add blocklist → Add a custom list:
| List | URL | What it covers |
|---|---|---|
| AdGuard DNS filter | (on by default) | Ads, trackers, analytics |
| OISD Big | https://big.oisd.nl | Ads, malware, phishing |
| Steven Black Hosts | https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts | Ads and malware; the social, gambling and other extensions are separate files |
OISD Big is also under Choose from the list, but OISD’s setup page says those copies are cached and may lag behind, so add it as a custom list by URL. Start with the AdGuard DNS filter and OISD Big.
Steven Black is for more aggressive blocking, and every list you add is another source of false positives to chase in the Query Log. I would stop at two.
Step 6: Add local DNS records
Filters → DNS rewrites → Add DNS rewrite, one per host:
| Domain | IP |
|---|---|
pve.lan | 192.168.1.2 |
nas.lan | 192.168.1.3 |
portainer.lan | 192.168.1.4 |
adguard.lan | 192.168.1.5 |
uptime.lan | 192.168.1.10 |
A wildcard works too (the rewrite form asks for a domain name or wildcard): *.homelab.lan → 192.168.1.10 sends every name under it to one reverse proxy, such as Nginx Proxy Manager.
Step 7: Point your network at AdGuard Home
Router DHCP (OPNsense, pfSense, most consumer firmware): set the DNS server your router hands out to 192.168.1.5 and leave the second DNS field empty. A public server there leaks.
With two servers, Windows asks the second one whenever the first has not answered within one second (Microsoft’s DNS client timeouts), and 9.9.9.9 bypasses your AdGuard Home lists and cannot resolve your pve.lan rewrite. Want a second resolver?
Make it a second AdGuard Home with the same lists and local rewrites. Clients pick up the change when they renew their lease.
VLANs (built as in the VLAN homelab guide): have each VLAN’s DHCP hand out 192.168.1.5, and allow TCP and UDP port 53 to that address above any rule that blocks the VLAN from private ranges.
The guide’s IoT rule blocks all of RFC1918, AdGuard Home included, so an IoT VLAN without the allow rule gets no DNS at all.
AdGuard Home as the DHCP server: Settings → DHCP settings, enable it, set the range and gateway. Turn DHCP off on the router first. Two DHCP servers on one subnet hand out conflicting leases.
Step 8: Verify everything works
Run these in order. Replace 105 and the addresses with yours. Skip the DNSSEC row until you have installed Unbound in the optional section below. Before checking the Query Log, run nslookup github.com on the client without specifying a server; that checks its configured resolver rather than forcing a query to AdGuard Home.
| Check | Run on | Command | Working answer | A wrong answer means |
|---|---|---|---|---|
| Starts with the host | Proxmox host | pct config 105 | an onboot: 1 line | Start at boot is off (Proxmox’s default) |
| AdGuard Home holds port 53 | the container | ss -lunp | AdGuardHome on :53 | the service is down (systemctl status AdGuardHome) or another program has port 53 |
| Local names | any client | nslookup pve.lan 192.168.1.5 | 192.168.1.2 | a different answer points to the rewrite (Step 6); a timeout points to connectivity or the service |
| Blocking | any client | nslookup doubleclick.net 192.168.1.5 | 0.0.0.0 (and :: for IPv6) | check protection, the AdGuard DNS filter, exceptions and blocking mode; a timeout is not a blocking answer |
| Upstreams | any client | nslookup github.com 192.168.1.5 | a public address | check connectivity and the Query Log; press Test upstreams under DNS settings |
| One DNS server per client | a Windows client | ipconfig /all | 192.168.1.5 alone under DNS Servers | check the active adapter for an old lease, manual DNS or another advertised resolver (Step 7) |
| The client really uses it | AdGuard Home | Query Log, search the client’s IP | lookups from a few seconds ago | check the client’s resolver and whether the router forwards its queries (Common issues) |
| DNSSEC, Unbound only | the container | dig fail01.dnssec.works @127.0.0.1 -p 5335 | status: SERVFAIL | a successful answer means validation is missing; a timeout means Unbound is not answering |
The blocking row works because the AdGuard DNS filter carries the rule ||doubleclick.net^, and AdGuard Home’s default blocking mode answers an Adblock-style rule with 0.0.0.0 (configuration reference). Look at the answer address, not the DNS server address printed above it.
For the DNSSEC row, dig +ad dnssec.works @127.0.0.1 -p 5335 must also return status: NOERROR with ad among its flags; a broken resolver can return SERVFAIL for both names. Both tests come from Pi-hole’s Unbound guide. Run each command on the machine named in its row.
The Debian 13 container already carries ss, dig and nslookup (iproute2 and bind9-dnsutils are in the template’s package list (read from the unpacked 13.6-1 template on September 26, 2026)); pct belongs on the Proxmox host and ipconfig on Windows.
Optional: Unbound recursive resolver
Instead of forwarding lookups to Quad9 or Cloudflare, Unbound asks the root servers and each domain’s own servers. No single public recursive resolver receives the whole query stream. Unbound sends these as plain DNS unless you configure DNS-over-TLS upstreams, so your ISP can read them.
# Inside the container
apt update && apt install -y unbound
cat > /etc/unbound/unbound.conf.d/adguard.conf << 'EOF'
server:
verbosity: 0
interface: 127.0.0.1
port: 5335
do-ip4: yes
do-ip6: no
do-udp: yes
do-tcp: yes
harden-glue: yes
harden-dnssec-stripped: yes
use-caps-for-id: no
edns-buffer-size: 1232
prefetch: yes
num-threads: 1
private-address: 192.168.0.0/16
private-address: 172.16.0.0/12
private-address: 10.0.0.0/8
private-address: 169.254.0.0/16
private-address: fd00::/8
private-address: fe80::/10
EOF
systemctl restart unbound
The config is adapted from Pi-hole’s Unbound guide, and it goes in its own file because Debian’s /etc/unbound/unbound.conf holds one setting besides its comments, include-toplevel: "/etc/unbound/unbound.conf.d/*.conf", and the DNSSEC root trust anchor loads from root-auto-trust-anchor-file.conf in that same directory.
Nothing needs downloading. The package recommends dns-root-data, which carries the root server list.
If you wrote over unbound.conf, as an earlier version of this guide said, your Unbound resolves but does not validate: put the include line back as the whole of unbound.conf, move your settings into adguard.conf, and restart.
Then in AdGuard Home → Settings → DNS settings → Upstream DNS servers, replace the upstreams with the address below and clear the Fallback DNS servers from Step 4 so failed queries do not go to a public resolver:
127.0.0.1:5335
Run both DNSSEC tests described in Step 8. A cold lookup walks the delegation chain from the root and takes longer; cached answers and delegations make later lookups fast. That slow first answer is one more reason the router must not hand out a second DNS server.
Integrating with Tailscale
If you’re using Tailscale for remote access, your tailnet devices can use AdGuard Home from anywhere. The address has to be reachable over the tailnet first.
Either run Tailscale inside the container, which in an unprivileged LXC needs /dev/net/tun passed through (the container’s Resources → Add → Device Passthrough → /dev/net/tun, then restart it, per Tailscale’s LXC guide), or reach 192.168.1.5 through a subnet router on your LAN.
Then add the address the tailnet reaches as a nameserver on the DNS page of the Tailscale admin console: the container’s own 100.x tailnet address if Tailscale runs inside it (bring it up with tailscale up --accept-dns=false so the container does not point its own resolver back at itself, the step Tailscale’s Pi-hole guide documents), or 192.168.1.5 through the subnet router.
For .lan names alone, make it a restricted nameserver for the lan domain, which Tailscale also calls split DNS (Tailscale’s DNS docs): remote devices ask your AdGuard Home only for local names.
Common issues
Clients not using AdGuard Home after the DHCP change. Leases. A client keeps the old DNS server until its lease renews. On Windows, run ipconfig /release and then ipconfig /renew; elsewhere, reconnect to the network.
A phone that skips AdGuard Home. Android’s Private DNS setting on Automatic, the default, uses your network’s DNS server and only upgrades to DNS-over-TLS if that server offers it (Android Developers blog).
A hostname typed into Private DNS, dns.google for example, sends every lookup past AdGuard Home; set it back to Automatic or Off. On an iPhone, Safari with iCloud Private Relay on encrypts its lookups through Apple’s relay, so AdGuard Home never sees them.
Port 53 already in use during setup. The Debian 13 template does not include systemd-resolved. (Read from the unpacked 13.6-1 template on September 26, 2026.) On an Ubuntu template or host, systemd-resolved holds 127.0.0.53:53.
Press Fix next to the error in the wizard, or follow AdGuard’s FAQ: create /etc/systemd/resolved.conf.d/adguardhome.conf containing [Resolve], DNS=127.0.0.1 and DNSStubListener=no, then run mv /etc/resolv.conf /etc/resolv.conf.backup, ln -s /run/systemd/resolve/resolv.conf /etc/resolv.conf and systemctl reload-or-restart systemd-resolved.
The Query Log shows every client as the router. The router is forwarding its clients’ lookups instead of handing out AdGuard Home’s address. Change the DHCP DNS setting itself (Step 7), not the router’s own upstream.
Sources
- AdGuard Home Getting started – install, first-run ports (3000 for setup, 80 for the web interface, 53 for DNS)
- AdGuard Home configuration reference – blocking modes, upstream protocols, the web password reset
- AdGuard Home FAQ – the systemd-resolved port 53 conflict
- AdGuard Home releases – v0.107.79, August 18, 2026 (read September 26, 2026)
- AdGuard Home v0.107.79 release assets – archive SHA-256 and checksums.txt, checked October 12, 2026
- AdGuard Home release verification and service permissions – binary signatures, publisher key fingerprint, root ownership
- Proxmox VE Linux Container (LXC) and the pct manual – unprivileged containers, templates,
pct createoptions and defaults - Proxmox VE Roadmap – Proxmox VE 9.2, released May 21, 2026, on Debian 13
- lxc.container.conf(5) and user_namespaces(7) – why
lxc.cap.keepis harmful here and why port 53 needs no change - community-scripts AdGuard Home script – the maintained helper, its defaults and update path
- Unbound documentation (NLnet Labs) – the resolver’s configuration reference
- Pi-hole: Unbound recursive resolver guide – the same setup on port 5335 and the DNSSEC tests
- Pi-hole: DNS-over-HTTPS with cloudflared – the proxy-dns route the FAQ names, with Pi-hole’s own warning that Cloudflare deprecated it in November 2025 (read September 26, 2026)
- Microsoft: DNS client resolution timeouts – why a second DNS server leaks lookups
Frequently asked questions
Why use AdGuard Home instead of Pi-hole?
Can I run AdGuard Home in an LXC container on Proxmox?
How do I make *.lan hostnames resolve on all my devices?
Will AdGuard Home break anything?
Do I need Unbound? Can’t I just use AdGuard Home alone?
Sources and corrections
- Last updated
- Methodology
- See our methodology for research and review standards.
- Corrections
- Spotted an error or a stale number? Email contact@techfuelhq.com. Confirmed corrections are recorded here with the date and change.