Skip to content

Pi-hole with an Encrypted DNS-over-HTTPS Upstream

Pi-hole filters DNS requests for devices that use it as their resolver. This walkthrough runs Pi-hole and AdGuard’s dnsproxy in one Docker Compose project; Pi-hole sends upstream requests to dnsproxy, which forwards them to Cloudflare over DNS-over-HTTPS (DoH).

DoH encrypts the connection between dnsproxy and Cloudflare. The DNS provider can still see the queries, and observers can see the encrypted connection to that provider. This setup does not prevent every kind of tracking or replace endpoint security.

Use a server with a reserved LAN address. Replace 192.168.1.100 and 192.168.1.0/24 below with your server address and LAN subnet. Confirm host port 53 is available before starting the stack.


Step 1: Prepare a Private Admin Password

01

Create the Compose Directory and Password Secret

Preparation

Keep the Pi-hole password in a local Compose secret file instead of writing it into the Compose document. Run these commands as the account that will run Docker Compose. The secret and local ignore file should stay out of version control.

Terminal window
mkdir -p "$HOME/pihole"
cd "$HOME/pihole"
umask 077
openssl rand -base64 32 > pihole_webpasswd
printf '\npihole_webpasswd\n' >> .gitignore
❯ View Expected Console Output

The password file is created with owner-only permissions. Store a separate copy of the generated password in your approved password manager if you need to sign in later.


Step 2: Define Pi-hole and the DoH Proxy

02

Configure the Two Services on One Private Docker Network

Docker Compose

Save this as compose.yaml. Both containers join the Compose project’s private network, so Pi-hole can reach the proxy by its service name. The DNS proxy is not published on a host port. Pi-hole’s DNS and admin ports bind only to the server’s LAN address; this requires that address to be assigned before Compose starts.

services:
dnsproxy:
image: adguard/dnsproxy:latest
command:
- -l
- 0.0.0.0
- -p
- "5053"
- -u
- https://cloudflare-dns.com/dns-query
- -b
- 1.1.1.1:53
restart: unless-stopped
pihole:
container_name: pihole
image: pihole/pihole:latest
depends_on:
- dnsproxy
ports:
- "192.168.1.100:53:53/tcp"
- "192.168.1.100:53:53/udp"
- "192.168.1.100:8080:80/tcp"
environment:
TZ: "Etc/UTC"
WEBPASSWORD_FILE: pihole_webpasswd
FTLCONF_dns_listeningMode: "ALL"
FTLCONF_dns_upstreams: "dnsproxy#5053"
volumes:
- "./etc-pihole:/etc/pihole"
secrets:
- pihole_webpasswd
restart: unless-stopped
secrets:
pihole_webpasswd:
file: ./pihole_webpasswd
❯ View Expected Console Output

Pi-hole listens on the LAN address at DNS port 53 and the admin page at port 8080. dnsproxy listens on port 5053 inside the Compose network; Pi-hole uses that service name as its only upstream.


Step 3: Start the Stack and Verify DNS

03

Launch the Containers and Query Pi-hole

Verification

Start the services, check that both containers are running, then send a test query to the Pi-hole address. Open the admin page from a trusted LAN device at http://192.168.1.100:8080/admin and sign in with the password stored in the secret file.

Terminal window
cd "$HOME/pihole"
docker compose up -d
docker compose ps
dig @192.168.1.100 example.com
❯ View Expected Console Output
NAME IMAGE STATUS
<project>-dnsproxy-1 adguard/dnsproxy:latest Up
<project>-pihole-1 pihole/pihole:latest Up
;; ANSWER SECTION:
example.com. <TTL> IN A <address returned by the upstream>

Step 4: Set Pi-hole as the LAN DHCP DNS Server

04

Update the Router's LAN DHCP DNS Option

Client Setup

In the router’s LAN DHCP settings, set the DNS server handed to clients to 192.168.1.100, then renew a test client’s DHCP lease. Keep the router’s WAN or upstream DNS setting separate; pointing Pi-hole’s upstream back to Pi-hole can create a loop. If your network advertises IPv6 DNS through router advertisements, configure Pi-hole for IPv6 too or clients may continue bypassing it.

Terminal window
ipconfig /release
ipconfig /renew
ipconfig /flushdns
nslookup example.com 192.168.1.100
❯ View Expected Console Output
Server: pi.hole
Address: 192.168.1.100
Name: example.com
Addresses: ...

References

Comments