VPS Tutorials
Deploying AdGuard Home on a VPS (2026): Docker, DNS, DoH/DoT, Home Network and Security Configuration
Deploy AdGuard Home v0.107.78 on a VPS with Docker Compose, configure WireGuard private-network DNS, ad filtering, upstream DoH/DoT, DNS rewrites, IPv6, backup and upgrades, and understand port 53, DNS leaks and open-resolver security issues.

Deploying AdGuard Home on a VPS (2026): Docker, DNS, DoH/DoT, Home Network and Security Configuration
If you have many devices at home, in the office or across several VPSs, ads, tracking domains and malicious redirects are usually not a problem with one computer — every device is repeating the same requests. AdGuard Home puts DNS filtering into one unified service: clients ask it first, and it then queries upstream DNS.
Running AdGuard Home on a VPS lets phones, computers, home servers and remote devices use the same set of DNS rules through WireGuard or Headscale. It does not require installing a browser extension on every device, and it can uniformly handle ad filtering, malicious domain blocking, local DNS rewrites and query logs.
However, AdGuard Home is not a "free DNS you can just install and expose publicly." If you open public port 53 directly, the VPS may well become an open recursive resolver, abused for amplification attacks, scanning and traffic relaying. This article assumes by default a VPN private-network access model, with DNS listening only on the private IP.
The current official stable version used here is AdGuard Home v0.107.78, and the Docker persistent directories follow the official image's /opt/adguardhome/work and /opt/adguardhome/conf.
Cases where a VPS is suitable:
- You already have a WireGuard, Tailscale or Headscale private network;
- You want remote phones, computers and home devices to use the same DNS;
- You need centralized management of ad filtering, malicious domains and local service domains;
- The number of devices is small, and queries mainly come from individuals and families;
- You can accept maintaining the VPS, DNS, backups and access policies.
Cases where you should not expose it directly to the public internet for anyone to use:
- There is no VPN or source IP restriction;
- You plan to open 1.2.3.4:53 to the whole internet;
- You do not understand DNS recursion, caching, forwarding and amplification attacks;
- You need enterprise-grade auditing, identity policies or large-scale Anycast DNS;
- You only want to block browser ads but do not want to maintain a network service.
If it is just a few devices at home, a Raspberry Pi, NAS or router can also run AdGuard Home. The VPS advantage is remote availability, stable networking and easy combination with WireGuard/Headscale; the disadvantage is that all devices' DNS depends on this machine, so you must prepare a backup DNS and a recovery plan.
| Option | Main capabilities | Advantages | Points to note |
|---|---|---|---|
| AdGuard Home | DNS filtering, query logs, rewrites, encrypted DNS | Intuitive interface, supports DoH/DoT and multiple clients | You must maintain rules and privacy settings yourself |
| Pi-hole | DNS filtering, lists and statistics | Mature community, lots of material | Some advanced capabilities require extra components |
| Public DNS | Direct recursive queries | Zero maintenance, many global nodes | Cannot customize filtering per home device |
| Browser ad blocking | Only affects browser requests | Simple to install, fine-grained rules | Does not handle App, TV or IoT requests |
AdGuard Home is suited to a "unified DNS policy." It does not automatically replace browser content blockers, nor can it guarantee that ads on YouTube, streaming services or all Apps disappear. DNS can only block requests identifiable at the domain level.
Assume the VPS has already established a private network through WireGuard:
VPS 公网 IPv4
│
├── WireGuard wg0: 10.66.66.1
│ │
│ ├── 手机: 10.66.66.2 ── DNS 53 ──┐
│ ├── 笔记本: 10.66.66.3 ─────────┤
│ └── 家庭服务器: 10.66.66.4 ────┤
│ ▼
└──────────── Docker AdGuard Home ── 上游 DoH/DoT DNS
The key points here are:
- Clients connect to the VPS through encrypted WireGuard;
- AdGuard Home's port 53 binds only to 10.66.66.1, not to the public IPv4;
- AdGuard Home can use DoH or DoT for upstream DNS;
- Clients do not need to expose the admin panel to the public internet.
If you do not yet have WireGuard, you can first read the VPS WireGuard VPN setup tutorial. If you have many devices and need tags and unified policies, then read the VPS Headscale setup tutorial.
AdGuard Home itself is very light; for home scenarios with modest query volume you can start with the following configurations:
| Scenario | Recommended configuration | Notes |
|---|---|---|
| Personal and few devices | 1 core 1GB, 20GB SSD | Do not set log retention too long |
| Home lab | 2 cores 2GB, 30GB SSD | Leave headroom for WireGuard, monitoring and caching |
| Small team or multiple sites | 2–4 cores, 4GB+ | Focus on evaluating query volume, backups and high availability |
This article only needs these ports:
- 53/tcp and 53/udp: DNS queries, bound only to the VPN private IP;
- 3000/tcp: first-time setup wizard, bound only to the VPS itself;
- 80/tcp: optional AdGuard web admin port, bound only to localhost or inside a reverse proxy;
- 443/tcp, 853/tcp: open only when DoH/DoT is explicitly configured and access is restricted.
First check whether the system already occupies port 53:
sudo ss -lntup | grep -E ':(53|3000|80|443|853)\b' || true
Many Ubuntu hosts run systemd-resolved, which usually listens on 127.0.0.53:53. As long as AdGuard Home binds to the WireGuard private address 10.66.66.1, there is usually no conflict; do not blindly disable systemd-resolved just because you see a DNS tutorial.
If you really want AdGuard Home to listen on 0.0.0.0:53, you must first understand the local resolver, Docker ports and firewall rules; otherwise you may make the VPS itself unable to resolve domain names, or even expose a public recursive service.
First confirm Docker and Compose are available:
docker version
docker compose version
If Docker is not installed, you can refer to the Docker deployment practical guide. Create the AdGuard Home data directory:
sudo mkdir -p /opt/adguardhome/work
sudo mkdir -p /opt/adguardhome/conf
sudo chown -R "$USER":"$USER" /opt/adguardhome
sudo chmod 750 /opt/adguardhome
Confirm the WireGuard private address actually exists:
ip -4 addr show wg0
The example below uses 10.66.66.1. If your interface address is different, replace all 10.66.66.1 in the Compose file with the actual address. Do not copy an IP that does not exist on the host, or Docker will fail when binding ports.
Create /opt/adguardhome/compose.yaml:
services:
adguardhome:
image: adguard/adguardhome:v0.107.78
container_name: adguardhome
restart: unless-stopped
ports:
# 只在 WireGuard 私网 IP 提供 DNS,不绑定公网 IP
- "10.66.66.1:53:53/tcp"
- "10.66.66.1:53:53/udp"
# 首次安装和管理页面只通过 SSH 隧道访问
- "127.0.0.1:3000:3000/tcp"
- "127.0.0.1:8080:80/tcp"
volumes:
- /opt/adguardhome/work:/opt/adguardhome/work
- /opt/adguardhome/conf:/opt/adguardhome/conf
The official Docker example also lists optional ports such as DHCP, 443, 853 and 5443. This article does not map these ports, because ordinary DNS inside the VPN is already encrypted by WireGuard, and public DoH/DoT is not a default requirement.
Check the Compose file:
cd /opt/adguardhome
docker compose config --quiet
Start the container:
docker compose up -d
docker compose ps
docker compose logs --tail=150 adguardhome
Check the first-time setup page locally:
curl -I http://127.0.0.1:3000
If you get bind: cannot assign requested address, first check whether wg0 is up and whether the address really is 10.66.66.1. If you get a port-already-in-use error, check the ss output again; do not just switch to a public port to solve it.
Create a tunnel on your own computer:
ssh -L 3000:127.0.0.1:3000 your_user@your_vps_ip
Open in your browser:
http://127.0.0.1:3000
The initialization wizard will ask you to set the admin account, web admin port and DNS listening port. Recommendations:
- Use a separate long password for the admin account, not reused from the VPS, email or VPN;
- Keep the web admin port at 80 inside the container, and access it on the host through 127.0.0.1:8080 or an SSH tunnel;
- Choose all interfaces inside the container for DNS listening, and let the host port mapping restrict it to 10.66.66.1;
- Do not enable DHCP service you do not need, unless you clearly know the VPS is on a Layer 2 network that needs to hand out addresses;
- Record the web admin address you set, and do not confuse the admin page with the DNS port.
After completing the wizard, the admin page is on the host's local port 8080:
ssh -L 8080:127.0.0.1:8080 your_user@your_vps_ip
Then visit http://127.0.0.1:8080. If you prefer to use the first 3000 tunnel, you can also access it on the actual port shown by the wizard.
Configure upstream servers in Settings → DNS settings. You can choose DoH or DoT, for example:
https://dns.cloudflare.com/dns-query
https://dns.google/dns-query
tls://1dot1dot1dot1.cloudflare-dns.com
Then fill in Bootstrap DNS, for example:
1.1.1.1
9.9.9.9
Bootstrap DNS is only responsible for resolving upstream domain names into IPs; it does not mean all clients will bypass AdGuard Home. After configuring, use Test upstreams to test latency and availability.
A few practical suggestions:
- Choose 2 stable upstreams first; do not pile up a dozen addresses;
- Confirm the upstream service's privacy policy, log retention period and region yourself;
- When you need stability, keep a backup upstream from a different provider;
- Do not change DNSSEC, caching and concurrency settings all at once, so problems are easier to locate;
- If upstream DoH/DoT fails, first check the VPS time, certificate verification and Bootstrap DNS.
Client-to-AdGuard-Home DNS can also use DoH/DoT, but if the client is already inside the WireGuard tunnel, ordinary DNS requests are themselves encrypted by the VPN, which is simpler to configure and usually has lower latency.
In Filters → DNS blocklists, enable the default lists, then gradually add rules based on actual false positives. More filter lists is not better:
- Multiple lists overlap heavily, increasing memory use and update time;
- Overly aggressive lists may break logins, payments and CAPTCHAs;
- Remove list URLs promptly when they become invalid;
- After updates, check hit rates and false positives; do not optimize based only on "number blocked."
If a site cannot log in, first find the blocked domain in Query log, then add a single allow rule. Do not disable all filters just to restore one page.
Common rule operations:
- Allow a domain: add @@||example.com^;
- Block a domain: add ||ads.example.com^;
- Apply only to a specific client: use client-associated rules or settings;
- Temporary verification: pause filtering for a short while, confirm, then resume.
Rule syntax and third-party lists change; read the source description before adding. Do not copy an unknown "universal rule pack" directly to all devices.
In the client configuration, point DNS to the VPS's WireGuard address:
[Interface]
Address = 10.66.66.2/24
DNS = 10.66.66.1
The server-side WireGuard configuration needs to allow clients to access 10.66.66.1/32 or the corresponding private subnet. First confirm the VPN works, then test DNS:
ping 10.66.66.1
nslookup example.com 10.66.66.1
dig @10.66.66.1 example.com
If ping works but dig does not, focus on Docker port mapping, the firewall and AdGuard Home's listening status.
You can configure AdGuard Home's private address as the unified DNS, but confirm that the client's MagicDNS, DNS priority and exit node rules do not override it. For Headscale DNS configuration, ACL and routing issues, see the VPS Headscale setup tutorial.
If the home router can connect to the VPS through WireGuard, you can point the DNS handed out by DHCP to the home-side VPN address. Different routers vary greatly in support for "forcing all client DNS to a specified address"; first use one test device to confirm the server address in nslookup.
The router itself may continue handing out IPv6 DNS to clients, causing some queries to bypass AdGuard Home. When enabling IPv6, you must check RA, DHCPv6 and the client's actual DNS list at the same time.
In Filters or Clients, set names and groups for devices, for example:
- Family members' phones;
- TVs and game consoles;
- Home servers;
- Work devices;
- Temporary guests.
Client names make it easier to troubleshoot false positives than looking at IPs alone, but query logs may contain visited domains, times and device information. Suggestions:
- Shorten query log and statistics retention;
- Do not expose logs to people who do not need them;
- Before backing up configuration, assess whether logs contain private data;
- When sharing with family members, explain the scope of logging in advance;
- Do not use query logs to infer a person's entire behavior.
DNS rewrites let private-network services use stable domain names, for example:
grafana.home.example.com → 10.66.66.10
nas.home.example.com → 10.66.66.20
This way clients can use the same set of domain names whether at home or through the VPN. Note: DNS rewrites only change resolution results and do not automatically provide HTTPS certificates. Internal domains still need trusted certificates, application configuration or a client trust chain.
If the domain is hosted on Cloudflare, do not casually change management records to public addresses just for private-network resolution. You can use a separate internal subdomain, or restrict rewrite rules to specified clients.
First distinguish two kinds of encryption:
- AdGuard Home to upstream DNS DoH/DoT: protects queries between the VPS and the upstream resolver;
- Client to AdGuard Home DoH/DoT: lets browsers or phones access your DNS over an encrypted protocol.
The first is usually worth enabling. The second should only be considered when clients cannot use a VPN, the network hijacks port 53, or you really need HTTPS/TLS DNS.
If you want to provide client DoH/DoT:
- Apply for a dedicated domain and a valid certificate;
- Provide access only to controlled devices; do not run a public recursive service;
- Use NPM, Caddy, Cloudflare Access or a VPN to restrict sources;
- Set connection, traffic and logging policies for /dns-query and port 853;
- Test certificates, SNI, IPv6 and actual behavior on mobile networks;
- Close public entry points for admin APIs and web pages you do not need.
For personal home scenarios, WireGuard + ordinary DNS is already enough. Do not open 53, 443, 853, 3000 and 5443 all at once just to "support DoH."
On the client, check the system DNS, VPN DNS and the browser's secure DNS settings separately. You can test from the command line like this:
dig @10.66.66.1 example.com
resolvectl status
You should see the client sending requests to 10.66.66.1, not directly to the router, ISP DNS or the browser's built-in DoH.
If the browser has its own Secure DNS enabled, it may bypass system DNS. Either disable the browser's independent DoH so all requests go through AdGuard Home, or explicitly configure the browser to use your own DoH and accept that its policy differs from system DNS.
AdGuard Home's key content is in two directories:
- /opt/adguardhome/conf: configuration, users, listening and filtering settings;
- /opt/adguardhome/work: runtime data, filter caches, statistics and query logs.
Pause the container before backing up to reduce inconsistency during writes:
cd /opt/adguardhome
docker compose stop
sudo tar -czf adguardhome-$(date +%F).tar.gz work conf compose.yaml
docker compose start
Copy the archive to another machine or object storage, and restrict permissions:
chmod 600 adguardhome-*.tar.gz
If you do not need to restore query logs, you can back up only conf and the Compose file. Before restoring, stop the container, confirm directory ownership and version compatibility, then extract and overwrite.
Before upgrading, record the current version and back up:
cd /opt/adguardhome
docker inspect adguardhome --format '{{.Config.Image}}'
docker compose config > compose.before-upgrade.yaml
Then change the image version:
image: adguard/adguardhome:v0.107.78
Pull and rebuild:
docker compose pull
docker compose up -d
docker compose logs --tail=150 adguardhome
After upgrading, test:
- Whether VPN clients can resolve domain names;
- Whether filter rules update normally;
- Whether local DNS rewrites still take effect;
- Whether upstream DoH/DoT has errors;
- Whether query logs and admin login work normally.
If a regression occurs, first stop the container, restore the backup, then change the image back to the previous pinned version. Do not roll back only the image while ignoring configuration format changes.
Compose binds to 10.66.66.1, but the host currently does not have this address. Check:
ip -4 addr show wg0
docker compose ps
Confirm WireGuard starts first, or replace the address in Compose with the actual private IP. Do not change it to 0.0.0.0 as a first reaction; that may directly expose public DNS.
Check the process:
sudo ss -lntup | grep ':53'
sudo lsof -nP -iTCP:53 -iUDP:53
Common occupiers include systemd-resolved, dnsmasq, BIND, Unbound and another container. First decide which service is responsible for DNS, then handle the listening address; do not run two resolvers that both listen on all addresses by default.
Test layer by layer:
ping 10.66.66.1
nc -vz 10.66.66.1 53
dig @10.66.66.1 example.com
docker exec adguardhome cat /opt/adguardhome/conf/AdGuardHome.yaml | sed -n '1,100p'
If ping fails, check WireGuard routing first; if ping works but 53 does not, check Docker mapping and the firewall; if 53 responds but domain names fail, check upstream DNS and filter logs.
SERVFAIL does not mean "the domain was blocked." Focus on checking:
- The upstream DoH/DoT domain name cannot be resolved through Bootstrap DNS;
- The VPS system time is wrong, causing TLS verification failure;
- IPv6 upstream reachability is abnormal;
- DNSSEC or the upstream service has a temporary failure;
- The filter falsely blocked a dependency domain.
First test upstreams in AdGuard Home's DNS settings, then verify separately with an ordinary domain name and a known filtered domain name.
Open Query log and filter blocked requests by client and time. Add only the necessary allowlist rules, and record the reason after verification. Do not whitelist an entire top-level domain unless you confirm it is only used for trusted services.
Check the VPS disk, filter URLs, system time and outbound network:
df -h
docker compose logs --tail=200 adguardhome
Shorten query log, statistics and rule retention periods, and delete invalid lists. Do not use ever-growing logs to judge whether the service is healthy.
Check router RA, DHCPv6 and the client's DNS list:
resolvectl status
ip -6 route
If the client gets a public IPv6 DNS while the VPN only configures IPv4 DNS, it may prefer IPv6 and bypass filtering. Either complete the IPv6 VPN/DNS configuration, or explicitly disable the wrong IPv6 DNS handout on the router side.
- Do not expose recursive DNS 53 to the public internet unless you already have strict source control and abuse monitoring;
- Bind 53 only to WireGuard, Tailscale or other private addresses;
- Admin ports such as 3000 and 8080 listen only on loopback or the VPN;
- Do not enable DHCP, DoT, DoH and diagnostic ports you do not need;
- Admin passwords are separate, long enough, and not written into public repositories;
- Set reasonable retention periods for query logs and statistics;
- Use only trusted sources for filter lists, and regularly check false positives;
- Restrict permissions on configuration directories and backup files;
- Pin versions before upgrading and test recovery;
- Configure health checks and a backup resolution path for the DNS service;
- Do not treat AdGuard Home as a firewall, antivirus or complete parental control system;
- Only manage devices and networks you have the right to manage.
As long as the DNS port is bound to the VPN private IP, the host can still use systemd-resolved or another local resolver. Do not change the host's /etc/resolv.conf directly to an AdGuard Home address that has not started yet.
No. It handles DNS queries and filtering, not transport-layer encryption, routing, authentication or access control. Remote clients still need WireGuard, Tailscale, Headscale or another secure channel.
Not necessarily. The main value of DoH/DoT is protecting the transport from client to resolver; extra TLS, HTTP or connection reuse may change latency. Ordinary DNS inside a VPN already has an encrypted tunnel, so decide based on actual speed test results.
Many ads and business content use the same domain name, or are served by in-app, encrypted and hardcoded requests. The DNS layer cannot distinguish different paths under the same domain, nor can it replace application-layer filtering.
For personal and home scenarios, you can start with a 1-core 1GB VPS, a WireGuard private network and AdGuard Home v0.107.78. First connect only one test phone, confirm VPN, DNS, filters, upstream DoH and IPv6 behavior, then switch other devices over.
Do not treat "number blocked" as the only goal. A truly stable AdGuard Home solution is: DNS open only to controlled networks, a moderate number of rules, controllable query logs, a backup resolution path, and configuration and recovery steps written down.