- 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
- 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-stoppedbrings 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.
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/confPin 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.
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.txtThen 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.
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 addressThe 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”.