How to setup whole home Ad blocking! (Featuring AdGuard and Unbound DNS)

How to setup whole home Ad blocking! (Featuring AdGuard and Unbound DNS)

One of the greatest inventions of the past century is the internet. It has let everyone in the world become connected in a way that we never thought possible previously. But this great invention also has many dark sides: Ads. Most sites on the internet claim to be “free”, but in reality, they are making money off of their users using ads placed throughout the site. Not only Ads exists on these sites. Many sites contain trackers that collect everything you do their sites, which is then sold to the highest bidder. As scary as it is to think about, there is something we can do to protect ourselves. Many of these ads or tracker use a DNS server to look up the IP of their servers in order to server these ads or trackers.

Enter Adguard

There are a few services called “DNS sink-holes” that act as an intermediary between the device requesting the DNS and the actual DNS server. When your PC or other device requests the IP address of a domain, it asks the “sink-hole” and that either blocks the request or lets it continue to the DNS server. We can set blacklists for blocked domains to common ad and tracker services to prevent them from responding. This effectively blocks all of those ads and trackers from our devices. Cool huh?

One of the best “sink-holes” to use is a service called Adguard. It is a free to use service with “sink-hole” functionality as well as many more cool features. As with most of my other Home-Lab services, I will be using docker to install the service, as well as securing the service behind the Traefik reverse proxy.

Installing Adguard

To install Adguard, I will use the official Adguard docker image in a docker compose file. This file is the same one I use for all my services, so some of the items may not apply to everyone, so I will try and explain the parts. Below is the docker compose code:

    container_name: adguardhome
    restart: unless-stopped
    image: adguard/adguardhome
    hostname: adguard
      - no-new-privileges:true
      - $DOCKERDIR/adguard/conf:/opt/adguardhome/conf
      - $DOCKERDIR/adguard/work:/opt/adguardhome/work
      - t2_proxy
      - "53:53/tcp" # Ports for DNS server
      - "53:53/udp"
      - PUID=$PUID
      - PGID=$PGID
  • security_opt: This is an optional setting to prevent the service from trying to elevate its privileges.
  • volumes: This links our local folders to the docker folders. The $DOCKERDIR is an environment variable of the location of docker-compose file.
  • ports: These are the ports we want to expose to the host IP. Port 53 is the DNS server port. You may need more depending on services needed.
  • If you do not use a reverse proxy, such as Traefik, then you will also need to add ports 3000 and 80 to the ports list.

If you use Traefik, such as I do, then you can add the following labels you connect it to that:

      - "traefik.enable=true"
      ## HTTP Routers
      - "traefik.http.routers.adguard-rtr.entrypoints=https"
      - "traefik.http.routers.adguard-rtr.rule=Host(`dns.$DOMAINNAME`)"
      - "traefik.http.routers.adguard-rtr.tls=true"
      ## Middlewares
      - "traefik.http.routers.adguard-rtr.middlewares=chain-oauth@file"
      ## HTTP Services
      - "traefik.http.routers.adguard-rtr.service=adguard-svc"
      - ""

This sets the DNS service behind For extra security I hid the access behind Google OAuth so that only accounts I want can access it. The middlewares section will depend on how the rest of your system is setup.

One thing to not for the reverse proxy setup is the server port needs to be set to port 3000 for the setup of the service, then after that can be changed back to port 80.

Potential Issue: One issue that I ran into is my Ubuntu server was running an internal DNS service, so port 53 was already in use. To fix, I following the steps in the this link to disable the DNS server.

With the port set to 3000 at the start, go to either the IP of the host, port 3000, or to the if using Traefik. you should see the setup page if everything was successfull.

Follow the setup steps and create a admin account, making sure to use a strong password of course! Once initial setup is done, you can change the compose file to use port 80. Going to the page now should bring a login page. Use the credentials you made earlier. We now have Ad Guard Home fully installed! I will leave the settings for now, maybe I’ll cover them in another post. Finally, you can follow the setup guide to connect you devices to ad guard home.

Unbound DNS

With Adguard working, we now have our devices protected from many ads and trackers, but our internet usage is still being tracked by the upstream DNS servers themselves. The thing with Adguard is, it doesn’t actually know the addresses of any of the requests, so it has to ask another server to do that lookup for us. These upstream servers collect all of these requests. To avoid this, we can install our own upstream DNS server that knows the IP address, so our requests aren’t collected by company’s like Google or Cloudflare. That’s where Unbound DNS comes in. Unbound talks to the root name servers on the internet that allow us to cache these addresses locally. It’s a bit of an advance topic, so I recommend reading the Unbound site to better understand the difference between the DNS servers out there.

We will be using a premade docker container in docker compose, same as Adguard. First we need to create a folder and some files for Unbound to use:

mkdir unbound
touch unbound/a-records.conf
touch unbound/forward-records.conf
touch unbound/srv-records.conf

Next we need to define the docker container in our compose file:

    container_name: unboundDNS
    restart: unless-stopped
    image: mvance/unbound:latest
    hostname: unboundDNS
      - no-new-privileges:true
      - $DOCKERDIR/appdata/unbound:/opt/unbound/etc/unbound/

Things to note with the compose code:

  • The network is the same as the rest of my containers, but I define a static IP address for it. This IP is local only to my docker containers, and therefor not accessible outside the server. This allows Adguard to connect, but not individual devices.
  • I recommend adding a static IP for this container, but please not the IP will depend on the docker network sub-domain.
  • The volume is where we pass in the files and folder we created above. Mine is just using environment variables to define the absolute path.

Now we can run the compose file. I recommend viewing the syslogs of the container to make sure it is working, as there is no GUI to indicate if things are working.

Finally we can link the Unbound server to Adguard. In Adguard, go to Settings->DNS Settings. In the upstream servers section at the top, replace the default server with the IP of the Unbound server we set before.

Before saving, I recommend trying the “Test Upstream” button to ensure Adguard can communicate with Unbound. If all is good, save the new config.


So there we have it, we set up a personal DNS server with a DNS sink-hole for Ad and Tracker blocking. Now when a device queries Adguard, it will check if the domain is allowed or not. If it is not allowed, it stops the check there and the Ad or Tracker is stopped in its tracks. If the domain is allowed, it will ask Unbound for the IP address of the domain. If unbound already knows it, it will respond immediately. If Unbound does not know, it will find out using the Root DNS servers directly instead of third party ones like Google or Cloudflare.

Leave a Reply