Self-HostingDocker

AdGuard Home with Docker: Block Ads and Trackers for Your Whole Home Network (Linux, Windows, Mac)

Before you start

  • 19 min read

Boxes marked Run in terminal are what you type; plain text explains why. Check what you need first

What you get in one minute#

AdGuard Home is a free, open-source DNS server that blocks ads and trackers for every device on your network, with nothing to install on the phones, TVs and laptops themselves.

AdGuard Home official logo

Logo: the official AdGuard Home project (github.com/AdguardTeam/AdGuardHome)

Every website, app and smart TV first asks DNS "where is this name?". AdGuard Home answers that question. If the name is on a blocklist, it says "blocked" and the ad or tracker never loads. If not, it asks a real DNS server for you. You point your router at it once and the whole house is covered.

At a glance#

  • What you get: network-wide ad and tracker blocking, a query log, per-device rules, encrypted upstream DNS (DoH/DoT) and your own home names like nas.home.arpa.
  • Time needed: about 20 minutes, plus a few minutes in your router.
  • Difficulty: easy on a Linux box, medium if port 53 is busy (Ubuntu) or you use Windows or Mac.
  • You need: Docker with Compose, a computer that stays on, and access to your router's DHCP/DNS settings.
WhatValue
Current versionv0.107.79, released 18 Aug 2026 (GitHub releases)
Pinned image (official, Docker Hub)adguard/adguardhome:v0.107.79 (about 28 MiB compressed on amd64)
Ports53 TCP and UDP (DNS), 3000 (first-run wizard only), 80 (web page)
Official docsadguard-dns.io/kb/adguard-home

Why this version? We checked the GitHub issue tracker for reports about v0.107.79. We found no install failures and no data-loss reports. A few narrow bugs are open (listed under Known bugs). So this guide uses the latest stable release. Your rollback tag is v0.107.78. Do not use the beta or edge tags for DNS your family depends on.

How it fits together#

Your router tells every device to use AdGuard Home for DNS. AdGuard Home checks each lookup against its blocklists, answers "blocked" or forwards it to an encrypted upstream DNS server, and keeps a log.

How AdGuard Home works: the router hands out the AdGuard Home address, a phone, PC or TV asks it where a website is on port 53, AdGuard Home checks its blocklists and either answers blocked or asks an encrypted upstream DNS server, and writes a query log

Two rules to remember:

  1. Port 53 is the star. DNS lives on port 53, so the machine that runs AdGuard Home must be able to own it.
  2. It must stay on. If this machine is off, nothing on your network can look up names.

Where should it run?#

Docker Desktop on Windows or Mac is fine for trying AdGuard Home. A DNS server for the whole house belongs on a Linux machine that is always on, or on the native program.

WhereGood forWhy
Linux (mini PC, Raspberry Pi, NAS, small server)The real thingAlways on, and host networking works, so AdGuard Home sees each device's real IP
Windows or Mac with Docker DesktopA test runThe docs say host networking "is not supported on Docker Desktop for Mac, Docker Desktop for Windows". Without it, all devices look like one Docker address, so per-device rules do not work. Laptops also sleep
Windows or Mac, native programA home DNS server on a PC that stays onThe official release archive installs as a service (see "The native option" below)

The first two rows come from the official Docker page. The "laptops sleep" point is our advice.

Before you start#

You need Docker, a fixed address for the machine and permission to change your router's DNS.

SystemWhat you need
LinuxDocker Engine and the Compose plugin. On Ubuntu, a short fix to free port 53 (Step 1)
WindowsDocker Desktop (WSL2 backend), 64-bit Windows 10 or 11. Test use only, see above
MacDocker Desktop (Apple silicon or Intel). Test use only, see above

Check your tools:

Run in terminalbash
docker --version
docker compose version

Checklist:

  • [ ] Port 53 (TCP and UDP) is free on the machine (Step 1 shows how to check)
  • [ ] The machine has a fixed IP (a DHCP reservation in your router is enough)
  • [ ] You know how to log in to your router
  • [ ] You backed up any DNS settings you are about to change

