Be Your Own Cloud code · PART 3 — THE STACK

Chapter 10 Pi-hole + Unbound: Network-Wide Ad Blocking and Private DNS

3 blocks · pp. 46–48 in the paperback

Going further than the chapter: Pi-hole + Unbound: Going Deeper

Running It in Docker, Not the Bare-Metal Installer

# p. 46

File /opt/stacks/pihole/docker-compose.yml

Needs PIHOLE_PASSWORD in /opt/stacks/pihole/.env.

services:
  unbound:
    image: klutchell/unbound:1.26.1
    container_name: unbound
    restart: unless-stopped
    networks:
      dns:
        ipv4_address: 172.30.0.2
  pihole:
    image: pihole/pihole:2026.09.0
    container_name: pihole
    restart: unless-stopped
    depends_on:
      - unbound
    environment:
      - TZ=Your/Timezone
      # web admin login, from .env
      - FTLCONF_webserver_api_password=${PIHOLE_PASSWORD}
      # Unbound's fixed address, below
      - FTLCONF_dns_upstreams=172.30.0.2#53
      # answer the LAN and tailnet, not just Docker's own network
      - FTLCONF_dns_listeningMode=all
    volumes:
      - ./etc-pihole:/etc/pihole
    ports:
      - '53:53/tcp'
      - '53:53/udp'
      - '8081:80'   # web admin
    networks:
      dns:
        ipv4_address: 172.30.0.3

networks:
  dns:
    ipam:
      config:
        - subnet: 172.30.0.0/24

# p. 47

Run Terminal, on the server — before the first docker compose up -d
sudo sed -i 's/^#\?DNSStubListener=.*/DNSStubListener=no/' \
  /etc/systemd/resolved.conf
sudo ln -sf /run/systemd/resolve/resolv.conf /etc/resolv.conf
sudo systemctl restart systemd-resolved

Making the Whole Network Use It

# p. 48

Web UI Pi-hole → Group Management → Lists

Add each address as a list, then update Gravity.

https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts
https://big.oisd.nl
https://raw.githubusercontent.com/hagezi/dns-blocklists/main/hosts/pro.txt

Reproduced exactly from Be Your Own Cloud, Chapter 10. The book explains what each line does and what to do when it doesn't work. About the book

Something here not working as the book describes? Tell the author.