zero-stack/ZERO

I put the ad blocker in the router's ear, so nobody in the house has to install anything

5 MIN READPOST 1 OF 2
TL;DR
WHAT
AdGuard Home on the homelab laptop filters DNS for the whole house, and Tailscale carries the same filter to my phone when I'm out
STACK
AdGuard Home · Docker Compose · Tailscale · router DHCP settings

the itch

Most devices in a house can’t run an ad blocker, and the TV won’t even let you try. So I stopped asking the devices.

Nearly every device asks DNS where a website lives before it loads anything, and the router tells it which DNS server to ask. If that server refuses to answer for ad domains, the ad has nowhere to load from. Nobody installs anything.

I run that server on the homelab laptop, and I pointed my phone at it for when I’m outside. That second part turned out to be the easy one.

how it works

01Any deviceasks where a site lives
02RouterDHCP says: ask the laptop
03AdGuard Homeblocklist, then forward
04Upstream DNSDNS over HTTPS
away from home, Tailscale replaces the router's part and hands the phone the laptop's address
  • AdGuard Home runs in Docker on the laptop and listens on two addresses: its LAN address and its Tailscale address.
  • The router’s DHCP settings hand out the laptop as the DNS server, so anything that joins the Wi-Fi uses it.
  • Blocked names get an empty answer. Everything else is forwarded to a public resolver over HTTPS.

What DNS filtering can and cannot block

DNS only decides whether a name resolves. A banner served from ads.example.com dies when that name stops resolving. But YouTube serves its ads from the same hostnames as the videos, so there’s nothing to block without breaking the video. Ads baked into an app’s own feed are the same story. This setup cannot block YouTube or in-app video ads.

So it’s a floor, not a replacement for a browser extension. Trackers, banner networks and telemetry stop on every device, including ones where nothing else can be installed.

The router does the handing out

The Jio router’s LAN settings page has a DNS section. I set it to “Use Below” with the laptop’s LAN address as primary and 1.1.1.1 as secondary. Reserve the laptop’s address in the router’s DHCP list first.

Routers have a habit of saying “saved” and then not doing it, so check from the other side. AdGuard’s query log lists every client that asked. In the log I read on 6 November 2026, covering 2 to 5 November, queries arrived from about 22 LAN addresses besides the laptop itself. So other devices do use it.

When the laptop is down

I haven’t tested this by switching the laptop off, so this is how DNS is meant to behave, not something I watched. A device with two DNS servers tries the first and, after a timeout, usually tries the second. With a secondary set, a dead laptop means slow lookups for a while and then the ads come back. Without one, there’s no DNS at all, which feels exactly like no internet.

Where Tailscale fits

Away from home the router isn’t involved. Tailscale can push DNS settings to every device in the tailnet, so the laptop’s Tailscale address goes in as the first nameserver, with Cloudflare behind it. tailscale dns status on the laptop shows MagicDNS on and the resolvers in that order.

One switch decides whether any of this reaches your devices: “Override DNS servers” in the Tailscale admin console. With it off, devices may keep their own DNS and ignore your nameserver. I haven’t verified its state from the laptop, because the command doesn’t show it. Check the admin console under DNS.

It’s a VPN only in the narrow sense. DNS and traffic to my own machines go through the tailnet. There’s no exit node (tailscale debug prefs shows ExitNodeID empty and RouteAll false), so the phone’s general browsing still goes out directly.

known gaps

Nothing has broken the house yet, so these are ways it can.

  • One DNS server is one point of failure. The laptop is on Wi-Fi, runs behind an inverter, and its Wi-Fi firmware hung once on 2 November.
  • The secondary resolver is a hole in the filter. Some devices pick the second server whenever they feel like it, and that path has no blocking at all. I haven’t measured how often.
  • DNS filtering doesn’t stop in-app ads. YouTube and app feeds are untouched.
  • Devices can ignore the router. Browsers with their own DNS over HTTPS setting, and some TVs, use their own resolver.
  • Nothing watches the DNS itself. restart: unless-stopped brings the container back after a reboot, but nothing checks that it is answering.

steal this

The container uses host networking, so AdGuard answers on the laptop’s own addresses.

compose.yml
services:
  adguard:
    image: adguard/adguardhome:latest
    network_mode: host
    restart: unless-stopped
    command: ["--no-check-update", "-c", "/opt/adguardhome/conf/AdGuardHome.yaml", "-w", "/opt/adguardhome/work", "-p", "3002"]
    volumes:
      - ./work:/opt/adguardhome/work
      - ./conf:/opt/adguardhome/conf

Pin the image tag if you copy this. I’m on latest, a bad habit outside a homelab.

Binding is the part that bites. On Linux Mint, systemd-resolved already owns port 53 on the loopback address, so bind only to the two addresses you want, not 0.0.0.0 or 127.0.0.53. The addresses below are documentation examples.

AdGuardHome.yaml (dns section, example addresses)
dns:
  bind_hosts:
    - 192.0.2.10      # the laptop's LAN address
    - 100.64.0.10     # the laptop's Tailscale address
  port: 53
  upstream_dns:
    - https://dns10.quad9.net/dns-query
  bootstrap_dns:
    - 9.9.9.10
    - 2620:fe::10
  cache_enabled: true
filters:
  - enabled: true
    name: AdGuard DNS filter
    url: https://adguardteam.github.io/HostlistsRegistry/assets/filter_1.txt

Then set the router’s DNS and Tailscale’s global nameserver, and check with a name that’s on the list. doubleclick.net is a safe test.

check.sh
LAPTOP=192.0.2.10   # your laptop's LAN address
dig +short doubleclick.net @"$LAPTOP"   # filtered: empty or 0.0.0.0
dig +short doubleclick.net @1.1.1.1     # unfiltered: a real address

The software is AdGuard Home and Tailscale.

what i’d do differently

  • Plug the laptop into Ethernet first. A DNS server on flaky Wi-Fi works until it doesn’t. A USB-to-LAN adapter is next on my shopping list.
  • Measure the secondary before deciding.
  • Test the failure on purpose. Switch the container off and write down what each device does.
  • Monitor a real lookup. “Port 53 is open” tells you less than “a normal name resolves”.

secrets

This site has hidden things. Every one has instructions below, but what it is and what it does stays secret until you find it. You found 0 of 20.

Shortcut: press ? on any page to open this panel. The pixel buddy knows every secret and gives hints.