DEV Community

Own The Stack
Own The Stack

Posted on

Block Ads on Every Device, Everywhere!

Block Ads on Every Device, Everywhere

Most ad-blocking setups only work at home. You configure a DNS server on your router, enjoy a cleaner internet experience, then leave your Wi-Fi network and lose all of it. Your phone switches to mobile data, your laptop joins a coffee shop network, and suddenly you are right back to dealing with intrusive ads, trackers, telemetry requests, and analytics endpoints.

I wanted an infrastructure setup that followed me everywhere. Not just on my home network, and not just when connected to Wi-Fi. I needed it on every device, on every network, everywhere. This is how I built it using AdGuard Home and Tailscale—and why the simplest design ended up being the superior engineering choice.


What Is Tailscale?

Before diving into the implementation, it is worth understanding why Tailscale makes this architecture possible. Tailscale is a zero-config VPN built on top of the WireGuard protocol that creates a secure, private mesh network between all of your connected nodes, known as a Tailnet.

You can install it seamlessly on your laptop, phone, desktop, VPS, or home server. Once configured, they can securely communicate regardless of where they are physically located. Your phone on mobile data can route directly to your server at home, and your laptop in a coffee shop can securely reach a VPS in another country. Everything behaves as if it is sitting on the exact same local private network topology.

Each device on the mesh network receives a stable, private IP address in the 100.x.x.x range. Crucially for our use case, Tailscale allows you to centrally manage DNS settings for every single device connected to your Tailnet. Instead of configuring custom DNS servers separately across multiple operating systems, we configure it once in our Tailscale coordinator panel and let it distribute everywhere automatically.


Option 1: The Five-Second Setup

If all you want is robust ad blocking without running or maintaining any of your own compute infrastructure, AdGuard provides public DNS servers that can be integrated directly into your Tailnet.

To set this up, open your Tailscale Admin Console, navigate to DNS, and locate the Global Nameservers section. Add the following public upstream IP addresses:

  • 94.140.14.14
  • 94.140.15.15

Once added, toggle the Override Local DNS option to On. Every device connected to your Tailnet will immediately begin using AdGuard's public upstream resolvers for all outbound traffic.

For many users, this zero-maintenance approach is more than enough. However, the obvious downside is that you lose absolute control over your traffic. You cannot build custom blocklists, view granular query logs, write local DNS split-horizon overrides, or manage device-specific filtering rules. If you prefer owning your stack completely, self-hosting is the path forward.


Option 2: Self-Hosted AdGuard Home

Running a dedicated AdGuard Home instance gives you absolute data sovereignty and complete control over your network's DNS filtering layers. The computational footprint is incredibly lightweight. A minimal cloud VPS with roughly 512MB of RAM is plenty of overhead to handle a personal DNS server.

Deploying AdGuard Home Securely

Many standard deployment guides tell you to map ports directly out to the host using "53:53/udp". On Linux hosts, Docker manipulates iptables directly and completely bypasses standard host firewalls like ufw. This unintentionally exposes your DNS port to the public internet, making your VPS a prime target for weaponized DNS Amplification DDoS attacks.

To circumvent this risk completely, we can leverage Docker's host networking mode. This forces the container to share the host's network interfaces directly, allowing us to safely bind AdGuard Home exclusively to our private Tailscale interface during the initial configuration wizard.

Create your deployment file:

services:
  adguardhome:
    image: adguard/adguardhome
    container_name: adguardhome
    restart: always
    volumes:
      - ./adguard_data:/opt/adguardhome/work
      - ./adguard_conf:/opt/adguardhome/conf
    ports:
      - "53:53/tcp"
      - "53:53/udp"
      - "3000:3000/tcp"  # This will be your permanent Admin UI port
      - "8080:80/tcp"    # This maps the setup wizard/web service to 8080
      - "853:853/tcp"   # DNS-over-TLS (optional but good for tech gadgets)
Enter fullscreen mode Exit fullscreen mode

Run the stack in detached mode:

docker compose up -d
Enter fullscreen mode Exit fullscreen mode

If the container refuses to bind or fails to start, an existing system service is likely already listening on port 53. The most common culprits on modern Linux distributions are systemd-resolved or dnsmasq. You must stop, disable, or reconfigure the conflicting service on the host before AdGuard Home can successfully claim the port.


Connecting AdGuard Home to Tailscale

Once the container is initialized, access the setup wizard by pointing your browser to your server's IP address at port 3000: http://YOUR_SERVER_IP:3000. Complete the prompts to establish your admin credentials and choose your interfaces.

With AdGuard up and running, it is time to link it globally to your network:

  1. Open your Tailscale Admin Console and copy your VPS's private Tailscale IP (the 100.x.x.x address).
  2. Navigate to the DNS configuration tab.
  3. Under Global Nameservers, click Add Nameserver and choose Custom.
  4. Paste your private server IP, save the configuration, and enable Override Local DNS.

Always use the Tailscale network interface IP rather than the server's public IP address. This guarantees that your raw DNS payloads travel strictly inside an encrypted WireGuard tunnel, completely isolated from the open internet. Your laptop, phone, tablet, and desktop will now route their lookups through your private instance, regardless of where you travel.


What DNS Blocking Can and Can't Do

DNS filtering is an incredibly effective tool, but it is not magic. It excels at dropping queries to known tracking domains, blocking background application telemetry, and filtering advertisements on platforms that do not support standard browser extensions.

However, it cannot reliably strip out video advertisements on platforms like YouTube, nor can it stop ads served directly from the same domain hosting the content. Think of network-level DNS filtering as an essential, high-performance first layer of defense rather than a complete alternative to client-side browser extensions like uBlock Origin.


The Operational Reality

There is an unavoidable architectural reality to this setup. If your self-hosted DNS server drops offline or the container panics, your internet access effectively breaks. Because almost every app and web page initialization begins with a synchronous DNS lookup, a failure here causes everything to fail in strange, silent ways.

Fortunately, your emergency exit playbook is simple: Turn off Tailscale. Disconnecting your client device from your Tailnet instantly drops the operating system back to the local network's native DNS configuration, bringing you back online immediately.

When encountering this reliability challenge, the immediate engineering instinct is to add a public fallback resolver like 1.1.1.1 in the Tailscale dashboard. However, this disrupts your filtering strategy entirely. Operating systems do not gracefully fail over; instead, they often query multiple configured nameservers in parallel or round-robin between them, causing ads to slip past your filter even when your server is completely healthy.


The Over-Engineering Rabbit Hole

You could build an enterprise-grade high-availability system to mitigate this risk. You could spin up redundant global nodes, synchronize configurations automatically using open-source sync tools, and manage complex internal load balancers.

But for a personal project, that introduces massive architectural complexity to solve a failure that might happen once a year. The management overhead of the infrastructure quickly outgrows the practical utility of the tool.

Approach Control Reliability Complexity
Public AdGuard DNS Low High Very Low
Self-Hosted AdGuard Home High Moderate Low
Multi-Server Infrastructure High High High

A cleaner, more pragmatic engineering tradeoff is keeping a single, hardened AdGuard Home instance backed by a clear recovery plan. Simple systems fail in highly predictable ways, require zero configuration synchronization, and are easy to maintain. Sometimes, owning the stack isn't about deploying the most complex distributed architecture—it is about knowing exactly where complexity pays for itself, and where it doesn't.

Top comments (0)