Raspberry Pi 4: Setup AdGuard on Flatcar Container Linux

Life-is-short--so--enjoy-it
8 min readSep 25, 2023

--

Focusing on bringing up the AdGuard. It will be gradually optimized.

Raspberry Pi 4: Setup AdGuard on Flatcar Container Linux

Intro

In the previous post, I installed Flatcar Container Linux on Raspberry Pi 4.

Since it was my first time to install Flatcar on Raspberry Pi 4 ( I am new to Pi as well. ) and the steps required some manual works due to the missing OEM Image having Pi4 UEFI, it took some times to figure out how to setup it up. I believe ( or want to believe ) that it was worth spending time to try it :D

The Pi 4 that I installed the Flatcar is planed to build a local DNS to handle DNS lookup in my HomeLab. ( Maybe, it can be used a central machine to handle PXE boot, or etc. )

So, I setup the AdGuard on Pi 4. This is a documentation how I did it.

By the way, this post will focus on bringing the AdGuard service up first. ( not including optimization or details about AdGuard features )

AdGuard vs. Pi-Hole

My original plan was installing Pi-Hole. Why?? ( Well, maybe the board is Pi 4??.. Haha. )

When I shared the idea about this project installing Pi-Hole on Pi 4, one of my peers told that he hates Pi-Hole. ( Ok… ) and he prefers to use AdGuards.

As usual, I googled about the topic, AdGuard vs. Pi-Hole. It’s very opinionated topic. Depending on who you talk to, most likely you will get some level of opinionated answers.

The general answer can be “go with whatever you want to use”. Basically, the difference between AdGuard and Pi-Hole is very minor.

First, both AdGuard and Pi-Holes are FREE.

( NOTE: plz leave response if the difference is incorrect )

To AdGuard advocators, Pi-Holes are

  • missing some features ( as built-in ) that AdGuards already has ( e.g. DNS-over-TLS ). Those missing features can be configurable with some extra works in Pi-Holes
  • not a single service. Pi-Holes depends on some other services like DNSMASQ. Pi-Holes is a grouped services.
  • UI is not fancy.

To Pi-Holes advocators, AdGuards are

  • using more resources ( like CPU or Mem ), so it generates little more heat.
  • Not community driven project
  • UI is not refreshed automatically.

Since I more prefer to run a simple service, I changed my mind and decided to install AdGuard.

Overall Steps: Setting up AdGuard on Flatcar

The Flatcar is freshly installed. I haven’t done anything after installing Flatcar on the Pi 4.

Here are the steps.

  1. Update the DNS server address and Disable DNSStubListener ( resolved daemon on Flatcar ) and
  2. Create AdGuard config and work directories that will be mounted to AdGuard Container
  3. Bring up AdGuard container
  4. Setup the initial AdGuard config ( Admin UI port, DNS port, user/password )

Step 1: Update the DNS server address and Disable DNSStubListener

Actually, this was what I faced when I tried to bring up AdGuard container.

So, I had to update the DNS server address and disable DNSStubListener.

Flatcar Documenation about DNS resolution

DNS resolution in Flatcar is documented like below. ( This helps what the updated files are. )

By default, DNS resolution on Flatcar Container Linux is handled through /etc/resolv.conf, which is a symlink to /run/systemd/resolve/resolv.conf. This file is managed by systemd-resolved . Normally, systemd-resolved gets DNS IP addresses from systemd-networkd , either via DHCP or static configuration. DNS IP addresses can also be set via systemd-resolved’s resolved.conf .

Step 1.1: Check if port=53 in use

By using the commands below, it’s possible to check if the port=53 is in use.

( systemd-resolved listening on port 53 by default if running )

In my case, the systemd-resolved was using the port=53. ( If not, jump to Step 2 )

sudo lsof -i -P -n | grep LISTEN
sudo netstat -tulpn | grep LISTEN
AdGuard: conflict in port=53

Step 1.2: Pre-Check the status — Before the update

It’s always to good to check the content and the status before any modifications.

Here were the status and the content of the file I would modify.

  • /etc/resolv.conf, which is a symlink to /run/systemd/resolve/resolv.conf
  • the content of `/run/systemd/resolve/resolv.conf` like below. ( I am not sure why 192.168.2.1 — (my vlan 192.168.2.0/24) is defined as nameserver )
## Content of /run/systemd/resolve/resolv.conf

core@localhost ~ $ cat /run/systemd/resolve/resolv.conf
# This is /run/systemd/resolve/resolv.conf managed by man:systemd-resolved(8).
# Do not edit.
#
# This file might be symlinked as /etc/resolv.conf. If you're looking at
# /etc/resolv.conf and seeing this text, you have followed the symlink.
#
# This is a dynamic resolv.conf file for connecting local clients directly to
# all known uplink DNS servers. This file lists all configured search domains.
#
# Third party programs should typically not access this file directly, but only
# through the symlink at /etc/resolv.conf. To manage man:resolv.conf(5) in a
# different way, replace this symlink by a static file or a different symlink.
#
# See man:systemd-resolved.service(8) for details about the supported modes of
# operation for /etc/resolv.conf.

