Pi-hole blocks ads and trackers, but every query that survives the blocklist goes somewhere — and by default that somewhere is a plaintext request to whatever public resolver Pi-hole selected at install time. Choosing a deliberate custom upstream server is how you close that gap: encrypting outbound queries, keeping your ISP out of your browsing history, or routing through a local recursive resolver so no third party ever sees your traffic at all.

What Upstream DNS Means Inside Pi-hole

Pi-hole sits between your devices and the internet. Your router's DHCP server hands out Pi-hole's IP as the DNS server for every device on the network. When a device queries a domain, Pi-hole checks its blocklists first. If the domain is blocked, it returns 0.0.0.0 and the request dies. If the domain is allowed, Pi-hole forwards the query to its upstream resolver — the server responsible for doing the actual DNS lookup — and relays the answer back to the device.

That upstream resolver is the critical link. Whatever IP address Pi-hole forwards to sees every non-blocked query your household or office generates. The default selections (Google 8.8.8.8, Cloudflare 1.1.1.1, and others) are functional, but they are not the only options and are not always the best ones. Custom upstream configuration lets you target any resolver — public or private, standard DNS or encrypted DNS-over-TLS — and that decision has real privacy and performance consequences.

When Custom Upstream Matters

Most Pi-hole users reach this configuration for one of these reasons, ranked by how often they come up:

  • Unbound (local recursive resolver) — query root servers directly without any third-party intermediary; maximum privacy, no data leaves your network to a named company.
  • ISP resolver avoidance — many residential ISPs inject their own IP into NXDOMAIN responses (ad injection) or log queries commercially; a custom upstream skips their resolver entirely.
  • DNS-over-TLS or DNS-over-HTTPS — encrypt queries before they leave your Pi so your ISP cannot read them in transit even when using a public resolver.
  • Internal network DNS — a Windows Server, Active Directory, or home lab DNS server knows your local hostnames; Pi-hole should forward to it before reaching for a public resolver.
  • Stacked filtering — using NextDNS or AdGuard DNS as upstream adds a second threat-intelligence layer beneath Pi-hole's blocklists without replacing them.
  • Compliance or VPN requirements — corporate VPNs often mandate that DNS queries traverse their servers; the custom upstream field handles this without modifying anything else.

Configuring Custom Upstream in Pi-hole v5

Pi-hole v5 (still widely deployed as of 2026) uses a PHP-based admin interface at http://pi.hole/admin or http://[your-pi-ip]/admin.

Admin Panel Method

  1. Log in and navigate to Settings → DNS.
  2. On the left column, uncheck all preset resolver boxes — Google, Cloudflare, OpenDNS, Quad9, and any others currently ticked. Pi-hole round-robins between all active upstreams, so leaving a preset checked while adding a custom entry means a portion of queries still go to the preset.
  3. On the right column, locate Custom 1 and Custom 2 under "Custom DNS Servers."
  4. Enter your upstream addresses. For standard DNS on port 53, enter just the IP (e.g., 9.9.9.9). For a non-standard port, use IP#port format — for example, 127.0.0.1#5335 for a local Unbound instance running on port 5335.
  5. For IPv6 upstreams, fill in the Custom 1 (IPv6) and Custom 2 (IPv6) fields directly below.
  6. Scroll to the bottom and click Save.

After saving, reload the DNS service to apply the change immediately:

pihole restartdns pihole status

Command-Line Method (v5)

Pi-hole v5 stores upstream configuration in /etc/pihole/setupVars.conf. Read the current values first:

grep PIHOLE_DNS /etc/pihole/setupVars.conf

You'll see entries like PIHOLE_DNS_1=8.8.8.8 and PIHOLE_DNS_2=8.8.4.4. Edit them directly:

sudo nano /etc/pihole/setupVars.conf # Change PIHOLE_DNS_1 and PIHOLE_DNS_2 to your custom values # Save with Ctrl+X then Y then Enter pihole restartdns

Important caveat: the admin panel overwrites setupVars.conf whenever you save settings through the GUI. Use the panel as the primary edit path and reserve direct file editing for scripted or headless deployments.

Configuring Custom Upstream in Pi-hole v6

Pi-hole v6, released in February 2025, replaced the PHP interface with a Go-based API and a fully redesigned frontend. The DNS settings moved in the UI but the underlying logic is the same.

  1. Open http://pi.hole/admin — the new interface has a left sidebar and darker default theme.
  2. Click Settings in the left sidebar, then select the DNS tab at the top.
  3. Scroll down to Upstream DNS servers.
  4. Toggle on the Custom server rows and enter your upstream IPs. Use IP#port for non-standard ports.
  5. Click Save & Apply — v6 applies the change live without a full service restart.

Pi-hole v6 stores its configuration in /etc/pihole/pihole.toml instead of setupVars.conf. To inspect or override upstream values from the CLI:

