30 min read

How to Self-Host AdGuard Home and Block Ads Across Your Entire Network

Self-host AdGuard Home with Docker Compose for network-wide ad blocking on every device. No per-device installs, no port forwarding, remote dashboard access with Localtonet.

๐Ÿ›ก๏ธ AdGuard Home ยท DNS Filtering ยท Docker ยท Localtonet ยท 2026

Run a private DNS filtering service for your home or small-office LAN

AdGuard Home filters DNS requests for phones, computers, smart TVs, consoles, and other devices that use it as their resolver. This tutorial covers network planning, Docker Compose deployment, safe port bindings, router and IPv6 configuration, verification, maintenance, and troubleshooting. DNS is published only on a deliberately selected private IPv4 address, subject to your network access policy. For optional remote administration, we also show how to tunnel the loopback-bound dashboard with a Localtonet client running on the same host.

๐Ÿšซ DNS-level domain filtering ๐Ÿ”’ Explicit private DNS binding ๐ŸŒ Optional remote administration
Home devices send DNS requests through a local AdGuard Home server, which blocks advertising and tracking domains.
Filtering applies to devices that actually use AdGuard Home as their resolver.

How AdGuard Home DNS filtering works

DNS translates names such as example.com into IP addresses. In this design, your router advertises AdGuard Home as the DNS resolver for LAN clients. Clients send queries to TCP or UDP port 53 on the server. AdGuard Home checks its filtering rules and either blocks a request or resolves it using its cache or configured upstream resolvers.

A blocked query does not always produce an empty response. Depending on the blocking mode and record type, it might return a null address, NXDOMAIN, REFUSED, or another configured answer. Inspect the query log to confirm that filtering caused the result and identify the matching rule.

๐Ÿ One resolver for many devicesDevices accepting your router's DNS settings can receive filtering without a browser extension on every device.
๐Ÿ“‹Query visibilityLogs show requested domains, client information, filtering decisions, and matching rules.
โšกDNS cachingCached answers can satisfy repeated lookups locally while their cache lifetime remains valid.
๐Ÿ”Encrypted upstream DNSCompatible upstream resolvers can receive queries over DNS-over-HTTPS or DNS-over-TLS.
๐ŸงญLocal DNS controlCustom rules and DNS rewrites support narrowly scoped exceptions and local naming.
๐Ÿ”ŽPer-client diagnosisClient identification helps investigate broken services and unexpected resolver behavior.

What DNS filtering cannot do

DNS filtering operates on domain names, not individual page elements or URL paths. It cannot reliably remove advertisements served from the same domain as desired content without blocking that content too. Cosmetic selectors and scriptlets belong in a browser content blocker, which can complement DNS filtering.

Network-wide filtering also depends on resolver selection. Hardcoded DNS, browser DNS-over-HTTPS, Android Private DNS, VPN-provided resolvers, and external IPv6 resolver advertisements can bypass AdGuard Home. Verify the clients rather than assuming an IPv4 router setting controls every request.

Encrypted upstream DNS has a limited privacy boundary

DNS-over-HTTPS and DNS-over-TLS encrypt the hop between AdGuard Home and the upstream resolver. They do not conceal every subsequent connection. Destination addresses, timing, and traffic volume may remain observable, and the upstream resolver receives the queries. HTTPS provides separate protection for application content.

Prepare the host and recovery access

This deployment uses a Linux Docker host on a trusted private LAN, Docker's ordinary bridge networking, and an IPv4 DNS listener. It does not configure AdGuard Home's DHCP server, host networking, public encrypted DNS endpoints, or native IPv6 DNS service.

RequirementPrepare before deploymentWhy it matters
Always-available Linux hostA platform supported by the current official container image, reliable storage, power, and networkingSleeping or unavailable hosts interrupt DNS.
Docker Engine and ComposeA maintained Engine release and Compose pluginThe examples use docker compose and bind-mounted storage.
Administrative accessDocker permissions, SSH access, and authority to manage network policyResolver and firewall changes can affect the entire LAN.
Stable private IPv4 addressA DHCP reservation or properly planned static addressThe address is used in both port bindings and DHCP.
Router administrationLAN DHCP, IPv6 DNS advertisement, and inter-network access settingsResolver distribution and source restrictions are separate tasks.
Identified firewall stackDocker backend, host firewall manager, and upstream network policyPublished container ports do not necessarily follow ordinary host-input rules.
Backup and recovery pathProtected storage and one administrative device with working DNSYou need access even if the new resolver fails.
Optional Localtonet accessOur client installed directly on the AdGuard Home hostOnly that host can reach its own loopback-bound dashboard.

