I’ve been running AdGuard Home on my Raspberry Pi for a while now, and it’s been a solid way to block ads and trackers across my entire home network. Recently, I switched to using Podman with quadlets for a cleaner, more systemd-integrated setup. It’s rootless, which feels safer on a device like the Pi. To make DNS-over-HTTPS (DoH) available when I’m away from home, I routed it through a Cloudflare Tunnel. This keeps things secure without opening ports on my router.
In this post, I’ll walk through the full setup step by step. I’ll also cover the risks of making any DNS service public and why DoH through a tunnel helps mitigate some of those issues.
Why This Setup?
Podman quadlets make managing containers feel like native systemd services. No need for extra scripts or docker-compose files. Running rootless avoids privilege escalation risks. AdGuard Home itself is lightweight and works great on a Pi 4 or 5. For remote access, Cloudflare Tunnel is zero-trust friendly: it outbound-connects from your Pi to Cloudflare, so no inbound ports.
The goal here is secure remote DoH access (e.g., for your phone on mobile data) without exposing the full AdGuard admin interface or plain DNS ports to the internet.
Prerequisites
- A Raspberry Pi running Podman (version 4.4+ for full quadlet support).
- Basic familiarity with systemd and Podman.
- A Cloudflare account and a domain you control.
- Directories for persistent data, like
/home/youruser/adguard/container/workdirand/home/youruser/adguard/container/confdir.
Create those directories and set permissions if needed:
mkdir -p /home/youruser/adguard/container/{workdir,confdir}
Step 1: Create the Quadlet Files
Quadlets go in ~/.config/containers/systemd/ for rootless user services.
AdGuard Pod
Create adguard-home.pod:
[Unit]
Description=AdGuard Home Pod
[Pod]
PublishPort=53:53/tcp
PublishPort=53:53/udp
PublishPort=67:67/udp
PublishPort=68:68/udp
PublishPort=80:80/tcp
PublishPort=443:443/tcp
PublishPort=443:443/udp
PublishPort=3000:3000/tcp
PublishPort=853:853/tcp
PublishPort=784:784/udp
PublishPort=853:853/udp
PublishPort=8853:8853/udp
PublishPort=5443:5443/tcp
PublishPort=5443:5443/udp
[Install]
WantedBy=default.target
This exposes all the ports AdGuard Home might need locally (DNS, DHCP if you use it, web UI, etc.). For the complete and official list of ports along with explanations, check out the AdGuard Home Docker wiki page here: https://github.com/AdguardTeam/AdGuardHome/wiki/Docker#create-and-run-the-container.
The ports in the quadlet match what’s recommended there pretty closely, including extras like DNS-over-QUIC (784/udp and 8853/udp) and DNSCrypt (5443). You can trim them down if you’re not using certain features, like DHCP or QUIC.
AdGuard container
Create adguardhome.container:
[Unit]
Description=AdGuard Home Container
[Container]
ContainerName=adguardhome
Image=adguard/adguardhome:latest
Pod=adguard-home.pod
Volume=/home/youruser/adguard/container/workdir:/opt/adguardhome/work:Z
Volume=/home/youruser/adguard/container/confdir:/opt/adguardhome/conf:Z
[Service]
Restart=always
[Install]
WantedBy=default.target
cloudflared container
Create cloudflared.container:
[Unit]
Description=Cloudflare Tunnel Container for AdGuard Home
[Container]
ContainerName=cloudflared
Image=docker.io/cloudflare/cloudflared:latest
Pod=adguard-home.pod
Exec=tunnel --no-autoupdate run
Environment=TUNNEL_TOKEN=your-tunnel-token-here
[Service]
Restart=always
[Install]
WantedBy=default.target
Replace your-tunnel-token-here with the token from your Cloudflare dashboard (more on that below).
Reload systemd and start everything:
systemctl --user daemon-reload
systemctl --user enable --now adguard-home-pod.service
Check status with systemctl --user status adguard-home-pod.service or podman ps.
Access the AdGuard setup wizard at http://your-pi-ip:3000. Configure your filters, upstream DNS, etc.
Step 2: Enable DNS-over-HTTPS in AdGuard Home
Before setting up the Cloudflare Tunnel for remote access, you need to enable encryption and DoH support in AdGuard Home.
Go to the AdGuard Home web interface > Settings > Encryption settings.
Enable “Enable DNS-over-HTTPS”.
To secure the connection properly with a valid certificate:
- In your Cloudflare dashboard, go to the Origin Certificates page (direct link: https://dash.cloudflare.com/?to=/:account/:zone/ssl-tls/origin, or navigate via SSL/TLS > Origin Server).
- Create an Origin Certificate for your DoH domain (e.g., doh.yourdomain.com). Let Cloudflare generate the private key, or upload your own if you prefer.
- Download the certificate (PEM format) and private key.
- In AdGuard Home Encryption settings:
- Paste the certificate chain into the “Certificates” field.
- Paste the private key into the “Private key” field.
- Set “Server name” to your DoH domain (e.g., doh.yourdomain.com).
Save the settings. AdGuard will now serve HTTPS on port 443 with this certificate.
Note: Cloudflare Origin Certificates are trusted only by Cloudflare. Cloudflare handles this fine in tunnel mode, as it expects and allows such origin certs for end-to-end encryption.
Step 3: Finding the Internal Service IP
AdGuard’s DoH endpoint is at /dns-query on port 443 (now with encryption enabled).
To find the pod’s internal IP for the HTTPS service:
podman exec -it adguardhome netstat -lnt
Look for the line listening on :443.
Since we are using a direct port mapping to the host system, the IP should be the IP of the Raspberry PI itself.
Step 4: Setting Up the Cloudflare Tunnel
In the Cloudflare dashboard:
- Go to Zero Trust > Networks > Tunnels.
- Create a new tunnel (choose Cloudflared).
- Copy the token. It goes into the quadlet above.
- In the tunnel settings, add a Public Hostname.
For the route:
- Subdomain: e.g., doh.yourdomain.com
- Service: HTTPS
- URL:
172.16.10.xxx(the internal IP from above) - Path:
/dns-query(this restricts to only DoH queries; root/would expose the web UI)
Info
Important: Specify the path /dns-query in the route. This way, only DoH requests hit your server.
Risks of Public DNS Servers
Running any public DNS resolver carries risks. The big one is abuse in DNS amplification DDoS attacks: attackers spoof the victim’s IP and send small queries to your open resolver (especially plain UDP port 53), getting large responses bounced back. Your server unwittingly floods the victim.
Open resolvers also attract cache poisoning attempts or get scanned for other exploits.
DoH is different. It’s over HTTPS, so source IPs can’t be easily spoofed (HTTPS handshakes require real connections). Responses aren’t much larger than requests, killing amplification. Plus, through Cloudflare Tunnel, traffic hits Cloudflare’s edge first. They scrub a lot of junk and you avoid direct exposure.
That said, it’s still a public service: rate limiting in AdGuard helps, but heavy legitimate use could strain your Pi and network.
Restricting Access Further
To minimize abuse, use Cloudflare’s tools:
- In the tunnel’s public hostname settings or Zero Trust Access policies, restrict by country (e.g., allow only your own).
- Or block/allow specific IP ranges/ASNs.
I strongly recommend allowing queries only from your country (or known IPs like your mobile carrier). Go to the WAF/Firewall rules or Access application policies. Create a rule like “Allow if Country is CH” (or whatever yours is), and block everything else.
This cuts down opportunistic scans and abuse from distant bots.
Final Thoughts
This setup gives you network-wide ad blocking at home plus secure remote DoH without port forwarding. The Pi handles it fine. Mine’s been stable for months.
If you’re paranoid (and you should be with anything public-facing), monitor logs in AdGuard for unusual query volumes.