Post thumbnail

Install AdGuard Home with Docker on a Raspberry Pi 4

adguarddockerprivacydnsraspberry pi

The Raspberry Pi is a great little computer you can use for many different things. Unless you need a lot of processing power it’s good enough to run as a small home server. I’ve decided to use mine for some network related stuff since the fourth version has gigabit Ethernet. My Synology NAS will then take care of pure file related tasks like backups and media management. Separating physical devices this way can increase security and portability, even though containers and virtual machines makes the difference smaller.

AdGuard Home Dashboard interface

The first service we will add to the Raspberry Pi is a DNS server, AdGuard Home, running in its own docker container. Blocking trackers, analytics and privacy infringing ads on a DNS level is a great way to make it network wide since ad blockers like uBlock Origin only blocks this kind of stuff in the browser. This solution will work for all devices like your TV and smart home devices… if you really need them connected at all. Just remember that this is not a replacement of uBlock Origin, you need both.

The reason I’m not going with PiHole is the better interface and out-of-the-box solutions like encrypted DNS upstream servers you get with AdGuard Home. There is a company behind it, but the software is completely free and open source so not really much to complain about.

Set up the Raspberry pi with Ubuntu

Since this is a server without a GUI I decided to go with Ubuntu instead of Raspberry Pi OS, but it really doesn’t matter what you choose. For this guide it’s Ubuntu though.

Navigate to the Ubuntu download page and download the latest LTS 64-bit release. The installation steps are pretty much default for installing an OS on a removal drive, but the official guide is here.

When the OS is installed and you have logged in over ssh for the first time it's a good idea to update all the packages. sudo apt update and sudo apt upgrade, then we can reboot and head over to the next step.

Install Docker and Docker Compose

To be able to install AdGuard Home as a Docker container we first need to set up Docker itself. As well as Docker Compose for easier configuring of the container and network, especially when we add more containers later on.

Set up the Docker repository

First step is to add the Docker repository because it's the most up to date. We pick arm64 since that is the architecture of the Raspberry Pi 4.

sudo apt install apt-transport-https ca-certificates curl gnupg lsb-release
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo "deb [arch=arm64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

Install Docker Engine

Installing the latest version of Docker Engine is only one command now.

sudo apt install docker-ce docker-ce-cli containerd.io

We can test if everything is working by pulling and running a hello-world container.

sudo docker run hello-world

That should print some text that it’s running and then stop itself. Hopefully it's working 😉

Install Docker Compose

Docker Compose will make it easier for us managing the containers, especially when we need network configuration, since it’s all in on file. We need to install Docker Compose through pip because of the architecture Raspberry Pi is using. First we start with the dependencies

sudo apt install python3-pip libffi-dev

And then the actual package.

sudo pip3 install docker-compose

If everything went well you should be able to check the version with docker-compose --version.

Install Adguard Home

Now to the fun part, creating a Docker Compose file containing everything we need for running the service.

Create a new file called docker-compse.yml somewhere handy. Your home directory would probably be fine as this file will have the configuration for all your docker containers in it. Because ~/ usually is the default when login in through ssh you don't need to change location when managing containers.

Setting up the network parts

One important thing which actually needs some manual work from you is setting up the static IPs for AdGuard and the (future) other containers. So no copy/paste of commands as you did above 😄 but don't worry, I'll tell you how.

AdGuard Home needs a static IP assigned to it which accessible from all your devices so they can use that address as the DNS server. Since we are running AdGuard inside a container we need to connect the internal container network with the normal one you have at home, from your router. We'll do that with something called macvlan, so your router sees AdGuard Home as a physical device with its own MAC address.

First we need to decide what IP ranges our devices and services will have on our network. Unless you know differently I'd say you have some devices that have fixed IP addresses (either static or reserved in your DHCP server) and some that you just connect on the fly. I will assume your router (DHCP server) is assigning addresses with a format of 192.168.1.XXX, with the router itself on 192.168.1.1, the 192.168.1.0/24 subnet. If not, change accordingly.

We will then need to decide on three ranges. One range where you have your physical devices with fixed IPs, one with all the dynamic devices being assigned with DHCP and the third one which is you docker containers. Something like this:

  1. 192.168.1.0 -> 192.168.1.63 (192.168.1.0/26): Fixed IPs of physical devices you assign yourself (including router default).
  2. 192.168.1.64 -> 192.168.1.191 (192.168.1.64/25): Dynamic addresses assigned by your DHCP server.
  3. 192.168.1.192 -> 192.168.1.223 (192.168.1.192/27): Ip range of all docker containers on your Raspberry PI that should be accessible on router level.

You can organize this however you like but the most important thing is that you set the range of addresses your router is assigning devices with DHCP. This is so it doesn't interfere with the range of addresses we give our Docker containers on the Raspberry Pi. Keep in mind that the Raspberry Pi itself is not within the containers range as it is a physical device with an IP already. This is what it could look like in the DHCP settings of the router with the above example.

Settings of DHCP address pool