nameserver 192.168.2.1
search .

Step 1.3: Update DNS Address and Disable DNSStubListener

ref: https://hub.docker.com/r/adguard/adguardhome#resolved-daemon

The steps are

  • create /etc/systemd/resolved.conf.d/ if not exists.
  • create /etc/systemd/resolved.conf.d/adguardhome.conf with the content ( updated DNS Address and DNSStubListener-no )
# create the directory
sudo mkdir /etc/systemd/resolved.conf.d/

# switch to root user
sudo -s

# create a file with the content
# - change DNS Address to 127.0.0.1
# - disable DNSStubListener
cat >> /etc/systemd/resolved.conf.d/adguardhome.conf << EOL
[Resolve]
DNS=127.0.0.1
DNSStubListener=no
EOL

# reload or restart systemd-resolved with the updated config
systemctl reload-or-restart systemd-resolved

# exit from root user
exit
AdGuard: Update DNS Address and Disable DNSStubListener

Step 1.4: Post-Check the status — Before the update

  • Check if the port=53 is released
  • Check the new nameserver 127.0.0.1 in /etc/resolv.conf
sudo lsof -i -P -n | grep LISTEN
sudo netstat -tulpn | grep LISTEN
core@localhost ~ $ cat /etc/resolv.conf
# This is /run/systemd/resolve/resolv.conf managed by man:systemd-resolved(8).
# Do not edit.
#
# This file might be symlinked as /etc/resolv.conf. If you're looking at
# /etc/resolv.conf and seeing this text, you have followed the symlink.
#
# This is a dynamic resolv.conf file for connecting local clients directly to
# all known uplink DNS servers. This file lists all configured search domains.
#
# Third party programs should typically not access this file directly, but only
# through the symlink at /etc/resolv.conf. To manage man:resolv.conf(5) in a
# different way, replace this symlink by a static file or a different symlink.
#
# See man:systemd-resolved.service(8) for details about the supported modes of
# operation for /etc/resolv.conf.

nameserver 127.0.0.1
nameserver 192.168.2.1
search .

Step 2: Create AdGuard config and work directories that will be mounted to AdGuard Container

When creating AdGuard container, two paths will be mounted.

  • AdGuard data directory
  • AdGuard config directory

If the paths are NOT mounted, then the AdGuard’s config and data will be lost once the AdGuard container stopped.

( By the way, why does docker use root user when running container? maybe I should set and use a non-root user next time. )

# create adguards work and config directories
# since the parent directory is owned by root, sudo has to be used
sudo mkdir -p /mnt/dockers/adguards/{work,config}
AdGuard: Create AdGuard directories to mount to Container

Step 3: Bring up AdGuard container

ref: https://hub.docker.com/r/adguard/adguardhome#quickstart

There are more ports we might want to bind to enable some other services like

But, since I focused on bringing up the AdGuard first, I just used that I needed for now which are

  • AdGuard Admin Panel ( once setup ): -p 80:80/tcp
  • AdGuard Initial Setup UI: -p 3000:3000/tcp
  • Plain DNS: -p 53:53/tcp -p 53:53/udp

( NOTE: It might be safer to expose AdGuard Admin Panel through Reverse Proxy like Nginx, but again for now, I skipped it for now )

docker run --name adguardhome \
--restart unless-stopped \
-v /mnt/dockers/adguard/work:/opt/adguardhome/work \
-v /mnt/dockers/adguard/conf:/opt/adguardhome/conf \
-p 80:80/tcp -p 3000:3000/tcp \
-p 53:53/tcp -p 53:53/udp \
-d adguard/adguardhome
AdGuard: Bring up AdGuard container

Step 4. Setup the initial AdGuard config

Open browser with http://<Pi-IP>:3000 .

( Using the port=3000 is important. Once the initial setup is completed, the port won’t be listened by AdGuard anymore. )

AdGuard setup screen: http://<ip>:3000

The default value can be used, so just click next page. And, add your AdGuard login / password.

Once the initial configuration is completed and you click “Open Dashboard”, the you will see the screenshot below.

Summary

In this post, I mainly focused on bringing up the AdGuard service. It might miss several things ( even important things ).

I think the future tasks can be learning more about these features in AdGuard.

Previous

If you’re interested in installing Flatcar Container Linux on Raspberry Pi 4, here is the post you can take a look.

If you’re interested in purchasing a cost-efficient Raspberry Pi 4 required accesseries, you might want to take a look this post.

--

--

Life-is-short--so--enjoy-it

Gatsby Lee | Data Engineer | City Farmer | Philosopher | Lexus GX460 Owner | Overlander