New releases
Recipe version compared with the latest known upstream release. A version number, not a reading of what changed.
AdGuard Home replaces the router's DNS resolver and filters ads, trackers, and malicious domains for every device on the network at once, including TVs and phones where no blocker can be installed. One container, a web panel with statistics and a query log, plus DNS-over-HTTPS and DNS-over-TLS upstreams.
AdGuard Home is a DNS server that sits between the devices on your network and the internet and drops requests to advertising, tracking, and malicious domains. Because filtering happens at the network level, it covers TVs, set-top boxes, smart speakers, and guest devices where a browser extension cannot be installed.
Beyond filtering it works as an ordinary resolver: it supports DNS-over-HTTPS and DNS-over-TLS upstreams, caches answers, keeps a query log, and shows per-client statistics. Individual rules and schedules can be assigned to specific devices.
The recipe runs a single container with two Docker volumes and the control panel
on 127.0.0.1:3000. The built-in AdGuard Home DHCP server is not included: it
requires host networking or macvlan, which is incompatible with the isolated
Compose network.
The container runs without privileged, host networking, or the Docker socket,
and with no-new-privileges. The control panel is published on 127.0.0.1
only — a DNS administrator sees the browsing history of the entire network, and
the panel must never be exposed without HTTPS and authentication.
The main risk is specific to DNS: a server listening on port 53 on a public
interface becomes an open resolver and gets abused in DNS amplification attacks.
That is why ADGUARD_DNS_BIND defaults to 127.0.0.1, and the guide tells you
to set a specific LAN interface address rather than 0.0.0.0. On a VPS, DNS
access should be granted over a VPN only.
The second risk concerns the data itself: the query log stores the domains every device on the network opens. Retention and client anonymization are configured in the query log settings.
These are stored results of separate checks, not real-time data. An unknown status means the check has not been performed yet.
Recipe version compared with the latest known upstream release. A version number, not a reading of what changed.
Result of a dedicated Docker image scan, not a Compose configuration check.
A new major version is found by comparing version numbers. A compatibility verdict is entered by hand after reading the release notes and the migration steps.
Upstream Compose changes relative to the verified recipe.
Pinned image tags checked for newer versions.
From observation to action
The recipe already pins the latest known release. The steps below show the evidence retained for that version.
v0.107.79v0.107.79Open this release on GitHub ↗ The link is evidence to review, not proof of compatibility.
The version numbers do not cross a major compatibility boundary. No compatibility verdict has been recorded.
No image-tag change is currently planned.
Change the tag and restart. Release-specific migration steps still come from the release notes and installation guide.
No version change is currently planned. The recipe includes a backup procedure. Restore has been tested.
An exact Fully verified check for this target was recorded on 2026-08-31.
The status shows how fully the published recipe meets each level's requirements.
A minimal setup for a local network or access through a VPN.
The recipe covers every requirement at this level.
A public deployment with protected access and backups.
You still need to add: domain, https via reverse proxy, off-server backup.
A setup designed for predictable operations and observability.
You still need to add: separate database, external monitoring, cpu and memory limits.
Tailored to your server
Answer five questions and download a ready-to-run AdGuard Home bundle.
Screenshots have not been added yet.
Read the full guide first: it explains external access, backups, and data locations.
cp .env.example .envdocker compose up -ddocker compose psMinimum: 1 CPU, 256 MB RAM, and 2 GB of disk. 512 MB RAM is recommended — usage grows with the cache, the query log, and the number of blocklists. You need Ubuntu 22.04+ or Debian 12+ with Docker Engine and Docker Compose v2.24+. No separate database is required.
docker --version
docker compose version
On Ubuntu and Debian port 53 is almost always held by systemd-resolved. Check:
sudo ss -lunp | grep ':53 '
If the output mentions systemd-resolve, disable its stub listener but keep the
service running — otherwise the server itself loses name resolution:
sudo mkdir -p /etc/systemd/resolved.conf.d
printf '[Resolve]\nDNSStubListener=no\n' | sudo tee /etc/systemd/resolved.conf.d/adguardhome.conf
sudo ln -sf /run/systemd/resolve/resolv.conf /etc/resolv.conf
sudo systemctl restart systemd-resolved
sudo ss -lunp | grep ':53 ' || echo "port 53 is free"
Pointing at /run/systemd/resolve/resolv.conf leaves the host with working
upstream servers. Do not point /etc/resolv.conf at AdGuard Home itself before
it is running, or the server will be left without DNS.
Put compose.yaml, .env.example, backup.sh, restore.sh, and the proxy
directory into a dedicated directory:
mkdir -p ~/services/adguard-home
cd ~/services/adguard-home
cp .env.example .env
chmod 600 .env
.env defines:
ADGUARD_VERSION — the pinned image tag, including the v prefix;ADGUARD_DNS_BIND — the address the DNS ports are published on, 127.0.0.1 by default;ADGUARD_DNS_PORT — the published DNS port, normally 53;ADGUARD_WEB_PORT — the local control panel port, 3000 by default;ADGUARD_CONF_VOLUME — the volume holding AdGuardHome.yaml: users, upstreams, rules;ADGUARD_WORK_VOLUME — the volume holding statistics, the query log, and downloaded blocklists;TZ — the time zone, which drives charts and schedules.All user data lives in those two volumes only. Nothing is stored in the recipe directory.
docker compose pull
docker compose up -d
docker compose ps
With the defaults, no port faces the outside world: both DNS and the panel are
bound to 127.0.0.1. On a VPS it should stay that way — grant resolver access
over a VPN (WireGuard, Tailscale) by setting ADGUARD_DNS_BIND to the VPN
interface address. Never open port 53 on a public address: the server would
become an open resolver and be abused for DNS amplification.
The panel is local only, so forward the port over SSH:
ssh -L 3000:127.0.0.1:3000 user@server.example
Open http://localhost:3000. In the wizard keep the web interface on port
3000 and DNS on port 53 — those match the in-container ports from
compose.yaml. Choosing different ones makes the healthcheck and the port
mapping disagree with the configuration. Set an administrator name and a long
password, and store it in a password manager.
After the wizard, verify the server works:
docker compose exec adguard-home nslookup example.org 127.0.0.1
For the devices on your network to use filtering, DNS must listen on the LAN
interface. Set the server address in .env and recreate the container:
sed -i 's/^ADGUARD_DNS_BIND=.*/ADGUARD_DNS_BIND=192.168.1.10/' .env
docker compose up -d
Do not use 0.0.0.0: on a VPS it exposes the resolver to the whole internet.
Close port 53 on the external interface in the firewall and allow it from the
local subnet only:
sudo ufw allow from 192.168.1.0/24 to any port 53 proto udp
sudo ufw allow from 192.168.1.0/24 to any port 53 proto tcp
Then set the server address as the DNS server in your router, which enables
filtering for every device at once. The control panel stays on 127.0.0.1.
A reverse proxy publishes the control panel only: DNS speaks its own protocol
and does not pass through an HTTP proxy. Ready-made samples live in
proxy/Caddyfile, proxy/nginx.conf, and proxy/traefik.yaml; replace
dns.example.com with your domain. Caddy obtains a certificate automatically,
the Nginx sample assumes a Certbot certificate, and Traefik uses the
letsencrypt resolver. For Traefik in a container, replace 127.0.0.1 with a
host gateway address the container can reach.
The panel exposes the query history of the whole network, so restrict the domain further by IP or with basic authentication on the proxy side. This recipe does not configure AdGuard Home’s own DNS-over-HTTPS: that needs port 443 published separately and a certificate handed to the server.
Data lives in the adguard-home-conf and adguard-home-work volumes. The
script archives both at once — configuration restored without statistics ends up
inconsistent:
chmod +x backup.sh restore.sh
./backup.sh
The archive appears in ./backups. It contains AdGuardHome.yaml with the
administrator login and password hash, so keep a copy off the server and in a
protected place.
Restoring completely replaces the contents of both volumes with the chosen archive:
./restore.sh ./backups/adguard-home-YYYYMMDDTHHMMSSZ.tar.gz
docker compose ps
Before unpacking, the script creates a safety copy of the current data.
Disable the built-in auto-update in the panel — the version is set by the image
tag. Create a backup, read the release notes, change ADGUARD_VERSION in .env
(keep the v prefix), then run:
./backup.sh
docker compose pull
docker compose up -d
docker compose ps
docker compose logs --tail=100 adguard-home
Restore the previous ADGUARD_VERSION in .env and run docker compose pull
and docker compose up -d. AdGuard Home keeps its settings in
AdGuardHome.yaml and rewrites the file into its own version’s format on start,
so after a rollback the older binary may fail to read the upgraded config. In
that case restore the archive made before the update:
./restore.sh ./backups/adguard-home-YYYYMMDDTHHMMSSZ.tar.gz
The application has no separate database; migrations are limited to that file.
Keep the data: docker compose down. Remove the container and all data
irreversibly:
docker compose down
docker volume rm adguard-home-conf adguard-home-work
rm -rf ~/services/adguard-home
Remember to restore the previous DNS server in the router and on your devices, or the network will be left without name resolution. If you disabled the stub listener, bring it back:
sudo rm /etc/systemd/resolved.conf.d/adguardhome.conf
sudo systemctl restart systemd-resolved
Sources: Docker installation, post-install setup, and configuration.
The port is held by systemd-resolved or another resolver:
sudo ss -lunp | grep ':53 '
docker compose logs --tail=100 adguard-home
Disable the stub listener as described in step 2 of the guide. If another
container holds the port, find it with docker ps — two DNS servers cannot
share one address.
The healthcheck queries the panel on port 3000 inside the container:
docker compose ps
docker inspect --format '{{json .State.Health}}' "$(docker compose ps -q adguard-home)"
The most common cause is a different web interface port chosen in the setup
wizard. Set it back to 3000 in AdGuardHome.yaml (the http.address key), or
adjust the healthcheck and the port mapping in compose.yaml to match.
Check which address the port is published on:
docker compose port adguard-home 53/udp
An answer starting with 127.0.0.1 means ADGUARD_DNS_BIND was never changed.
Set the server’s LAN interface address and run docker compose up -d. Then test
from a client:
nslookup example.org 192.168.1.10
An empty answer while the server is running usually means the firewall: port 53 is needed over both UDP and TCP.
Make sure the device really goes through the server: its domains should appear in the query log. Many browsers and phones use their own DNS-over-HTTPS, bypassing the system settings — turn it off in the browser or block it with a router rule. Ads served from the same domain as the content (a social media feed, for example) cannot be removed by DNS filtering at all.
Find the domain in the query log, open the entry, and click unblock — an
@@||domain^ rule goes into your custom exceptions. You can also check which
rule matched without making a query:
curl -u admin:password 'http://127.0.0.1:3000/control/filtering/check_host?name=example.org'
The container did not find its configuration — nearly always a different volume:
docker volume inspect adguard-home-conf
docker compose config
Do not run the wizard again before you check the data path: a fresh install
overwrites AdGuardHome.yaml. If the volume is gone, restore an archive with
./restore.sh.
There is no reset command: stop the container, open AdGuardHome.yaml in the
adguard-home-conf volume, replace the hash in the users block with a new
bcrypt hash, and start the container. Make a backup before editing.
Fossary is an independent informational catalog and is not affiliated with the developers of the listed applications. We collect information from public sources and verify published recipes, but we do not develop, distribute, or control these applications.
A listing or validation status is not a requirement, endorsement, or personal recommendation to use an application. You decide whether it is suitable and use it at your own risk. We do not warrant an application's security, availability, or fitness for purpose and, to the extent permitted by law, are not liable for resulting loss or damage. Review its license, security settings, data processing, and backup procedures before installation.