If Docker is not installed, use the distribution-specific instructions in the Docker Engine installation guide and install the Compose plugin. Package repositories and installation commands differ by distribution, so this guide begins with a working Docker installation rather than prescribing one universal package command.

docker --version
docker compose version
ip -4 route
ip -4 address
ip -6 address

Record the default gateway, current resolvers, DHCP lease range, and existing IPv6 configuration. Save the router's previous DNS settings. Keep a recovery device that can administer the router without depending on AdGuard Home. Commands using SERVER_LAN_IP below require substitution with your actual private IPv4 address.

Plan the DNS address and enforce the access boundary

Use an explicit private address, not a wildcard publication

Reserve the host's address in DHCP, or choose a static address outside the dynamic pool while preserving the correct gateway and subnet. The Compose example requires ADGUARD_LAN_IPV4 and publishes DNS only at that address. It deliberately avoids "53:53", which publishes on all host addresses by default.

An address-specific mapping limits the destination address; it is not a source-subnet allowlist or an ingress-interface firewall rule. Another network that can route to that address may still reach the published service. Your policy must allow TCP and UDP port 53 from trusted client subnets and deny them from guest networks, untrusted VLANs, VPN peers without permission, and public paths.

Complete the firewall procedure before starting the container

Identify your actual stack and follow Docker's packet filtering and firewall documentation before running docker compose up -d. A generic UFW allow/deny rule is not sufficient evidence: Docker-published traffic can be diverted before UFW's normal input processing. Do not combine unrelated iptables, nftables, or firewalld recipes.

Use the following deployment gate:

  1. Identify the backend: inspect the Docker daemon configuration and the firewall system managing this host. Determine whether Docker uses its iptables or nftables backend.
  2. Implement source restrictions: for the iptables backend, follow Docker's DOCKER-USER filtering procedure. Published traffic has already undergone destination translation there, so rules matching the original host address or port require the documented connection-tracking approach. For the nftables backend, follow Docker's nftables firewall procedure; do not assume a DOCKER-USER chain exists.
  3. Make policy persistent: use the mechanism appropriate to the identified firewall manager, preserving administrative access and existing container rules. Verify behavior after a firewall reload and host reboot.
  4. Check the network edge: remove any DNS port forwarding, and restrict inter-VLAN or guest-network routes to the server. Never disable Docker firewall management as a shortcut.
  5. Define acceptance tests: an approved LAN client must resolve over both UDP and TCP. A client on each relevant untrusted path must not obtain an answer.

This article does not supply interchangeable firewall commands because the installed backend and existing policies are not known. Completing the authoritative procedure for your identified stack is a prerequisite, not an optional hardening task. If you cannot identify or manage that stack, stop before deployment and obtain help from the network administrator.

Directly public and multi-homed hosts need additional review

Do not substitute a public address into the private binding. On hosts with public interfaces, multiple NICs, routed private networks, custom Docker routing, or globally reachable IPv6 addresses, a private destination binding alone is not proof of isolation. Review forwarding and direct-routing behavior, apply source restrictions, and test all reachable paths. Use a dedicated private LAN host if you cannot establish those boundaries.

Treat IPv4 and IPv6 separately

The supplied configuration publishes DNS over IPv4 only. It contains no [::]:53 or other IPv6 publication. IPv4 DNS can still answer AAAA queries, so clients can resolve IPv6 destinations through this resolver without an IPv6 DNS listener.

Review router advertisements and DHCPv6 independently. Ensure they do not advertise an unintended external resolver. Do not advertise the host's IPv6 address as DNS unless you have separately configured and tested an IPv6 listener, Docker networking, stable addressing, and IPv6 firewall rules. IPv4 restrictions do not automatically protect IPv6. Do not casually disable IPv6 on a network that depends on it.

A public secondary resolver is a bypass, not filtered redundancy