Tested on 2026-10-26: not run. Commands checked against the official AdGuard Home knowledge base (read 2026-10-26).

Step 1: Make sure port 53 is free#

Only one program can own port 53. On Ubuntu, systemd-resolved already does, so you have to switch its small built-in listener off first.

Linux#

First, see who uses port 53:

Run in terminalbash
sudo ss -lntup | grep ':53 '

If you see systemd-resolve on 127.0.0.53:53, free the port. This is the official method from the AdGuard Home Docker page:

Run in terminalbash
sudo mkdir -p /etc/systemd/resolved.conf.d
sudo tee /etc/systemd/resolved.conf.d/adguardhome.conf <<'EOT'
[Resolve]
DNS=127.0.0.1
DNSStubListener=no
EOT
sudo mv /etc/resolv.conf /etc/resolv.conf.backup
sudo ln -s /run/systemd/resolve/resolv.conf /etc/resolv.conf
sudo systemctl reload-or-restart systemd-resolved

Why DNS=127.0.0.1? Without the stub listener, the old address 127.0.0.53 no longer works, so the machine itself must be told to ask AdGuard Home on 127.0.0.1.

One side effect (our note, not from the docs): while the AdGuard Home container is stopped, this machine cannot resolve names either, so docker compose pull fails. Start the container first, or comment out DNS=127.0.0.1 for the update.

Check it worked:

Run in terminalbash
sudo ss -lntup | grep ':53 '

Expected: no output at all. Nothing is listening on port 53 yet.

Windows (PowerShell)#

Docker Desktop publishes the port on Windows. Check that nothing else uses it:

Run in terminalpowershell
netstat -ano | findstr ":53 "

Check it worked: no line with 0.0.0.0:53 or 127.0.0.1:53 in LISTENING state. If there is one, note the last number (the process id) and look it up with Get-Process -Id <PID>.

Mac (Terminal)#

Run in terminalbash
sudo lsof -nP -iUDP:53 -iTCP:53 -sTCP:LISTEN

Check it worked: no output. If a program is listed, stop it or use the native option below.

Step 2: Start AdGuard Home#

You create a folder, save one Compose file and start it. Data lives in ~/docker_volumes/adguardhome.

The container uses two folders, as the official page says: /opt/adguardhome/work (data: logs, statistics) and /opt/adguardhome/conf (settings, in AdGuardHome.yaml).

Linux#

Run in terminalbash
mkdir -p ~/docker_volumes/adguardhome/{work,conf}
cd ~/docker_volumes/adguardhome
nano docker-compose.yml

Paste this, save, and start:

File contentyaml
services:
  adguardhome:
    image: adguard/adguardhome:v0.107.79
    container_name: adguardhome
    restart: unless-stopped
    ports:
      - "53:53/tcp"
      - "53:53/udp"
      - "3000:3000/tcp"   # first-run wizard; remove after setup
      - "80:80/tcp"       # web page after setup
    volumes:
      - ./work:/opt/adguardhome/work
      - ./conf:/opt/adguardhome/conf
Run in terminalbash
docker compose up -d

Want AdGuard Home to see every device's real IP? On Linux you can replace the ports: block with network_mode: host. The official page says --network host is how to make it "see the original client IPs". It is also needed for the built-in DHCP server (ports 67 and 68), which this guide does not use. With host mode, the web page uses whatever port you pick in the wizard.

Windows (PowerShell)#

Codepowershell
$d = "$HOME\docker_volumes\adguardhome"
New-Item -ItemType Directory -Force "$d\work", "$d\conf" | Out-Null
Set-Location $d
notepad docker-compose.yml

Paste the same Compose file as in the Linux tab, save, and start:

Run in terminalpowershell
docker compose up -d

Mac (Terminal)#

Same commands as Linux, with the same Compose file:

Run in terminalbash
mkdir -p ~/docker_volumes/adguardhome/{work,conf}
cd ~/docker_volumes/adguardhome
nano docker-compose.yml   # paste the file from the Linux tab, save
docker compose up -d

Check it worked#

Run in terminalbash
docker compose ps

