AdGuard Home in Docker

AdGuard Home in Docker

using Docker-Compose

·

10 min read

Introduction

A week or two ago, I had seen a reference to AdGuard Home as an alternative to Pi-Hole somewhere. I don't recall exactly where; I instantly opened a new tab Google tab to search for it and quickly lost track of the source.

AdGuard Home does its magic blocking ads at the network level by acting as a DNS server that "black holes" queries for known ad/tracking domains.

From the AdGuard Home README.md:

AdGuard Home is a network-wide software for blocking ads & tracking. After you set it up, it'll cover ALL your home devices, and you don't need any client-side software for that.

It operates as a DNS server that re-routes tracking domains to a “black hole”, thus preventing your devices from connecting to those servers. It's based on software we use for our public AdGuard DNS servers, and both share a lot of code.

It looked pretty slick, but I already run my own DNS/DHCP server using my docker-dnsmasq project. After a little research and testing in docker, I couldn't figure out how to set up multiple DHCP scopes and static DNS entries, so I opted to keep docker-dnsmasq in place.

I now have AdGuard Home configured as the primary DNS server that's handed out by my DHCP server, and it routes queries for "naked hostnames" and my internal domain to docker-dnsmasq. This effectively allows AdGuard Home to send all queries via DNS-over-TLS to CloudFlare's 1.1.1.1 service, and all queries for internal hosts to docker-dnsmasq over plain DNS.

I use the docker-dnsmasq container I created mainly because it makes for easy management by splitting up different functions into different text files. This allows me to maintain DHCP reservations in one file, static DNS entries in another, and the main service config in yet another file.

Initially, I created the docker-dnsmasq project to replace the two Windows Server 2016 domain controllers I had acting as the primary and secondary DNS & DHCP servers on my network. They were both running on a loud, hot Dell PowerEdge T420 that I was itching to get out of my apartment.

Anyhow, I'm rambling now, so let's dive in!

Setting Up AdGuard Home in Docker

I've created the following GitHub Gist, which is exactly what I'm using to run it at home.

Let's get started!

First, get logged into your Linux host with Docker and Docker-Compose installed.

Once logged in, let's create a directory for our docker-compose.yml file. Create this wherever it makes sense for you. Personally, I keep most of my projects like this in a Projects folder in my home directory, keeping all of my clutter neatly hidden away in one directory.

I've called mine "adguard," so that's what I'll use in the examples below.

Once you've created your directory, change to it. Now, let's create our docker-compose.yml file. Use the command below to open the nano editor with our new blank file.

nano docker-compose.yml

Next, paste the contents of the Gist above into the editor. When you're done, hit Ctrl+X to exit. Hit Y to save the file, and if prompted, hit Enter to confirm the file name.

Starting AdGuard Home

To start AdGuard Home, while working in the directory containing our docker-compose.yml file, run the following command.

docker-compose up -d

Above, we've told docker-compose to do is to bring up our stack (the 'up' keyword) and then detach from its console (the '-d' flag). The detach flag is important, otherwise, your container will die when you close your terminal window. This allows the container to run in the background, just as if it was a service installed directly on the host rather than a containerized application.

You can watch the startup progress using the docker logs command:

docker logs adguard

If things are all working as expected, the last group of lines in the container logs before it starts fielding requests will look similar to below.

2022/10/07 21:00:53.623899 [info] Entering the UDP listener loop on [::]:53
2022/10/07 21:00:53.623978 [info] Entering the tcp listener loop on [::]:53
2022/10/07 21:00:53.624009 [info] Entering the tls listener loop on [::]:853
2022/10/07 21:00:53.624031 [info] Entering the DNS-over-QUIC listener loop on [::]:853

These are the last four lines printed to my console when the container starts up successfully.

Initial AdGuard Home Setup

Now that AdGuard Home is running, you'll want to complete the configuration.

Open the initial setup page at http://<docker_host_ip>:3000/, and you should be greeted with the initial setup wizard. Replace with the IP address of your Linux docker server/workstation.

On the first page, click Get Started.

adguardhome1.png

On the second screen is where you'll select the interfaces AdGuard Home listens on. You can skip this page because we'll be using the Docker host's IP address to communicate with the AdGuard Home container.

adguardhome2.png

On the third screen, you'll enter a username, then a password, and password confirmation.

adguardhome3.png

The fourth screen explains how to set up your devices to query the DNS server. You can skip this page as well because we're going to modify that config shortly.

adguardhome4.png

Finally, complete your initial setup by clicking Open Dashboard.

You'll log in using the credentials you set up in step 3 above.

Additional AdGuard Home Configuration

Refer to the AdGuard Home Wiki for complete configuration documentation.

General Settings

Once you get logged in, click the Settings item in the top navbar. From the menu, select General Settings.

Look over the settings here and modify anything that interests you.

The default filter update interval is 24 hours, which has been sufficient for me. I don't use the AdGuard browsing security service, parental control service, or safe search, so I can't speak to their effectiveness at the moment.

Logs Configuration

Under Logs configuration, you can choose how long to store your query logs. Keep in mind that on a network with 10-20 devices (think phones, laptops, media streaming devices, TVs, smart home devices, etc.), these will get huge rather quickly. I have mine set at 30 days, but 7 days should be more than enough for most people.

You'll also find a button here to clear your query logs and clean up disk space.

Statistics Configuration