Clients may use either advertised DNS server during normal operation. A public secondary can therefore bypass filtering. For redundancy, deploy a second filtering resolver with equivalent rules and upstream settings, and test it independently. With one server, accept the outage risk and retain a documented procedure for restoring the router's previous DNS values.

Inspect port 53 before changing the host resolver

Do not assume Ubuntu, Debian, or Raspberry Pi OS always uses the same resolver. Inspect the actual listener and how /etc/resolv.conf is managed:

sudo ss -lntup | grep -E '(:53[[:space:]]|:53$)' || true
systemctl is-active systemd-resolved || true
readlink -f /etc/resolv.conf
cat /etc/resolv.conf
resolvectl status 2>/dev/null || true

A listener at 127.0.0.53:53 normally need not conflict with publication at a different, explicit LAN address. Leave it alone unless a real binding conflict occurs. A wildcard listener or another service on the chosen LAN address requires a service-specific decision. Check dnsmasq, Unbound, BIND, and existing containers before disabling anything.

Conditional systemd-resolved procedure

Only use this procedure if the stub actually conflicts and /run/systemd/resolve/resolv.conf contains usable upstream resolvers. The resolved.conf documentation explains DNSStubListener; the systemd-resolved service documentation explains resolver-file modes. Using the non-stub file can change split-DNS behavior, especially on VPN-connected hosts.

First preserve the original file or symbolic link. Do not overwrite an existing recovery backup or an existing override named adguardhome.conf.

sudo cp -a /etc/resolv.conf /etc/resolv.conf.pre-adguard
cat /run/systemd/resolve/resolv.conf

Continue only after confirming that file contains appropriate upstream entries and your backup is intact:

sudo mkdir -p /etc/systemd/resolved.conf.d
sudo tee /etc/systemd/resolved.conf.d/adguardhome.conf <<'EOF'
[Resolve]
DNSStubListener=no
EOF

sudo rm -f /etc/resolv.conf
sudo ln -s /run/systemd/resolve/resolv.conf /etc/resolv.conf
sudo systemctl restart systemd-resolved
getent hosts example.com
sudo ss -lntup | grep -E '(:53[[:space:]]|:53$)' || true

This disables the stub, not the entire service. It does not prematurely make the host depend on AdGuard Home at 127.0.0.1, where this tutorial does not publish DNS.

Restore immediately if host resolution fails

The following rollback assumes the backup and override were created exactly as above. If you modified an existing override, restore its original content instead of deleting it. Stop AdGuard Home first if it now occupies an address required by the restored resolver.

sudo rm -f /etc/systemd/resolved.conf.d/adguardhome.conf
sudo rm -f /etc/resolv.conf
sudo cp -a /etc/resolv.conf.pre-adguard /etc/resolv.conf
sudo systemctl restart systemd-resolved
getent hosts example.com

Install AdGuard Home with Docker Compose

AdGuard Home container connected to LAN DNS traffic and persistent configuration and working-data storage.
This deployment uses host bind mounts for persistence and publishes DNS only at the chosen private IPv4 address.

The official AdGuard Home Docker guide documents the adguard/adguardhome image, persistent directories, initial setup on port 3000, and container update workflow. The Compose port reference explains host-address bindings. We map the ongoing administration service from container port 80 to host loopback port 8080.

1

Create persistent project directories

Create separate configuration and working-data directories on reliable storage.

2

Set the private address and create Compose configuration

Require an explicit IPv4 address for DNS. Keep both web mappings on loopback.

3

Validate policy and start

Complete the firewall gate, inspect the rendered configuration, start the container, and review its logs.

4

Complete onboarding locally or through SSH

Configure container-side listeners and create a strong administrator credential.

5

Remove the setup publication

Once the dashboard works, remove the port 3000 mapping and recreate the service.

mkdir -p adguard/work adguard/conf
cd adguard

Create .env in this directory. Replace the example address with the private IPv4 address already assigned to this host:

ADGUARD_LAN_IPV4=192.168.1.10

Create compose.yaml:

services:
  adguardhome:
    image: adguard/adguardhome:latest
    container_name: adguardhome
    restart: unless-stopped
    volumes:
      - ./work:/opt/adguardhome/work
      - ./conf:/opt/adguardhome/conf
      - /etc/localtime:/etc/localtime:ro
    ports:
      - "${ADGUARD_LAN_IPV4:?Set a private LAN IPv4 address}:53:53/tcp"
      - "${ADGUARD_LAN_IPV4:?Set a private LAN IPv4 address}:53:53/udp"
      - "127.0.0.1:3000:3000/tcp"
      - "127.0.0.1:8080:80/tcp"