We will also create a network called vlan_bridge which simply put is for bunching together all the containers on the Raspberry Pi on the same subnet, so they can get internet access from the host and talk to each other. I’ve picked the subnet 192.168.10.0/24 (192.168.10.XXX) in this example.

Here is the finished docker-compose file for the examples above. Please read above before just copy pasting. The rest of the properties can be left as-is, if you want you can check them out here.

---
version: "2.1"

services:
  adguard:
    container_name: adguard
    image: adguard/adguardhome
    ports:
      - 53/tcp
      - 53/udp
      - 80/tcp
      - 3000/tcp
      - 67/udp
      - 68/tcp
      - 68/udp
      - 443/tcp
      - 853/tcp
    volumes:
      - adguard_work:/opt/adguardhome/work
      - adguard_conf:/opt/adguardhome/conf
    networks:
      macvlan:
        ipv4_address: 192.168.1.192 # this IP will show as a device on your router
      vlan_bridge:
        ipv4_address: 192.168.10.192 # Pick same last numbers as macvlan and use "vlan bridge subnet" (10)
    restart: unless-stopped

# Here we define what networks are available to use by the services above
# Make sure to set the config of both macvlan and vlan_bridge as described in the guide.
networks:
  macvlan:
    name: docker_macvlan
    driver: macvlan
    enable_ipv6: false
    driver_opts:
      parent: eth0
    ipam:
      config:
        - subnet: 192.168.1.0/24 # Set to same subnet as your router and other devices
          ip_range: 192.168.1.192/27 # Available range from 192 to 224.
          gateway: 192.168.1.1 # This is your router IP

  vlan_bridge:
    name: docker_vlan_bridge
    driver: bridge
    enable_ipv6: false
    ipam:
      config:
        - subnet: 192.168.10.0/24 # subnet for all containers 
          ip_range: 192.168.10.192/27 # for easier use, set to same range as under macvlan (192 to 224 here)
          gateway: 192.168.10.1 # for easier use, set to same last number as your router

# Here we define where to save the files that live outside the container
# Located under /var/lib/docker/volumes
volumes:
  adguard_work:
    name: adguard_work
  adguard_conf:
    name: adguard_conf

Then inside your home folder, you should then be able to start AdGuard Home with this command. It will first pull the image and then start the service.

sudo docker-compose up -d adguard

You can tail the logs to check if everything is going well with:

sudo docker-compose logs -f adguard

Set up and configure AdGuard Home

If everything is running smoothly we are almost done, just some final tweaks. Navigate to <adguard-macvlan-ip>:3000, in our case it was 192.168.1.192:3000, and we'll set the final configuration.

  • Admin interface listens on only the macvlan address, 192.168.1.192 in our example.
  • DNS Server can listen on all interfaces.
  • Set up a username and a strong password, then save it in your password manager 😎

On the last page you see a short guide of how to configure your router to tell all devices that your new AdGuard Home DNS Server should be used as default. This differs a lot between routers but this is what it could look like. Just make sure the second one is a fallback so every request still goes to your AdGuard Home, otherwise only put one DNS server.

Configured DHCP server in router

When that is done your DNS Server is up and running and hopefully can start blocking some trackers and ads. The default settings can be improved some, so let's do that.

Tweak the default settings of AdGuard Home

Navigate to the IP (macvlan) in your browser and login in with the credentials you entered in the previous step. Go to Settings and we'll change these values:

  • General Settings
    • Logs configuration: 7 days (unless you really want more)
    • Statistics configuration: 7 days (unless you really want more)
  • DNS Settings
    • Upstream DNS servers: You can add pretty much anyone that you feel is safe/fast. I picked two that works well for me.
      • https://doh.mullvad.net/dns-query
      • https://dns10.quad9.net/dns-query
    • Private DNS servers: Pick your real router (DHCP server), with this example it's 192.168.1.1.
    • Enable reverse resolving of clients' IP addresses: Checked

Add blocklists

Go to Filters -> DNS blocklist and add those you want, like a local blocklist if you find one in the presets. I'd also recommend adding the oisd list, it is aiming to be a one-list-block-all and it's working really well. The URL is https://abp.oisd.nl/, and it contains a couple of hundred thousand entries 🤤

Set AdGuard Home as the DNS server on the Raspberry Pi itself

All devices in your network will use this new DNS server when it's configured to do so in the router, but for the host device itself, it's a bit different. With macvlan, client interfaces can't communicate directly with the host, so we need to use our bridge network. Login to your Raspberry Pi and open the file /etc/systemd/resolved.conf with sudo. There you will need to uncomment DNS and FallbackDNS and add your own values. The DNS value should be the bridge address to AdGuard Home, 192.168.10.192 in our case.

[Resolve]
DNS=192.168.10.192
FallbackDNS=9.9.9.9
#Domains=
#LLMNR=no
#MulticastDNS=no
#DNSSEC=no
#DNSOverTLS=no
#Cache=no-negative
#DNSStubListener=yes
#ReadEtcHosts=yes

Enjoy 🥳