The final section is Statistics configuration. These are the stats displayed on the main dashboard page. The timeframe you choose is what will be presented on the main page. Currently, mine is set to 30 days, but I haven't been using it for more than 30 yet. I may end up adjusting this myself.

Similar to above, you'll find a button here to clear your statistics. This will reset the dashboard stats to zero.

⚠️ Once you've made your changes, don't forget to click one of the Save buttons--it doesn't matter which one, they all save settings changed on the whole page.

DNS Settings

Now, click Settings in the top navbar again and select DNS Settings.

Upstream DNS servers

This section is where we'll configure our upstream resolvers. These are the DNS servers that AdGuard Home will send any queries it doesn't have in its cache. The order in which the servers are listed doesn't matter.

Here's the configuration I'm using:

# Default primary & secondary upstream resolvers: DNS-over-TLS to Cloudflare
tls://1.1.1.1:853
tls://1.0.0.1:853
# Additional default upstream resolvers: DNS-over-QUIC to AdGuard and DNS-over-TLS to Quad9
quic://dns-unfiltered.adguard.com
tls://dns.quad9.net:853
# Send requests for home domain and naked hostnames to docker-dnsmasq (dns01)
[/home.example.com/]10.0.0.10
[//]10.0.0.10
# Disabled Servers
#1.1.1.1
#10.0.0.1
#2606:4700:4700::1111
#2606:4700:4700::1001
#tls://one.one.one.one
#https://dns10.quad9.net/dns-query

Here's a breakdown of what the above is doing. The first 6 lines are my four "primary resolvers" (with two of the lines being comments). Below that, you'll see my configuration for sending internal hosts to my docker-dnsmasq server, with the domain and IP addresses changed.

The line that reads [/home.example.com/]10.0.0.10 tells AdGuard Home to send any queries for hosts matching *.home.example.com to my internal DNS server, and the line that reads [//]10.0.0.10 tells AdGuard Home to send any queries for "naked hostnames" (i.e., syslog or dns01) to my internal DNS server. These two lines allow me to reference all of the hosts on my home network by name rather than by IP address. You can leave these lines off of your configuration if you don't plan to use another resolver for internal host names, as I have opted to do.

The last 8 lines are servers that are currently disabled.

Below the Upstream servers text box, you'll find three options: Load-balancing, Parallel requests, and Fastest IP address. I'm using Parallel requests, which sends every query to all of your upstream servers at the same time, sending the fastest response to the client making the query.

Bootstrap DNS servers

This section is where you'll define the servers used to resolve any hostnames you entered in the Upstream DNS servers section.

I don't think I changed the defaults, but here's what I'm using:

1.1.1.1
1.0.0.1
2606:4700:4700::1111
2606:4700:4700::1001
9.9.9.10
149.112.112.10
2620:fe::10
2620:fe::fe:10

Private reverse DNS servers

This section can be left at the defaults unless you're going to be using a separate internal DNS resolver for internal hosts. I'll provide my configuration below so you can get a feel for how it works.

# Use dns01 for rDNS for requests in the 10.0.0.0/8 space
[/10.in-addr.arpa/]10.0.0.10
# Use Cloudflare for all other rDNS requests
tls://1.1.1.1:853
tls://1.0.0.1:853

The config above instructs AdGuard Home to send rDNS requests for anything in the 10.0.0.0/8 address space to my internal host via plain DNS, and all other requests are directed to CloudFlare's 1.1.1.1 service via DNS-over-TLS.

The checkbox below the config field should only be checked if you're using a separate resolver for internal hostnames.

Finally, the checkbox for Enable reverse resolving of clients' IP addresses will allow AdGuard Home to display hostnames in addition to IP addresses for your internal hosts in its logs instead of just the client IP address.

Time to Test! Now that our config is completed, click the Test upstreams button to ensure everything is working as it should, then click Apply.

DNS server configuration

This section can be left at the defaults unless anything here piques your interest. You can find more info on this section in the AdGuard Home Wiki.

DNS cache configuration

I've left this section with the defaults as well. Refer to the Wiki for complete info on this section.

Access settings

This section will allow you to allow or disallow specific IP ranges (CIDR format, i.e., 1.2.3.4/24), IP addresses, or ClientIDs.

I'm not using this feature, but if you wanted to restrict your server so only certain clients can access it, or if you want to block particular clients, you'd do that here.

I've left the Disallowed domains field with the defaults.

⚠️ Once you've made any changes you'd like, click any one of the Save buttons or Save configuration at the bottom.

Encryption Settings

This section will remain out of scope for this article, as it'll require a custom SSL cert which is a whole document in itself. (Stay tuned, I'll probably do an article for that too!)

Refer to the AdGuard Home Wiki for complete information on this section.

Client Settings

This section is where you'll define any persistent clients, as well as view the list of "runtime clients". Runtime clients is a list of all clients that have queried the AdGuard Home server but are not defined as a persistent host.

For complete information, visit the AdGuard Home Wiki.

DHCP Settings

This will remain out of scope for this article as well, mostly because I'm not using the DHCP features at home due to the lack of multiple scopes. I might just be daft and not see how to do it, but for now, my setup works for me 🙂

As above, refer to the AdGuard Home Wiki for more information.

Wrap Up

That's it for now! I hope this tutorial has helped you set up a functioning AdGuard Home container for your home network! Feel free to ask questions in the comments, I'll try my best to answer anything that comes up.