The required-variable expression prevents an unset value from silently becoming a wildcard mapping. It does not validate that your chosen address is private or belongs to the host. Inspect the rendered configuration before starting:

docker compose config
# Continue only after completing the firewall prerequisite.
docker compose up -d
docker compose ps
docker compose logs --tail=100 adguardhome
Keep Docker current for loopback isolation

Docker documents that releases older than 28.0.0 could allow hosts on the same layer-2 segment to reach ports published to localhost. Use a maintained release and verify isolation rather than relying on the mapping alone. See Docker's port-publishing security notes.

Open the setup wizard

On the server, browse to http://127.0.0.1:3000. For a headless server, run this from your workstation and keep the SSH session open:

ssh -N -L 3000:127.0.0.1:3000 -L 8080:127.0.0.1:8080 your-user@SERVER_LAN_IP

Then open http://127.0.0.1:3000 on the workstation. The second forward provides access to the dashboard after onboarding. If either local port is occupied, select another workstation-side port and adjust the browser URL accordingly.

Configure DNS on container port 53 and administration on container port 80. Select interfaces that include the container's network interface, not only container loopback. The host's private LAN address is not an interface inside this bridge-networked container. Host publication supplies the external access boundary.

Create a unique administrator username and strong password, then test http://127.0.0.1:8080. The wizard may offer a link using its container-side port; use the mapped host URL instead. Remove the following line from compose.yaml after successful onboarding:

      - "127.0.0.1:3000:3000/tcp"
docker compose up -d
docker compose ps
docker port adguardhome
sudo ss -lntup | grep -E '(:53[[:space:]]|:8080[[:space:]]|:3000[[:space:]])' || true

Expect DNS at your chosen private address, dashboard access at 127.0.0.1:8080, and no published setup port. Docker may implement publication through NAT without a visible userspace listener, so ss is supplementary, not a complete exposure audit.

Choose upstream DNS without creating loops

In DNS settings, select upstream resolvers appropriate to your availability and privacy requirements. Use the available upstream test function, then issue direct queries and inspect logs. Encrypted upstream hostnames may need suitable bootstrap resolvers so they can be resolved before the encrypted connection is established.

Do not forward AdGuard Home to itself or to a router that forwards back to AdGuard Home. Such loops cause timeouts. Upstream logging, filtering policy, jurisdiction, and validation behavior remain separate choices from installing AdGuard Home.

Configure router DHCP and client DNS

Router DHCP distributes AdGuard Home as DNS, while a public secondary resolver provides a filtering bypass.
Advertise only intended filtering resolvers if consistent filtering is required.

Change DHCP only after direct UDP and TCP tests in the verification section succeed. Router interfaces vary: LAN DHCP DNS controls client resolver distribution, while WAN DNS may control only the router's own upstream behavior.

  1. Confirm the server owns its reserved address and no duplicate exists.
  2. Test direct DNS queries from an approved LAN client.
  3. Set the LAN DHCP DNS value to the server's private IPv4 address.
  4. Remove unintended public secondary DNS values.
  5. Review IPv6 router advertisements and DHCPv6 for external resolver bypasses.
  6. Renew a test client's lease, reconnect its network interface, or wait for renewal.
  7. Inspect the resolver actually received before rolling out to the remaining clients.
# Linux with systemd-resolved
resolvectl status

# Linux with NetworkManager
nmcli device show | grep -E 'IP4.DNS|IP6.DNS'

# macOS
scutil --dns

# Windows Command Prompt
ipconfig /all

Some routers advertise themselves and proxy requests to the configured resolver. That can work, but the query log may identify the router instead of individual clients. Verify the router's upstream path and ensure it does not form a forwarding loop.

Android Private DNS does not accept this plaintext DNS setup

Provider-hostname mode expects a DNS-over-TLS hostname with a valid TLS service, not the ordinary LAN IP used here. Let the phone obtain DNS through Wi-Fi DHCP and check whether an external Private DNS provider overrides it. Hosting DNS-over-TLS requires a separate hostname, certificate, reachability, and access-control design.