Expected: adguardhome is running with ports 53, 3000 and 80 listed. Then open http://<machine-ip>:3000 (or http://127.0.0.1:3000 on the same machine). You should see the AdGuard Home setup wizard.

Potential issue: address already in use on port 53. Go back to Step 1.

First run: the setup wizard#

The wizard asks where to listen and which admin account to create. Take the defaults for the ports and set a long password.

  1. Open http://<machine-ip>:3000 and click Get Started.
  2. Admin Web Interface: keep all interfaces and port 80 (that is the container's port, which the Compose file maps).
  3. DNS server: keep all interfaces and port 53. For a home server, "all interfaces" is what lets other devices reach it. Keep it off the internet (see Security).
  4. Create the admin user and a long password.
  5. Finish, then open http://<machine-ip> and log in.

After this, the wizard is gone. Edit docker-compose.yml, delete the 3000:3000 line and run:

Run in terminalbash
docker compose up -d

Check it worked#

Ask your new DNS server about a known ad domain (use 127.0.0.1 if you are on the same machine, otherwise the machine's IP):

Run in terminalbash
nslookup doubleclick.net 127.0.0.1

Expected: an answer of 0.0.0.0 (or an empty or refused answer, depending on the blocking mode in Settings > DNS settings). A normal site such as example.org should answer with real addresses. The Query Log page in the web UI shows both lookups. The same nslookup command works in PowerShell and the Mac Terminal.

Step 3: Point your router at it#

Set AdGuard Home's address as the DNS server in your router's DHCP settings, and every device that joins or renews its lease will use it.

The official guide describes it like this: open the router's settings in your browser (the address is usually printed on the router), find the DHCP/DNS settings and enter the AdGuard Home server address. Exact menus differ by router.

  1. Give the AdGuard Home machine a fixed IP (a DHCP reservation in the router).
  2. In the router's LAN or DHCP settings, set the primary DNS to that IP.
  3. Leave the secondary DNS empty, or use a second AdGuard Home. Our advice: a public secondary DNS lets some devices skip your blocking.
  4. Reconnect a device (or turn Wi-Fi off and on) so it gets a new lease.

Some routers do not allow a custom DNS server. The official guide says that then the built-in DHCP server of AdGuard Home may help. That needs host networking on Linux, so it is a job for a second day.

The router step is the same whatever computer runs AdGuard Home. What differs is how you set a single device by hand and how you check it.

Linux#

To try one device first, set its DNS by hand (for example in your network manager) to the AdGuard Home address. Check it worked:

Run in terminalbash
nslookup example.org

The Server: line must show your AdGuard Home machine's IP. Then watch the Query Log in the web UI fill up with that device's lookups.

Windows (PowerShell)#

The official guide sets it by hand like this: Control Panel, Network and Sharing Center, Change adapter settings, your connection, Properties, Internet Protocol Version 4 (TCP/IPv4), "Use the following DNS server addresses". Check it worked:

Run in terminalpowershell
nslookup example.org

The Server: line must show your AdGuard Home machine's IP. Renewing the lease with ipconfig /renew helps after a router change.

Mac (Terminal)#

The official guide sets it by hand in System Settings, Network, your connection, Advanced, DNS tab. Check it worked:

Run in terminalbash
nslookup example.org

The Server: line must show your AdGuard Home machine's IP. Phones: the official guide covers Android and iOS (Wi-Fi network settings, DNS field); mobile data cannot use a custom DNS.

Make it yours: blocklists, upstream DNS, clients and home names#

Four settings turn it from "running" into "useful".

You wantWhere in the web UIWhat to do
More blockingFilters > DNS blocklistsKeep the default AdGuard DNS filter and add lists slowly. Big lists use more memory
Private lookupsSettings > DNS settings > Upstream DNS serversUse encrypted servers, for example https://dns.cloudflare.com/dns-query (DoH) and tls://dns.quad9.net (DoT). Put plain IP addresses in Bootstrap DNS servers so it can find them
Different rules per deviceSettings > Client settingsAdd a device by IP, CIDR, MAC or ClientID, then give it its own blocked services, safe search or upstreams
Home namesFilters > DNS rewritesMap nas.home.arpa to your NAS address. home.arpa is the name reserved for home networks
Allow a blocked siteFilters > Custom filtering rulesAdd `@@

Per-device rules need AdGuard Home to see each device's real IP. Behind Docker Desktop all devices share one address, so this feature does not work there.

Encrypted upstream is for the leg from AdGuard Home to the internet. The leg from your devices to AdGuard Home stays plain DNS on port 53 at home.

AdGuard Home or Pi-hole?#

Both do the same job. AdGuard Home is one small program with encrypted DNS built in; Pi-hole has the bigger community.

AdGuard HomePi-hole
InstallOne program or one small Docker imageDocker image or install script
Encrypted upstream (DoH/DoT)Built inUsually via an extra helper such as cloudflared
Answers DoH/DoT to your devicesYes (needs a certificate)No
Per-device rulesBuilt in (Client settings)Groups
Own DHCP serverYesYes
Home namesDNS rewrites, wildcards allowedLocal DNS records

The AdGuard Home column follows its official docs. The Pi-hole column is our general summary, not an official claim: check Pi-hole's own docs for the version you use. We have older Pi-hole posts on the site too.

The native option#

If you would rather not use Docker, the developer's release archive runs as a service on Linux, Windows and Mac.

From the official Getting started page: download the archive for your system from the latest stable release page, extract it, enter the AdGuardHome folder and install the service.

Linux and Mac:

Run in terminalbash
sudo ./AdGuardHome -s install

Windows (PowerShell as administrator):

Codepowershell
.\AdGuardHome.exe -s install

Official notes: on macOS Catalina and newer, keep the working folder inside /Applications. On Fedora, install into /usr/local/bin. Control the service with -s start, -s stop, -s restart, -s status and -s uninstall. The first start listens on port 3000 for the same wizard as above. Docker installs cannot use the built-in update button: pull a new image instead.

Put it behind HTTPS (with or without Traefik)#

This is about the admin web page only: it keeps your password off the network in plain text. DNS stays on port 53. Running only at home on one machine? You can skip this section.

Important: both routes below want ports 80 and 443 on the same machine. So AdGuard Home must stop publishing port 80 (and you do not publish 443 for it at all). DNS on port 53 stays published directly.

With Traefik#

Prerequisites: a domain, DNS pointing to the server, ports 80 and 443 open, and Traefik on the external proxy network.

Run in terminalbash
docker network create proxy

Change the service like this. The labels are our example, not from the AdGuard docs (adjust names to your Traefik setup):

File contentyaml
services:
  adguardhome:
    image: adguard/adguardhome:v0.107.79
    container_name: adguardhome
    restart: unless-stopped
    ports:
      - "53:53/tcp"
      - "53:53/udp"
    volumes:
      - ./work:/opt/adguardhome/work
      - ./conf:/opt/adguardhome/conf
    networks:
      - proxy
    labels:
      - traefik.enable=true
      - traefik.docker.network=proxy
      - traefik.http.routers.adguard.rule=Host(`<your-domain>`)
      - traefik.http.routers.adguard.entrypoints=https
      - traefik.http.routers.adguard.tls=true
      - traefik.http.services.adguard.loadbalancer.server.port=80

networks:
  proxy:
    external: true

Without Traefik: Caddy#

Choose Caddy if you have no Traefik yet and run one small server.

CaseResultYou do
Public domainAutomatic Let's EncryptDNS A record to the server, ports 80 and 443 open
Home onlytls internal (local certificate)Trust Caddy's root certificate on each device

If Caddy runs on the machine itself (not in Docker), publish the AdGuard Home web page on a private local port. Change the 80:80 line in your Compose file to:

File contentyaml
      - "127.0.0.1:8080:80"

Caddyfile for a public domain:

File contenttext
adguard.example.com {
    reverse_proxy localhost:8080
}

Caddyfile for home only (add a DNS rewrite in AdGuard Home that points the name at the Caddy machine):

File contenttext
adguard.home.arpa {
    tls internal
    reverse_proxy localhost:8080
}

Trust the root certificate (pki/authorities/local/root.crt in Caddy's data directory):

Run in terminalbash
# Linux: on the Caddy machine
sudo caddy trust
# other Linux devices, with root.crt copied over
sudo cp root.crt /usr/local/share/ca-certificates/caddy-root.crt && sudo update-ca-certificates
# Windows (PowerShell as administrator)
certutil -addstore -f ROOT root.crt
# Mac
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain root.crt

Run Caddy (Linux: install from the official apt repository on caddyserver.com/docs/install first):

Run in terminalbash
# Linux
sudo systemctl reload caddy
# Windows (PowerShell)
winget install CaddyServer.Caddy
caddy run
# Mac
brew install caddy && caddy run

Or use Docker on the same network. Remove the 80:80 line from AdGuard Home, add the proxy network to it, use reverse_proxy adguardhome:80 in the Caddyfile and save this in ~/docker_volumes/caddy (create the network once: docker network create proxy):

File contentyaml
services:
  caddy:
    image: caddy:2.11.7
    container_name: caddy
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - ./caddy_data:/data
      - ./caddy_config:/config
    networks:
      - proxy
networks:
  proxy:
    external: true

Check it worked: curl -I https://adguard.example.com answers HTTP/2 200 (or a redirect to the login page).

AdGuard Home can also serve DNS-over-HTTPS and DNS-over-TLS itself (Settings > Encryption settings, with a certificate). That is for devices outside your home. It is not needed on your own network and is not covered here.

Security and testing#

Keep it off the internet, lock the settings folder and check the basics.

  • Never forward port 53, 80 or 443 on your router to this machine. An open DNS server on the internet gets abused. To use it away from home, go through a VPN such as WireGuard or Tailscale.
  • Secrets stay in conf/. The admin password hash lives in AdGuardHome.yaml. Lock the folder and never put it in git:
Run in terminalbash
chmod 700 ~/docker_volumes/adguardhome/conf
  • Listen only where you need to. The official security guide says that on a home network you should pick the local interface, and for a public server restrict access with the Access settings (allowed clients) and keep the default plain-DNS rate limit of 20.
  • Update when security fixes appear. Several recent releases fixed security problems (see the release notes), so do not stay on one tag forever.

Tests:

Run in terminalbash
docker compose ps
docker compose logs --tail 100 adguardhome
nslookup doubleclick.net 127.0.0.1
curl -I https://<your-domain>

Expected: container running, no errors in the log, a blocked answer for the ad domain and HTTP/2 200 (or a redirect to login) from the last command (only if you set up HTTPS).

Troubleshooting#

ProblemSolution
address already in use on port 53 when startingAnother program owns port 53. On Ubuntu follow Step 1. Find the program with sudo ss -lntup | grep ':53 '
Setup page does not open on port 3000docker compose ps must show 3000->3000. Open the port in the host firewall
Devices do not use itRenew the lease or reconnect Wi-Fi. nslookup example.org must show your server as Server:
A site is not blockedCheck Settings > General settings (blocking on), Filters > DNS blocklists, and that no allowlist, rewrite or custom rule overrides it (official FAQ)
A site is blocked by mistakeAdd an allow rule such as `@@
Every device shows one IPYou are behind Docker Desktop or NAT. Use a Linux host with network_mode: host, or the native program
"Blocked by CNAME or IP" in the logNormal: AdGuard Home blocks trackers hidden behind another name (CNAME cloaking)
Names stop resolving on the host after DNSStubListener=noThe container is stopped. Start it, or temporarily remove DNS=127.0.0.1
Container will not start after editing settingsEdit AdGuardHome.yaml only while the container is stopped, or the running program overwrites it. Restore the file from backup if the YAML is broken

Typical error: port 53 in use#

Codetext
Error response from daemon: ... failed to bind host port ... :53/tcp: address already in use

The wording changes between Docker versions; the official page only says Docker "will fail to bind on port 53". Cause: the systemd-resolved stub listener on 127.0.0.53:53. Fix: Step 1.

Known bugs#

Open on GitHub at the time of writing (read 2026-10-26):

  • #8569: the DNSCrypt server is reported broken in v0.107.79. Only matters if you serve DNSCrypt.
  • #8596: optimistic caching serves records for hours after they expire. Leave optimistic cache off if you see TLS errors.
  • #8658: the DNS server can deadlock on shutdown in one report.
  • #8662: a request for a release on a newer Go version because of a security advisory. Watch the releases page.
  • If v0.107.79 misbehaves for you, roll back by setting the tag to v0.107.78. Back up first: settings formats can change between releases.

Updates and backups#

Docker installs do not update themselves: change the tag and recreate the container. Back up by copying the folder.

Update (Linux/Mac; on Windows use the same docker compose lines in PowerShell):

Run in terminalbash
cd ~/docker_volumes/adguardhome
# read the release notes, edit the image tag, then:
docker compose pull && docker compose up -d

The official page's own update path is the same idea: pull the new image, stop and remove the old container, start a new one from the same command.

Backup script (Linux/Mac). It stops the container briefly so the files are consistent:

Run in terminalbash
cd ~/docker_volumes/adguardhome
mkdir -p ~/adguard-backups
docker compose down
tar czf ~/adguard-backups/adguardhome-$(date +%F).tgz -C ~/docker_volumes adguardhome
docker compose up -d

Windows (PowerShell):

Codepowershell
Set-Location "$HOME\docker_volumes\adguardhome"
New-Item -ItemType Directory -Force "$HOME\adguard-backups" | Out-Null
docker compose down
Compress-Archive -Path "$HOME\docker_volumes\adguardhome" -DestinationPath "$HOME\adguard-backups\adguardhome-$(Get-Date -Format yyyy-MM-dd).zip" -Force
docker compose up -d

Schedule: Linux/Mac cron at 03:00 (0 3 * * * <path-to-script>); Windows Task Scheduler running the same steps. A short DNS gap at 03:00 is normal.

Restore test#

Do this once, before you need it. It starts the backup on other ports so it does not clash with the real one (Linux/Mac):

Run in terminalbash
mkdir -p /tmp/restore && tar xzf ~/adguard-backups/adguardhome-*.tgz -C /tmp/restore
docker run --rm -d --name adguard-restore -p 5353:53/udp -p 3001:80/tcp \
  -v /tmp/restore/adguardhome/work:/opt/adguardhome/work \
  -v /tmp/restore/adguardhome/conf:/opt/adguardhome/conf \
  adguard/adguardhome:v0.107.79
nslookup -port=5353 doubleclick.net 127.0.0.1

Open http://localhost:3001, log in and check that your settings, blocklists and query log are there. Then clean up:

Run in terminalbash
docker stop adguard-restore && rm -rf /tmp/restore

Performance and resources#

AdGuard Home is light, but it is also the thing every device waits for. The official docs give no RAM or CPU numbers and we did not measure them, so measure on your own machine:

Run in terminalbash
docker stats --no-stream adguardhome

Tips:

  • Blocklists cost memory. Add lists one at a time and watch docker stats.
  • The query log and statistics cost disk. Set the retention in Settings > General settings to what you need.
  • Speed. Use more than one upstream server and the parallel-request mode in Settings > DNS settings, and enable the cache.
  • Hardware. A Raspberry Pi or a small mini PC is enough for a home.

FAQ#

Can I use the latest tag? Yes, the official page uses it. We pin v0.107.79 so updates and rollbacks are your choice.

Should I use beta or edge? No. The official page calls them unstable development builds. Do not put them under the DNS of your whole family.

Will it break websites? Sometimes a login or shop page needs a domain that is on a list. Add an allow rule in Custom filtering rules.

Does it block YouTube ads? Mostly no. DNS blocking works per name, and many video ads come from the same names as the video.

Can it replace my router's DHCP server? Yes, with host networking on Linux. Start with DNS only and add DHCP later.

Can I run it on my Windows PC or Mac? For a test, yes (Docker Desktop). For the whole house, use an always-on Linux machine or the native program, because devices lose DNS whenever the PC sleeps.

Sources#

Checked against the official docs on 2026-10-26. Not run by DIY Lab Hub. Error text marked "typical" is not copied from the official docs.