ShieldDNS is a lightweight, efficient, and privacy-focused DNS-over-TLS (DoT) proxy.
It enables you to securely accept DNS queries (e.g., Android Private DNS) and forward them to your local DNS server (AdGuard Home, Pi-hole) or public resolvers.
- 🔒 DNS-over-TLS (DoT)
- 🌍 DNS-over-HTTPS (DoH): Supported on port 443 (or custom).
- 🚇 Cloudflare Tunnel Support: Expose your DoT/DoH server safely.
- 🚀 High Performance: Alpine + CoreDNS.
- 🪵 Flexible Logging: Configurable log levels.
services:
shielddns:
image: ghcr.io/faserf/shielddns:latest
ports:
- "853:853" # DoT
- "443:443" # DoH
environment:
- UPSTREAM_DNS=1.1.1.1
- CLOUDFLARE_TUNNEL_TOKEN=eyJh... # Optional
- ENABLE_INFO_PAGE=true # Optional: Enable Info Page on DoH Port
- LOG_LEVEL=info # debug, info, error
- CERT_FILE=/certs/fullchain.pem
- KEY_FILE=/certs/privkey.pem
volumes:
- ./certs:/certsYou can enable a lightweight "Fancy" Info Page to display a professional landing page for your DNS endpoint.
- Why? To inform visitors (or yourself) that this is a private DNS endpoint and not a public website.
- Enable: Set
ENABLE_INFO_PAGE=true. - Port: Default is
8080(mapped in Docker).
Since you are exposing a DNS server to the public (via Tunnel or Port Forwarding), you should secure it to prevent abuse (DNS Amplification, Scanning, DDoS).
Using Cloudflare Tunnel hides your Origin IP and allows you to use Cloudflare Zero Trust features.
- WAF / Custom Rules:
- Block Countries: Block all countries except your own.
- Block Bots: Enable "Bot Fight Mode" or block known bot User-Agents.
- Rate Limiting: Set a Rate Limiting rule for your hostname (e.g. max 50 requests / 10 seconds per IP) to prevent flooding.
- Zero Trust Authentication: If feasible, put the DNS endpoint behind Cloudflare Access (Note: This breaks standard DoH clients unless they support authentication headers. For a pure public endpoint, rely on WAF).
If running without Cloudflare (Direct Exposure):
- Whitelist IPs: Only allow your own mobile IP ranges or specific networks if possible.
- Fail2Ban: Monitor logs and ban abusive IPs (requires mounting logs to host).
- Limit Rates: Use
iptablesor UFW to limit connection rates on port 853/443.
- Android: Use strict Private DNS hostname. Android verifies the certificate chain.
- iOS: Use a
.mobileconfigthat enforces HTTPS and specific SNI.
To use ShieldDNS effectively, it helps to understand the two main protocols:
| Protocol | Port | Description | Android Support |
|---|---|---|---|
| DoT (DNS-over-TLS) | 853 (TCP) |
Uses a dedicated secure port. | Native Support. Used by "Private DNS" setting. |
| DoH (DNS-over-HTTPS) | 443 (TCP) |
Uses standard HTTPS web port. | Requires App (Intra/Nebulo) or Browser Config. |
| UDP/53 | 53 (UDP) |
Standard unencrypted DNS. | Legacy. Not supported by ShieldDNS (by design). |
Cloudflare Tunnel (without Enterprise/Spectrum) only proxies HTTP/HTTPS traffic (DoH). It does not proxy raw TCP (DoT/853).
- If you use Cloudflare Tunnel: You MUST use DoH (via an App on Android). "Private DNS" setting will FAIL because it tries to use DoT/853.
- If you use Port Forwarding: You can use both DoT (Private DNS) and DoH.
- A Public Domain (e.g.,
dns.example.com). - A Valid SSL Certificate for that domain.
- Android Private DNS checks for a valid chain (Let's Encrypt or similar).
- Cloudflare Tunnel: You can usage a Cloudflare Origin Certificate (lasts 15 years) on your server, and let Cloudflare Edge handle the valid public cert.
If you want "Native" Android support AND Cloudflare Tunnel, you need two DNS records.
- Type: CNAME (Proxied via Cloudflare Tunnel).
- Target: Your Tunnel ID.
- Tunnel Config: Service
HTTPS://192.168.1.x:3443(No TLS Verify). - iOS Profile: Use
https://doh.example.com/dns-query.
- Type: A/CNAME (DNS Only / Grey Cloud).
- Target: Your Home IP (DDNS).
- Router: Port Forwarding WAN:853 -> LAN:8853 (HA IP).
- Android Setting: Enter
dot.example.com. ⚠️ Restriction: You explicitly stated "No Port Forwarding". If you do not open Port 853, Method 1 (Native Private DNS) is IMPOSSIBLE. You must use Method 2 (App).
This gives you the best of both worlds: Tunnel security where possible, and raw TCP access where required.
Method 1: Native "Private DNS" (Cannot work with Tunnel-Only) Requires Port Forwarding (WAN 853). If you refuse to open ports, SKIP THIS.
- Ensure you have Port Forwarding (853->8853) and a Grey Cloud DNS record.
- Go to Settings > Network > Private DNS.
- Enter:
dot.example.com.
Method 2: App (Works with Tunnel-Only) This is your ONLY option if you refuse to open ports.
- Install Intra.
- URL:
https://doh.example.com/dns-query.
iOS supports native encrypted DNS via Configuration Profiles.
- Download Template: Get the mobileconfig-template.mobileconfig file from this repository.
- Edit: Open it with a text editor and replace
REPLACE_ME_DOMAINwith your domain (e.g.doh.example.com). - Install: Email/AirDrop file to device -> Settings > Profile Downloaded -> Install.
- Result: System-wide ad-blocking on 4G/5G/Wifi.
- Settings > Network > Ethernet/Wi-Fi > DNS settings > Edit.
- Set IPv4 DNS to
127.0.0.1(dummy) or actual server IP. - Set DNS over HTTPS to On (Manual).
- Template:
https://dns.example.com/dns-query.
This project is also available as a Home Assistant Addon. View on GitHub