Choose DNS-compatible filters and narrow exceptions

Start with AdGuard Home's default DNS filter and the curated catalog in the installed dashboard. Review each list's scope, ownership, syntax, and maintenance status. More rules do not automatically mean better results.

The official AdGuard Home filtering syntax documentation explains supported DNS rules, including hosts-style entries and supported Adblock-style rules. Cosmetic selectors, scriptlets, and browser URL-path rules cannot be applied meaningfully to DNS requests.

SourceRecommended useQualification
AdGuard DNS filterA DNS-oriented starting point, available through the current catalogThis is the DNS filter project, not the general browser content-filter repository.
EasyList and EasyPrivacyUnderstand their browser-filtering purpose before considering any DNS useMaintained by the EasyList project, not the uBlock Origin team; browser rules are not automatically DNS-compatible.
OISDReview current variants and policies, preferably through the installed catalogSuitability and false-positive tolerance depend on your services; avoid obsolete copied feed URLs.
AdGuard Home catalogSelect current DNS-oriented entriesCheck descriptions and maintainer activity rather than enabling everything.
Custom rulesAddress specific needs identified in query logsKeep exceptions narrow and document their purpose.

Enable a small baseline, then observe. When an application breaks, reproduce the problem while watching the query log. Identify the hostname and matching rule, allow only what is necessary, and retest. Avoid broadly allowing a parent domain without evidence.

Add one list at a time

Overlapping lists complicate diagnosis without necessarily adding useful coverage. Authentication, consent, telemetry, and content-delivery dependencies can break under aggressive filtering. Record changes and review intentional exceptions periodically.

Verify the complete deployment

Client DNS test results matched with allowed and blocked requests in the AdGuard Home query log.
Correlate actual client requests with log entries instead of treating a successful page load as proof of filtering.

1. Inspect container state and publications

Run commands from the project directory. Confirm the service is running without repeated startup errors:

docker compose ps
docker compose logs --tail=100 adguardhome
docker port adguardhome
docker inspect adguardhome --format '{{json .NetworkSettings.Ports}}'
sudo ss -lntup | grep -E '(:53[[:space:]]|:8080[[:space:]])' || true

DNS publications must show only your intended private IPv4 address. No wildcard IPv4 or IPv6 DNS publication belongs in this example. Dashboard publication must remain 127.0.0.1:8080. Inspect Docker's actual firewall behavior alongside these outputs.

2. Test direct UDP and TCP DNS

From another approved LAN device with dig installed, run:

dig @SERVER_LAN_IP example.com A
dig +tcp @SERVER_LAN_IP example.com A
dig @SERVER_LAN_IP example.com AAAA

The first normally uses UDP; the second explicitly tests TCP. Both matter. The third demonstrates that IPv6 record lookup can work over an IPv4 DNS transport. Match requests to the query log.

3. Perform a controlled blocking test

Add this temporary custom rule in AdGuard Home:

||example.org^
dig @SERVER_LAN_IP example.org A

Confirm the query log identifies the temporary rule as the reason for blocking. The answer depends on the configured blocking mode. Remove the rule, repeat the query, and confirm it is allowed. This avoids relying on an advertising domain that might disappear from a list.

4. Verify DHCP and normal application traffic

Renew a client lease and inspect its active resolver values, including IPv6 entries. Open ordinary sites and applications, then find corresponding requests in the log. Missing entries can indicate cached answers, browser secure DNS, a VPN, manual resolver settings, or an external IPv6 resolver.

5. Test denied paths, not just allowed paths

From a guest VLAN or another untrusted network you control, attempt both UDP and TCP queries to the server address if a route exists. Neither should return a DNS answer. Where the host has a public interface, separately test that address from outside the LAN. For globally reachable IPv6 addresses, test IPv6 paths as well. A timeout alone can have several causes, so correlate results with firewall policy and counters where available.

If an untrusted client receives answers, stop the container and correct the policy before continuing. Repeat the checks after a reboot or firewall reload. An allowed LAN test alone cannot establish that the resolver is private.

6. Verify web isolation

On the host, http://127.0.0.1:8080 should work. From another LAN device, http://SERVER_LAN_IP:8080 should not. Port 3000 should no longer be published. If these expectations fail, inspect mappings, Docker version, reverse proxies, routing, and firewall rules.