sudo grep -A5 "\[dns\]" /etc/pihole/pihole.toml # Override the upstream list directly via pihole-FTL: sudo pihole-FTL --config dns.upstreams '["127.0.0.1#5335"]'
💡 After switching upstream resolvers, verify your domain's public records still look right. Use our DNS Propagation Checker to confirm records are resolving correctly from multiple global vantage points — useful if you're also running split-horizon DNS for any domains you manage.

Choosing a Public Custom Upstream

If a local recursive resolver isn't the goal and you want a trustworthy public resolver, here are the main options with honest trade-offs:

  • Cloudflare 1.1.1.1 — 1.1.1.1 / 1.0.0.1 (IPv6: 2606:4700:4700::1111 / 2606:4700:4700::1001). Fastest median latency globally. No-log policy backed by an annual KPMG audit. Best default public choice.
  • Quad9 9.9.9.9 — 9.9.9.9 / 149.112.112.112 (IPv6: 2620:fe::fe / 2620:fe::9). Non-profit operator. DNSSEC-validating by default. Blocks known malware and phishing domains at the resolver level — a useful second filter layer beneath Pi-hole's blocklists.
  • Google Public DNS — 8.8.8.8 / 8.8.4.4 (IPv6: 2001:4860:4860::8888 / 2001:4860:4860::8844). Extremely reliable and fast. Google retains query data for a period per their Public DNS documentation — weigh that against the reliability.
  • OpenDNS — 208.67.222.222 / 208.67.220.220. Cisco-owned. Supports optional category-based content filtering via resolver policy, useful for parental controls or network-wide enforcement without relying only on Pi-hole blocklists.

Setting Up Unbound as a Local Recursive Upstream

Unbound performs recursive resolution itself — it queries root servers, then TLD servers, then authoritative servers, caching results locally. No third party sees your queries. This is the highest-privacy upstream configuration and the most requested custom setup for Pi-hole.

Step 1 — Install Unbound

sudo apt update && sudo apt install unbound -y

Step 2 — Create the Pi-hole Configuration File

sudo nano /etc/unbound/unbound.conf.d/pi-hole.conf

Paste the following and save:

server: verbosity: 0 interface: 127.0.0.1 port: 5335 do-ip4: yes do-udp: yes do-tcp: yes do-ip6: no harden-glue: yes harden-dnssec-stripped: yes use-caps-for-id: no edns-buffer-size: 1232 prefetch: yes num-threads: 1 so-rcvbuf: 1m private-address: 192.168.0.0/16 private-address: 169.254.0.0/16 private-address: 172.16.0.0/12 private-address: 10.0.0.0/8 private-address: fd00::/8 private-address: fe80::/10

Step 3 — Start Unbound and Test It Directly

sudo systemctl start unbound sudo systemctl enable unbound # Test Unbound before touching Pi-hole dig @127.0.0.1 -p 5335 google.com

You should get a valid A record and status: NOERROR. If you see SERVFAIL, stop here and diagnose before pointing Pi-hole at it:

sudo journalctl -u unbound -n 50

Step 4 — Point Pi-hole at Unbound

In the Pi-hole admin panel (Settings → DNS in v5, or Settings → DNS tab in v6), enter 127.0.0.1#5335 as the sole custom upstream. Uncheck every preset resolver box. Save, then restart Pi-hole's DNS engine:

pihole restartdns

Step 5 — Verify the Full Chain

# From the Pi itself dig @127.0.0.1 cloudflare.com # From a client device on the network dig @[pi-hole-ip] cloudflare.com

Successful resolution from a client confirms the complete path: client → Pi-hole → Unbound → authoritative DNS servers. In Tools → Query Log inside Pi-hole admin, forwarded entries should show 127.0.0.1#5335 as the destination.

IPv6 Upstream DNS

On dual-stack networks, configure IPv6 upstreams to prevent AAAA query leakage. Without them, IPv6 DNS queries may fall back to a resolver that bypasses Pi-hole entirely — a common source of unexplained tracking that persists even after Pi-hole is confirmed working.

In Pi-hole v5, use the "Custom 1 (IPv6)" and "Custom 2 (IPv6)" fields beneath the standard custom fields. In v6, add IPv6 rows in the upstream panel. For Unbound with IPv6 support, change do-ip6: no to do-ip6: yes in your config, then restart and test:

sudo systemctl restart unbound # Verify AAAA resolution through Unbound dig @127.0.0.1 -p 5335 AAAA google.com

CLI Verification After Any Upstream Change

Never assume a change applied correctly. Run these checks from the Pi after every upstream modification:

# Confirm resolution via Pi-hole DNS engine dig @127.0.0.1 google.com # See which upstream answered (look for the ;; SERVER: line) dig @127.0.0.1 google.com +stats # v5: confirm stored upstream values grep PIHOLE_DNS /etc/pihole/setupVars.conf # v6: confirm active upstream config sudo pihole-FTL --config dns.upstreams # Check for port 53 conflicts sudo ss -tlnp | grep :53 # Verify systemd-resolved isn't intercepting resolvectl status # Flush DNS cache and confirm service running pihole restartdns && pihole status

From a Windows client on the same network:

nslookup google.com [pi-hole-ip]

From a macOS or Linux client:

dig @[pi-hole-ip] google.com

Use our DNS Lookup tool to run an external DNS check on any domain and confirm it resolves correctly from outside your network — useful for ruling out upstream issues that only appear to affect external-facing DNS.

Common Misdiagnoses and Pitfalls

Preset resolvers left active alongside custom

Pi-hole distributes queries across all active upstreams. If Google 8.8.8.8 is still checked while your custom entry is also set, roughly half your queries go to each. The symptom is inconsistent query log behavior — some entries show the custom IP, others show 8.8.8.8. Fix: uncheck every preset before entering a custom value.

Port 53 conflict with systemd-resolved

On Raspberry Pi OS Bookworm and Ubuntu Server, systemd-resolved binds port 53 by default. If Pi-hole tries to bind the same port at startup, it silently fails. Diagnose with:

sudo ss -tlnp | grep :53

If systemd-resolve appears, disable it and fix resolv.conf:

sudo systemctl disable --now systemd-resolved sudo rm /etc/resolv.conf echo "nameserver 127.0.0.1" | sudo tee /etc/resolv.conf pihole restartdns

Pointing to 127.0.0.1#5335 with Unbound stopped

If Unbound isn't running, every forwarded query times out. Pi-hole's query log shows timed out for all non-blocked domains, which looks identical to a network connectivity failure. Always test Unbound directly with dig @127.0.0.1 -p 5335 before pointing Pi-hole at it, and confirm it is enabled at boot.

Conditional Forwarding overlap with custom upstreams

Conditional Forwarding (Settings → DNS → Advanced DNS Settings in v5) routes local-domain queries to your router. If your router's IP is also entered as a general upstream, all external queries may hit the router first, causing unexpected latency or filtering. Keep Conditional Forwarding addresses separate from the upstream server fields.

DNSSEC double-validation with Unbound

Pi-hole's built-in DNSSEC toggle (Settings → DNS → Use DNSSEC) tells FTL to validate records itself. Unbound also validates DNSSEC independently, and the harden-dnssec-stripped: yes directive in the config above is already strict. Running both validators causes SERVFAIL on many legitimately signed domains because the two validators disagree on intermediate state. When Unbound is your upstream, leave Pi-hole's DNSSEC toggle off and let Unbound own validation end-to-end.

Preventing Drift After the Fix

  1. Remove any secondary DNS IP (like 8.8.8.8) from your router's DHCP configuration. Devices use a secondary only when the primary is slow — having one lets traffic bypass Pi-hole silently whenever it has a hiccup.
  2. For devices that hardcode DNS regardless of DHCP — Chromecast, some smart TVs, Amazon Echo, Nintendo Switch — add a NAT redirect rule on your router that sends all outbound UDP and TCP port 53 to the Pi-hole IP. This forces compliance without touching each device individually.
  3. Run Tools → Pi-hole diagnosis in the admin UI periodically; it explicitly flags upstream configuration problems that wouldn't otherwise surface in the query log.
  4. Set a DNS monitoring check in Uptime Kuma or a cron script that runs a dig query against Pi-hole every 60 seconds and fires an alert if resolution fails — upstream issues become visible immediately instead of when users start complaining.

2026 Notes on Encrypted Upstream and DNSSEC

Pi-hole v6 adds native DNS-over-TLS (DoT) and DNS-over-HTTPS (DoH) upstream support without requiring cloudflared or Stubby as sidecar processes. In the upstream configuration panel, enter DoT addresses in tls://resolver-ip:853 format, or DoH URLs directly. Quad9's DoT endpoint is tls://9.9.9.9:853; Cloudflare's DoH URL is https://cloudflare-dns.com/dns-query. Pi-hole handles the TLS handshake and certificate validation internally.

On Pi-hole v5, encrypted upstreams require a local proxy. The most common approach is cloudflared acting as a DoH forwarder:

# Download ARM64 build for Raspberry Pi 4 or 5 wget https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-arm64.deb sudo dpkg -i cloudflared-linux-arm64.deb # Create the service config sudo mkdir -p /etc/cloudflared sudo nano /etc/cloudflared/config.yml # Add the following lines: # proxy-dns: true # proxy-dns-port: 5053 # proxy-dns-upstream: # - https://1.1.1.1/dns-query # - https://1.0.0.1/dns-query sudo cloudflared service install sudo systemctl start cloudflared sudo systemctl enable cloudflared # Then in Pi-hole, set upstream to: # 127.0.0.1#5053

For technical grounding on how DNS message structure works at the wire level — relevant when debugging encrypted upstream failures where the resolver returns malformed or truncated responses — the definitive reference is RFC 1035 at the IETF, which defines the original DNS message format that all modern extensions including DNSSEC and DoT build on top of.

On the DNSSEC front in 2026: all major public resolvers validate by default. Enable DNSSEC in Pi-hole only when your upstream does not validate — typically a legacy corporate resolver or a home router. Unbound with harden-dnssec-stripped: yes actively refuses results where DNSSEC was stripped in transit, which defends against downgrade attacks that strip signatures between the authoritative server and your resolver.