Super Ad Blocking with AdGuardHome - Pihole - Unbound

Hi,

I’m new to Docker and have already like it a lot. After two weeks of self-learning Docker Compose and Portainer, and through extensive trial and error, I’ve successfully set up a robust ad-blocking solution in Docker. This setup combines two renowned ad-blocking images—AdGuard Home and Pi-hole—with Unbound image.

The collaboration between AdGuard Home and Pi-hole effectively blocks all ads. I tested the setup using two well-known ad test sites and saw zero ad.

fuzzthepiguy.tech/adtest
canyoublockit.com/extreme-test

Additionally, I incorporated the Nginx and Watchtower images to complete the setup.

Watchtower to automate the updating of all service latest images.
Nginx to add proxy hosts to map service names to IP:port:
nginx.lan → 192.168.1.18:81
adguard.lan → 192.168.1.18:82
pihole.lan/admin → 192.168.1.18:83/admin
portainer.lan → 192.168.1.18:9002

Then I added a DNS rewrite record in AdGuardHome to rewrite all domain.lan to nginx
*.lan → 192.168.1.18

I’m sharing my docker-compose.yml for this comprehensive ad-blocking project with the forum, hoping it will be useful for newcomers. You can use it with Docker Compose or Portainer Stack.

docker-compose.yml

volumes:
  nginx_data:
  nginx_letsencrypt:
  unbound_etc:
  pihole_pihole:
  pihole_dnsmasq:
  adguard_workdir:
  adguard_confdir:
  
services:

  nginx:
    image: jc21/nginx-proxy-manager:latest
    container_name: nginx
    environment:
      - TZ=Asia/Singapore
      - INITIAL_ADMIN_EMAIL=my@example.com
      - INITIAL_ADMIN_PASSWORD=mypassword1
    ports:
      - 80:80
      - 81:81
      - 443:443
    volumes:
      - nginx_data:/data
      - nginx_letsencrypt:/etc/letsencrypt
    restart: unless-stopped

  unbound:
    image: mvance/unbound:latest
    container_name: unbound
    environment:
      - TZ=Asia/Singapore
    ports:
      - 5335:53/tcp
      - 5335:53/udp
    volumes:
      - unbound_etc:/opt/unbound/etc/unbound
    restart: unless-stopped

  pihole:
    image: pihole/pihole:latest
    container_name: pihole
    environment:
      - TZ=Asia/Singapore
      - WEBPASSWORD=yourpassword1
      # Configure pihole FTL local IP address. Replace 192.168.1.18 with IP address of docker host.
      - FTLCONF_LOCAL_IPV4=192.168.1.18
      - DNSSEC=true
      - DNSMASQ_LISTENING=single
      # Configure unbound as upstream DNS of pihole. Replace 172.18.0.1 with gateway IP address of docker network. run docker network ls to find network name. run docker inspect <network name> to find getway IP address.
      - PIHOLE_DNS_=172.18.0.1#5335
    ports:
      - 5353:53/tcp
      - 5353:53/udp
      # Access pihole admin web interface at 192.168.1.18:83/admin in your browser. Replace 192.168.1.18 with the IP address of your docker host.
      - 83:80
    volumes:
      - pihole_pihole:/etc/pihole
      - pihole_dnsmasq:/etc/dnsmasq.d
    restart: unless-stopped

  adguardhome:
    image: adguard/adguardhome:latest
    container_name: adguardhome
    environment:
      - TZ=Asia/Singapore
    ports:
      - 53:53/tcp
      - 53:53/udp
      - 67:67/udp
      - 68:68/udp
      # Access adguardhome admin web interface at 192.168.1.18:82 in your browser. Replace 192.168.1.18 with the IP address of your docker host.
      - 82:80/tcp
      - 444:443/tcp
      - 444:443/udp
      # Access adguardhome initial setup web interface at 192.168.1.18:3000 in your browser. Replace 192.168.1.18 with the IP address of your docker host.
      - 3000:3000/tcp
      - 853:853/tcp
      - 784:784/udp
      - 853:853/udp
      - 8853:8853/udp
      - 5443:5443/tcp
      - 5443:5443/udp    
    volumes:
      - adguard_workdir:/opt/adguardhome/work
      - adguard_confdir:/opt/adguardhome/conf
    restart: unless-stopped
  # 1. Initial setup of adguardhome:
  # Once the adguardhome container is up and running, access the web interface at 192.168.1.18:3000 in your browser. Replace 192.168.1.18 with the IP address of your docker host.
  # Follow the on-screen instructions to complete the initial setup, which includes creating an admin username and password. You may accept all the default settings provided.

  # 2. Configure pihole as upstream DNS of adguardhome:
  # After completing the adguardhome initial setup, access the web interface at 192.168.1.18:82 in your browser. Replace 192.168.1.18 with the IP address of your docker host.
  # Navigate to Settings → DNS Settings. In the Upstream DNS servers section, add pihole as an upstream DNS by entering 172.18.0.1:5353. Replace 172.18.0.1 with gateway IP address of docker network.

  watchtower:
    image: containrrr/watchtower:latest
    container_name: watchtower
    environment:
      - TZ=Asia/Singapore
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    restart: unless-stopped

Just out of curiosity: Why 3 DNS server components? Why two adblocker components that use DNS blocklists? What’s the advantage of this seemingly over-engineered example?

I recently need an adblocker, had to decide between AdGuard Home and Pi-Hole. Went with simple AdGuard Home as it supports DNS blocklists, but can also act as a proxy with TLS cert, so I can filter URL paths in the future if I want to.

Why use 2 adblockers? Ans: to block all ads.

If you use only one adblocker, you can test it by visiting the 2 ad test sites to test if one adblocker can block all ads on these test sites?

Both use DNS blocklists. Can’t you simply load blocklists from both in one service?

Yes, can load blocklists from both in one service adguardhome or pihole but using two services still better than one service.

Hi,

I modified unbound container to use local root.hints file and schedule a cron job to update the root.hints file weekly. Below are changes that I made to the unbound container:

  • Installed wget and cron.
  • Used wget to download the latest root.hints file.
  • Used cron to schedule a cron job to update the local root.hints file weekly.
  • Added root-hints option to the unbound.conf file.

If you also like to configure your unbound container to use local root.hints file, you can run the following 3 commands in an SSH root session after your unbound container is created and running:

docker exec unbound /bin/sh -c '
apt-get update && apt-get install -y wget cron && \
wget -O /opt/unbound/etc/unbound/root.hints https://www.internic.net/domain/named.cache && \
(crontab -l 2>/dev/null; echo "0 0 * * 0 wget -O /opt/unbound/etc/unbound/root.hints https://www.internic.net/domain/named.cache && unbound-control reload") | crontab - && \
service cron start
'
docker exec unbound /bin/sh -c '
ROOT_HINTS_LINE="    root-hints: \"/opt/unbound/etc/unbound/root.hints\"";
CONF_FILE="/opt/unbound/etc/unbound/unbound.conf";

grep -qxF "$ROOT_HINTS_LINE" "$CONF_FILE" || \

sed -i "/^server:/a $ROOT_HINTS_LINE" "$CONF_FILE";

unbound-checkconf "$CONF_FILE" && echo "Config is valid" || echo "Error in config!"
'

docker restart unbound

I added mardkdown code blocks to your post the same way as in your first post 10 says ago. You should be able to check the change history of the message by clicking on the pencil icon at the top of the message. But you can also read our formatting guide so you can format your posts without risking missing charactrs or even lines in your messages next time.

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.