Expose only the dashboard with Localtonet

Localtonet provides remote access to the AdGuard Home dashboard while DNS port 53 remains private.
Run our client on the AdGuard Home host so it can reach the dashboard through that host's loopback address.

With Localtonet, you can obtain a public HTTPS address for the dashboard without inbound router port forwarding, inbound firewall changes, VPN setup, or a public IP address. Our client establishes an outbound connection to a relay and forwards HTTP requests to 127.0.0.1:8080.

This requires the Localtonet client to run directly on the AdGuard Home host, in the same host network namespace. On another machine, 127.0.0.1 means that other machine. A normally isolated client container also has its own loopback interface. Neither can use this target to reach the host dashboard.

This tutorial intentionally retains the same-host design. An alternate-device design would require a separate private-address dashboard binding and a Docker-aware access policy permitting only the approved client device. It is not a drop-in substitution for this loopback setup.

A public dashboard still needs authentication

Use a strong, unique AdGuard Home password, keep the software updated, and treat logs and client names as sensitive. Protect the Localtonet device token. Never include it in screenshots or shared configuration. Stop the tunnel when remote administration is unnecessary.

1

Install and run our client on the server

Use the current OS-specific installation method provided by Localtonet. Verify the dashboard is reachable at http://127.0.0.1:8080 from that host before creating a tunnel.

2

Authenticate the correct device

Use the device-specific token for the client running on the AdGuard Home host. Keep it secret.

3

Select a current relay server

Choose an available server or region from the dashboard rather than copying a hardcoded server code.

4

Create the HTTP configuration

Target 127.0.0.1, port 8080. Choose an available Process Type: Random Sub Domain, Custom Sub Domain, or Custom Domain. Availability varies by configuration or plan; check current documentation before configuring custom-domain DNS.

5

Start and use the tunnel

Press Start. From outside the LAN, open the assigned HTTPS address, confirm the AdGuard Home login page appears, and verify authenticated dashboard access.

6

Stop or delete unused access

Stop the tunnel after the session if continuous access is unnecessary. Delete it when it is no longer part of your design.

The Localtonet HTTP tunnel documentation provides current options. OS-specific installation commands are not reproduced here because they vary and are not established for the chosen host. Creating a tunnel does not start it: the selected client must remain connected and the tunnel must be running. A reboot does not prove either condition has been restored.

This HTTP tunnel exposes only the dashboard, not DNS port 53. It is not a VPN and does not change client DNS settings. The private DNS firewall policy remains independently necessary.

Back up, update, recover, and monitor

Create a consistent backup

Back up conf, work, compose.yaml, and .env. Protect archives because they can contain credentials, client activity, and network information. Stopping the service produces a consistent maintenance backup but causes a DNS outage unless another filtering resolver is available.

Run from the project directory. Use a private archive permission mask and preserve file ownership through privileged tar:

BACKUP="../adguard-backup-$(date +%Y%m%d-%H%M%S).tar.gz"
docker compose stop
sudo sh -c 'umask 077; tar -czpf "$1" compose.yaml .env conf work' sh "$BACKUP"
docker compose start
docker compose ps
sudo tar -tzf "$BACKUP"

Inspect every command result. Restart the service even if archive creation fails, then correct the backup problem. Store a protected copy elsewhere and test restoration in an isolated environment without publishing a duplicate DNS service on your production address.

Record the running image before pulling

Follow the official container update procedure, not an in-container binary update. Review AdGuard Home release notes first. The example uses latest; a selected version or digest is preferable when you need deterministic deployments.

Record the image actually used by the running container, rather than assuming the current local latest tag still identifies it:

OLD_IMAGE_ID=$(docker inspect adguardhome --format '{{.Image}}')
docker image inspect "$OLD_IMAGE_ID" --format '{{json .RepoDigests}}'
ROLLBACK_TAG="adguardhome-local:rollback-$(date +%Y%m%d-%H%M%S)"
docker image tag "$OLD_IMAGE_ID" "$ROLLBACK_TAG"
printf '%s\n' "$ROLLBACK_TAG" > rollback-image.txt

Retain that tag without pruning it. Save the matching pre-update backup and image details together. For recovery onto a different host, retain a pullable digest or separately export the image; a local tag is not a remote registry copy.

