Pi-hole 6, DoH, and Docker
I recently upgraded both of my Pi-Holes to version 6.
Not much to really write about, but there are some differences for some edge use cases, none of which impacted myself.
I wanted to implement DNS over HTTPS (DoH), which adds an additional layer of security to your network.
If you are running Pi-hole on a Unifi UDM, see this post: https://www.nodinrogers.com/post/2025-09-15-pihole6-doh-unifi/
DNS query/response basics
You open your web browser and go to https://yahoo.com.
Your computer has no idea what a yahoo.com is, but it does know what an IP address is, and DNS translates hostnames (yahoo.com) to an IP address.
Your computer, under the hood, uses the IP address associated with yahoo.com and sends you on your merry way.
The problem with traditional DNS
However, DNS queries/responses are sent in plain text by default.
Easy to troubleshoot, but also easy to see/manipulate for any bad actor.
As you can see from the packet capture snippet below, DNS queries/responses are very easy to read.
No big deal, but you have some Internet Service Providers (ISP) that track this information, and sell it to advertisers.
Worse, unencrypted DNS traffic can be intercepted and misdirected.
For example, if you wanted to go to your online bank website, and a bad actor intercepted the DNS query, they would provide an IP address that would send you to a website that looks like your bank's website, but is actually a fraudulent website, trying to extract personal information.
Hello DNS over HTTPS (DoH)
To prevent us from being tracked, and to secure out internet activity, we can implement DNS over HTTPS (DoH), which sends DNS queries/responses over HTTPS (encrypted).
Someone trying to snoop in on your DNS queries/responses after that is only going to see HTTPS data:
Traffic flow with DNS using DoH
Locally, your computer will send an unencrypted DNS request to the DNS server (Pi-hole), and Pi-hole, assuming it doesn't have an entry for that hostname already, will send an encrypted DNS (using DoH) query to our configured upstream DNS server (Cloudflare).
DNS queries will following this traffic pattern:
-
Client >>> local DNS server (Pi-hole), unencrypted DNS query, using port 53
-
Local DNS server (Pi-hole) >>> upstream DNS server (Cloudflare), encrypted DNS query, using port 443
-
Upstream DNS server (Cloudflare) >>> Local DNS server (Pi-hole), encrypted DNS response, using port 443
-
Local DNS server (Pi-hole), unencrypted DNS response, using port 53
Implement DoH for Pi-hole in Docker
This was my baseline Pi-hole, version 6, compose.yaml file:
services:
pihole:
container_name: pihole
hostname: pihole-1
# image: pihole/pihole:latest
image: pihole/pihole:2025.07.1
ports:
- "53:53/tcp"
- "53:53/udp"
- "67:67/udp"
- "8880:80/tcp"
- "8443:443/tcp"
environment:
TZ: 'America/Denver' #change to your local timezone
networks:
Internal:
ipv4_address: 172.28.0.5
net192:
ipv4_address: 192.168.1.5
volumes:
- './etc-pihole/:/etc/pihole/'
- './etc-dnsmasq.d/:/etc/dnsmasq.d/'
dns:
- 127.0.0.1
- 1.1.1.1
restart: unless-stopped
When initially deploying a new service, I specify the latest version, but after I get it working, I specify a specific tag, which is why you see pihole/pihole:latest and pihole/pihole:2025.07.01
Adding the cloudflared container and settings to the existing compose.yaml file for Pi-hole:
cloudflared:
# image: cloudflare/cloudflared:latest
image: cloudflare/cloudflared:2025.8.1
container_name: cloudflared
network_mode: service:pihole
command: proxy-dns
environment:
- "TUNNEL_DNS_UPSTREAM=https://1.1.1.1/dns-query,https://1.0.0.1/dns-query"
- "TUNNEL_DNS_PORT=5053"
- "TUNNEL_DNS_ADDRESS=0.0.0.0"
restart: unless-stopped
Environment variables explained:
- TUNNEL_DNS_UPSTREAM - What to use as the upstream DNS servers
- TUNNEL_DNS_PORT - Which port for containerd to listen on
- TUNNEL_DNS_ADDRESS - IP address to listen on, in this case, all addresses
The network_mode specifies to attach to the same network as the service, in this case, pihole.
Configure Pi-hole to use the cloudflared tunnel
Before adding the cloudflared container, Pi-hole was configured to use Cloudflare's public DNS servers, 1.1.1.1 and 1.0.0.1.
Once adding the cloudflared container, we need to configure Pi-hole to use DoH for the upstream DNS requests.
We can configure Pi-hole to use this either using a Docker environment variable or set it in the web UI.
Set upstream DNS server as a Docker environment variable
Under the pihole service definition, add the variable (in Pi-hole 6.x) FTLCONF_dns_upstreams:
FTLCONF_dns_upstreams: 127.0.0.1#5053
This configures Pi-hole to use the 127.0.0.1 server (localhost) and port 5053. As the cloudflared container runs on the same network as pihole, this works.
The major downside to using a Docker environment variable is that you can NOT change it in the web UI afterwords. You have to change it using the Docker environment variable, or remove it and add it using the web UI.
Set upstream DNS server in the Pi-hole web UI
Configure Pi-hole to use the local cloudflared service as the upstream DNS server by specifying 127.0.0.1#5053 as the Custom DNS (IPv4).
Settings >>> DNS, under Upstream DNS Servers, uncheck all DNS servers.
Under Custom DNS servers, add:
127.0.0.1#5053
Click Save & Apply at the bottom.
Test new settings
Using a browser, simply open https://1.1.1.1/help and look at the results.
If everything is configured correctly, you should see Yes for the section Using DNS over HTTPS (DoH):
Complete compose.yaml file
For prosperity, this is my full compose.yaml file for Pi-hole and cloudflared:
services:
pihole:
container_name: pihole
hostname: pihole-1
image: pihole/pihole:2025.07.1
ports:
- "53:53/tcp"
- "53:53/udp"
- "8880:80/tcp"
- "8443:443/tcp"
environment:
TZ: 'America/Denver'
# Variable for custom DNS server(s)
FTLCONF_dns_upstreams: 127.0.0.1#5053
networks:
Internal:
ipv4_address: 172.28.0.5
net192:
ipv4_address: 192.168.1.5
volumes:
- './etc-pihole/:/etc/pihole/'
- './etc-dnsmasq.d/:/etc/dnsmasq.d/'
dns:
- 127.0.0.1
- 1.1.1.1
restart: unless-stopped
cloudflared:
image: cloudflare/cloudflared:2025.8.1
container_name: cloudflared
# Attach to the same network as the pihole service
network_mode: service:pihole
command: proxy-dns
environment:
- "TUNNEL_DNS_UPSTREAM=https://1.1.1.1/dns-query,https://1.0.0.1/dns-query"
- "TUNNEL_DNS_PORT=5053"
- "TUNNEL_DNS_ADDRESS=0.0.0.0"
restart: unless-stopped
networks:
Internal:
external: true
net192:
external: true
References
Pi-hole documentation / Guides / DNS / cloudflared (DoH)
https://docs.pi-hole.net/guides/dns/cloudflared/
Github / cloudflare / cloudflared
https://github.com/cloudflare/cloudflared