Self-Hosting AdGuard Home: The Ultimate DNS Privacy Guide
I’m a big advocate for taking back control of your network traffic. Today, we’re moving beyond standard DNS.
We are deploying AdGuard Home inside an LXC container on Proxmox. We’ll be using Docker Compose for the deployment and, most importantly, securing our queries with DNS-over-TLS (DoT) and DNS-over-HTTPS (DoH) using the domain guard.dns.puvvadi.net.
Why AdGuard Home?
While Pi-hole is classic, AdGuard Home provides a more modern UI and native support for encrypted DNS protocols without needing extra proxies like Cloudflared or Unbound.
Prerequisites
- A Proxmox LXC container (Ubuntu/Debian).
- Docker and the latest Docker Compose installed.
- A domain (we’re using
puvvadi.net). - Your local DNS records managed (mine are at
10.10.10.10via Bind9).
1. Obtaining SSL Certificates (DNS-01 Challenge)
To run DoH or DoT, you need valid SSL certificates. Since this is a homelab, we’ll use the DNS-01 challenge. This allows Let’s Encrypt to verify ownership via a DNS TXT record, so you don’t have to open port 80 to the internet.
I use Cloudflare for my DNS. Here’s the quick setup:
Install Certbot:
1
apt update && apt install certbot python3-certbot-dns-cloudflare
Configure Credentials:
Create
/etc/letsencrypt/cloudflare.iniand add your API token:1
dns_cloudflare_api_token = YOUR_CLOUDFLARE_API_TOKEN
Change permissions 600 for less acceseble.
chmod 600 /etc/letsencrypt/cloudflare.iniFetch the Cert:
1 2 3
certbot certonly --dns-cloudflare \ --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \ -d guard.dns.puvvadi.net
2. The Docker Compose Setup
Now, let’s define our service. Note that we are mounting the Let’s Encrypt folders directly so AdGuard can access the certificates.
1
2
3
4
5
6
7
8
9
10
11
12
13
services:
adguardhome:
image: adguard/adguardhome:latest
container_name: adguardhome
restart: unless-stopped
volumes:
- ./work:/opt/adguardhome/work
- ./config:/opt/adguardhome/conf
- /etc/letsencrypt:/opt/adguardhome/ssl:ro
network_mode: host
dns:
- 127.0.0.1
- 10.10.10.10
Deploy with: docker compose up -d
3. Split DNS with Bind9
Since my internal lab records live on a Bind9 instance at 10.10.10.10, we need AdGuard to respect those while still blocking ads.
- Open the UI at
https://guard.dns.puvvadi.net. - Go to Settings -> DNS Settings.
In Upstream DNS Servers, add your local resolver for our domain:
[/puvvadi.net/]10.10.10.10 [/10.in-addr.arpa/]10.10.10.10 https://dns.cloudflare.com/dns-query
4. Enabling DoT and DoH
Finally, let’s encrypt the traffic. Navigate to Settings -> Encryption Settings.
- Enable Encryption: Checked.
- Server Name:
guard.dns.puvvadi.net - Certificates: Use the paths we mapped in the volumes:
- Certificate path:
/opt/adguardhome/ssl/live/guard.dns.puvvadi.net/fullchain.pem - Private key path:
/opt/adguardhome/ssl/live/guard.dns.puvvadi.net/privkey.pem
- Certificate path:
Once you hit save, AdGuard Home will begin listening for encrypted queries on ports 443 and 853.
5. Testing the Setup with Doggo
Once everything is up and running, you shouldn’t just assume it’s working. Let’s use doggo to test our new encrypted endpoints.
Install Doggo
If you don’t have it yet, you can grab the latest version
1
go install github.com/mr-karan/doggo/cmd/doggo@latest
Test DNS-over-TLS (DoT)
Run a query against your new server on port 853. We’ll use the –tls-hostname flag to ensure the certificate matches.
1
2
3
4
5
6
7
$ doggo google.com @tls://guard.dns.puvvadi.net
NAME TYPE CLASS TTL ADDRESS NAMESERVER
google.com. A IN 2163s 142.250.207.142 guard.dns.puvvadi.net:853
google.com. AAAA IN 2163s 2404:6800:4003:c05::64 guard.dns.puvvadi.net:853
google.com. AAAA IN 2163s 2404:6800:4003:c05::71 guard.dns.puvvadi.net:853
google.com. AAAA IN 2163s 2404:6800:4003:c05::66 guard.dns.puvvadi.net:853
google.com. AAAA IN 2163s 2404:6800:4003:c05::8b guard.dns.puvvadi.net:853
Test DNS-over-HTTPS (DoH)
To test the HTTPS endpoint, simply point doggo to your resolver URL:
1
2
3
4
5
6
7
$ doggo google.com @https://guard.dns.puvvadi.net/dns-query
NAME TYPE CLASS TTL ADDRESS NAMESERVER
google.com. A IN 1609s 142.250.207.142 https://guard.dns.puvvadi.net/dns-query
google.com. AAAA IN 1609s 2404:6800:4003:c05::64 https://guard.dns.puvvadi.net/dns-query
google.com. AAAA IN 1609s 2404:6800:4003:c05::71 https://guard.dns.puvvadi.net/dns-query
google.com. AAAA IN 1609s 2404:6800:4003:c05::66 https://guard.dns.puvvadi.net/dns-query
google.com. AAAA IN 1609s 2404:6800:4003:c05::8b https://guard.dns.puvvadi.net/dns-query
Test Local Record Resolution
Finally, verify that AdGuard is correctly talking to your Bind9 instance (10.10.10.10) by querying a local homelab record:
1
2
3
$ doggo home.puvvadi.net @https://guard.dns.puvvadi.net/dns-query -t A
NAME TYPE CLASS TTL ADDRESS NAMESERVER
home.puvvadi.net. A IN 80540s 10.20.20.68 https://guard.dns.puvvadi.net/dns-query
If you see your internal IP returned in the “Answers” section, congratulations! Your split-DNS setup is fully operational and secured.
Conclusion
Your homelab now has a centralized, privacy-respecting DNS gateway. No more ISP snooping, and no more ads—all while keeping your local hostnames resolvable.