docker compose pull
docker compose up -d
docker compose ps
docker compose logs --tail=100 adguardhome

Repeat UDP and TCP queries, a controlled filtering test, query-log checks, dashboard access, and denied-path tests after updating.

Roll back the image and its matching data

Do not assume an older image can read configuration modified by a newer release. During a maintenance window, stop the service, retain failed-state data for diagnosis, and restore the corresponding pre-update archive. Substitute the actual backup filename below:

docker compose stop
FAILED_DIR="../adguard-failed-$(date +%Y%m%d-%H%M%S)"
mkdir "$FAILED_DIR"
sudo mv conf work "$FAILED_DIR"/
sudo tar -xzpf ../adguard-backup-YYYYMMDD-HHMMSS.tar.gz -C .
cat rollback-image.txt

Edit the restored compose.yaml so its image: value uses the recorded rollback tag or preserved digest, not latest. Confirm the restored .env still contains the correct private address:

docker compose config
docker compose up -d --pull never
docker compose ps
docker compose logs --tail=100 adguardhome

The --pull never option requires the rollback image to exist locally. Verify that before beginning recovery. Restore ownership and permissions from the archive, then repeat the full verification sequence before re-advertising a repaired resolver.

Check reboot behavior and routine health

restart: unless-stopped requests restart when Docker starts, unless the container was intentionally stopped. It cannot repair missing storage, unavailable addresses, occupied ports, invalid configuration, or a failed Docker daemon.

docker compose ps
docker compose logs --tail=100 adguardhome
docker port adguardhome
getent hosts example.com

After a reboot, query from a separate client and retest firewall boundaries. The host's getent result tests its own resolver path, which may intentionally differ from AdGuard Home. If using Localtonet, verify client connection, tunnel state, and an external browser request.

  • Review logs and query trends after host or network changes.
  • Confirm filter updates and remove obsolete custom exceptions.
  • Monitor disk space for working data and backups.
  • Recheck IPv4 and IPv6 resolver distribution after router firmware updates.
  • Retest policy persistence after firewall reloads.
  • Review whether continuous public dashboard access is still justified.

Troubleshoot common failures

Port 53 is already in use

Inspect sudo ss -lntup and docker ps. Identify the exact address and owning process. A loopback stub may coexist with the private-address mapping; a wildcard listener may not. Resolve ownership deliberately rather than disabling every DNS service or repeatedly restarting Docker.

Docker cannot bind the chosen address

Check ip -4 address, .env, and docker compose config. The address must belong to this host and be available when the container starts. A DHCP reservation on the wrong interface or a changed subnet can invalidate it.

The host loses DNS before the container starts

Restore its saved resolver configuration and verify getent hosts example.com. Do not point it to 127.0.0.1: DNS is not published there in this design. Avoid a boot dependency where image downloads require a resolver that has not started yet.

LAN queries time out

Check the stable address, container logs, container-side DNS binding, both protocol publications, Docker-aware firewall rules, VLAN policy, and wireless client isolation. A successful host-local query does not prove another segment can reach the service.

Clients keep the old resolver or bypass filtering

Renew leases and distinguish WAN DNS settings from LAN DHCP settings. Inspect browser secure DNS, Android Private DNS, VPN profiles, endpoint-security software, manually configured resolvers, and IPv6 advertisements. Managed devices may enforce organizational DNS; change only settings you are authorized to manage.

A site or application breaks

Correlate the failure timestamp with blocked queries. Temporarily allow the narrowest required hostname, retest, and document the exception. If no related request is blocked, investigate application caching or a non-DNS cause rather than disabling filtering globally.

IPv6 clients bypass AdGuard Home

Inspect advertised IPv6 DNS values. An external IPv6 resolver can be selected even when IPv4 DHCP is correct. Correct advertisements for this IPv4-transport design, or separately engineer a protected IPv6 DNS service. Do not advertise an unconfigured IPv6 listener.

DNS fails intermittently or upstream queries loop

Check for an unfiltered secondary, inconsistent filtering servers, host suspension, address changes, and upstream timeouts. Trace every forwarding hop. AdGuard Home forwarding to the router while the router forwards back to AdGuard Home is a loop, not redundancy.

