Standalone Pi-hole + DoH dnsproxy sidecar
Find a file
2026-09-27 01:31:40 +00:00
config/pihole Initial commit: standalone Pi-hole + dnsproxy DoH sidecar 2026-09-26 23:43:16 +00:00
.gitignore Initial commit: standalone Pi-hole + dnsproxy DoH sidecar 2026-09-26 23:43:16 +00:00
bootstrap.sh fix: use FTLCONF_webserver_port and disable systemd-resolved for host-mode Pi-hole v6 2026-09-27 01:31:40 +00:00
docker-compose.yml fix: use FTLCONF_webserver_port and disable systemd-resolved for host-mode Pi-hole v6 2026-09-27 01:31:40 +00:00
README.md fix: use FTLCONF_webserver_port and disable systemd-resolved for host-mode Pi-hole v6 2026-09-27 01:31:40 +00:00

BCS_PiHole_Project

Standalone Pi-hole DNS sinkhole with an AdGuard DNS-over-HTTPS (DoH) proxy sidecar.

This is a clean, deployable extraction of the Pi-hole + dnsproxy setup originally built for the Sawgrass Security Dashboard.

What it does

  • Pi-hole (pihole/pihole:latest) provides network-wide ad blocking, tracker blocking, and local DNS resolution.
  • dnsproxy (adguard/dnsproxy:latest) runs on 127.0.0.1:5335 and forwards all upstream queries to Cloudflare via encrypted DNS-over-HTTPS.
  • Pi-hole forwards its upstream queries to the local dnsproxy, falling back to plaintext Cloudflare malware-filtered resolvers (1.1.1.2 / 1.0.0.2) if the proxy is unavailable.

Ports

Port Protocol Purpose
53 TCP + UDP DNS service for clients
8080 TCP Pi-hole web admin interface
5335 TCP Internal DoH proxy (localhost only)

Requirements

  • Linux host with Docker and Docker Compose v2
  • host networking enabled (Pi-hole must bind directly to the host interface)
  • Firewall/UFW configured to allow inbound TCP/UDP 53 and TCP 8080 from trusted sources
  • A static host IP recommended (SERVERIP in .env)

Configuration

  1. Copy the example environment file and edit it:
cp .env.example .env
nano .env
  1. Set at least these variables in .env:
Variable Description
PIHOLE_PASSWORD Password for the Pi-hole web admin
SERVERIP IP address of the host interface clients use for DNS
PUID User ID for container file ownership (default 1000)
PGID Group ID for container file ownership (default 1000)
TZ Timezone (default America/New_York)

Host preparation

This project is designed for a dedicated Pi-hole host using network_mode: host. On such a host, systemd-resolved binds port 53 and will prevent Pi-hole from starting.

Run the provided host-prep script as root before the first deploy:

sudo ./bootstrap.sh

Or manually stop and disable systemd-resolved and point /etc/resolv.conf to 127.0.0.1.

⚠️ Only do this on a host where this Pi-hole instance is intended to be the DNS resolver. Rewriting /etc/resolv.conf can break DNS on shared or desktop systems.

Deploy

cd /home/secbeard/.openclaw/workspace/projects/BCS_PiHole_Project
docker compose up -d

Verify the containers are running:

docker compose ps
docker logs bcs-pihole
docker logs bcs-pihole-dnsproxy

If Pi-hole fails to start with a port 53 "Address in use" error, the host may already have another DNS resolver or a Sawgrass setup.sh DNS watchdog running. Stop or disable the conflicting service before deploying.

Pi-hole v6 runs as non-root UID 1000, so this compose file adds cap_add: [NET_BIND_SERVICE, NET_ADMIN, NET_RAW, SYS_NICE, SYS_TIME] so the container can bind privileged port 53 and manage DNS sockets. Without these capabilities, port binding will fail.

Web admin port

The web admin port is set to 8080. In Pi-hole v6 the legacy WEBPORT environment variable is no longer honored; the compose file uses FTLCONF_webserver_port so FTL overrides webserver.port in pihole.toml at runtime.

First use

  • Open the Pi-hole admin panel at http://<SERVERIP>:8080/admin
  • Log in with the password set in .env (PIHOLE_PASSWORD)
  • Point client devices to <SERVERIP> for DNS

Networking notes

  • Both services use network_mode: host so Pi-hole can listen on the host's real interfaces and dnsproxy can bind 127.0.0.1:5335 from the host network namespace.
  • DNSMASQ_LISTENING: local tells Pi-hole to listen on all local interfaces.
  • The web admin port is set to 8080 via FTLCONF_webserver_port because Pi-hole v6 does not honor the legacy WEBPORT variable.

Firewall / security

Ensure the host firewall allows the following from trusted client networks:

# UFW example
sudo ufw allow 53/tcp comment 'Pi-hole DNS TCP'
sudo ufw allow 53/udp comment 'Pi-hole DNS UDP'
sudo ufw allow from <TRUSTED_SUBNET> to any port 8080 proto tcp comment 'Pi-hole web admin'

The dnsproxy DoH sidecar listens only on 127.0.0.1:5335, so it is not externally reachable.

Updating

docker compose pull
docker compose up -d

Stopping

docker compose down        # stop containers, keep volumes
docker compose down -v     # stop and delete volumes (resets Pi-hole data!)

Repository