The dashboard is unavailable after onboarding

Confirm the wizard selected container port 80 and a non-loopback container interface. The external URL is http://127.0.0.1:8080, not container port 80. For SSH access, ensure the 8080 forward remains open.

The local dashboard works but the Localtonet URL does not

Verify our client runs directly on the same host, not another machine or an isolated container. Confirm local dashboard access, the correct device token, a connected client, an available relay, target 127.0.0.1:8080, and an explicitly started HTTP tunnel. Test from outside the LAN.

The server is unavailable and clients cannot resolve names

Use the recovery device to restore previous router DNS values and renew affected leases. Repair the host or restore a known-good backup, then test directly before distributing its address again. If availability is critical, deploy a second equivalent filtering resolver.

Frequently asked questions

Can AdGuard Home block every advertisement?

No. DNS filtering cannot selectively remove advertisements served from the same domain as desired content, apply cosmetic browser rules, or control clients using another resolver. A browser content blocker can complement it.

Can I use a Raspberry Pi?

Yes, when its architecture is supported by the current image and it has reliable storage, power, networking, and sufficient capacity. Inspect the installed OS rather than assuming every Raspberry Pi image has the same resolver configuration.

Does binding DNS to a private IP replace firewall rules?

No. It limits the destination address, not the permitted sources. Routed guests or other networks may still reach it. Apply Docker-aware source restrictions and verify both allowed and denied paths, including relevant IPv6 paths.

Should I configure a public secondary DNS server?

Not for consistent filtering. Clients may use either server normally. Use a second filtering resolver with equivalent settings for filtered redundancy.

Does DNS-over-HTTPS hide all browsing activity?

No. It protects the DNS hop to the upstream resolver. Destination addresses, timing, and traffic volume may remain visible, and the upstream receives the DNS queries.

Can I enter the server IP in Android Private DNS?

Not for this plaintext LAN DNS configuration. Provider-hostname mode requires a DNS-over-TLS hostname and valid service. Use Wi-Fi DHCP for the ordinary LAN resolver and check for external overrides.

Will a WireGuard client automatically use AdGuard Home?

No. Configure the resolver, routes to its address, and firewall permissions explicitly. Our self-hosted WireGuard guide explains route selection and remote connectivity.

Does the Localtonet tunnel expose DNS port 53?

No. This workflow creates an HTTP tunnel to the dashboard at 127.0.0.1:8080, using our client on the same host. DNS remains separately restricted by its private binding and access policy.

How do I update without losing settings?

Back up both persistent directories, Compose configuration, and address settings. Preserve the running image for rollback, review release notes, pull the intended image, recreate the container, and repeat verification. If rolling back across configuration changes, restore matching pre-update data as well as the image.

Manage your private dashboard with Localtonet

Keep DNS restricted to approved clients, then run our client on the AdGuard Home host to reach the loopback-bound dashboard remotely. Verify authentication and stop or delete the HTTP tunnel when administration is complete.

Get Started Free โ†’

Corrections & updates

Substantive changes approved by the Localtonet editorial team are listed transparently below.

Remove the outer <article> wrapper from Model.Body and make the hero the first component, relocating the lead diagram to an appropriate position after the hero and guide navigation. Revise the Compose and firewall section so DNS is demonstrably restricted to the intended trusted interface or subnets, including separate treatment of IPv4 and IPv6 and an explicit warning for directly public or multi-homed hosts. Provide a verifiable firewall path for the supported example or clearly require readers to follow the authoritative procedure

Rebuild the article using only approved lt-* components and no inline styles; shorten the hero so it does not repeat the page title; expand the clickable guide navigation; add prerequisites and network planning; explain DNS filtering limits, bypass paths, blocking responses, caching, and encrypted-upstream privacy accurately; make the systemd-resolved change conditional with verification and rollback; secure the Compose port mappings and setup interface; add stable-IP, DHCP, IPv4, IPv6, firewall, and redundancy guidance; replace inacc

Localtonet is a secure multi-protocol tunneling and proxy platform designed to expose localhost, devices, private services, and AI agents to the public internet supporting HTTP/HTTPS tunnels, TCP/UDP forwarding, mobile proxy infrastructure, file server publishing, latency-optimized game connectivity, and developer-ready AI agent endpoint exposure from a single unified control plane.

support