Self-Hosting Your Own DNS with Pi-hole and Unbound in 2026: The Complete Reference Setup

Self-Hosting Your Own DNS with Pi-hole and Unbound in 2026: The Complete Reference Setup
Self-Hosting Your Own DNS with Pi-hole and Unbound in 2026: The Complete Reference Setup
Listen to this post

AI-narrated version of this post using a synthetic voice. Great for accessibility or listening while busy.

Self-Hosting Your Own DNS with Pi-hole and Unbound in 2026: The Complete Reference Setup

Building a privacy-respecting, high-performance DNS infrastructure at home has become increasingly important as DNS queries reveal significant information about browsing habits, device inventory, and network usage patterns. This guide provides a production-grade approach to deploying Pi-hole with Unbound as a recursive resolver, examining the architectural decisions, security implications, and operational considerations required for a robust self-hosted DNS solution in 2026.

Understanding the DNS Resolution Architecture

Before deploying any DNS infrastructure, it is essential to understand the resolution chain and where each component fits within the query lifecycle. Traditional DNS resolution involves a client querying a recursive resolver, which then contacts authoritative nameservers on behalf of the client. Most home users rely on their ISP’s recursive resolvers or public services like Cloudflare (1.1.1.1) or Google (8.8.8.8), which centralizes DNS query visibility with third parties.

Pi-hole functions as a DNS sinkhole and filtering proxy. It intercepts DNS queries, checks them against blocklists, and either returns a null response for blocked domains or forwards legitimate queries to an upstream resolver. Pi-hole itself does not perform recursive DNS resolution by default–??it acts as a forwarding resolver. This is where Unbound enters the picture.

Unbound is a validating, recursive, caching DNS resolver designed with security and performance in mind. When deployed alongside Pi-hole, Unbound queries the DNS root servers directly and follows the delegation chain to authoritative nameservers, eliminating the need to trust third-party recursive resolvers. This architecture provides complete query privacy while maintaining DNSSEC validation and response caching.

The Pi-hole plus Unbound combination creates a two-tier DNS infrastructure: Pi-hole handles ad-blocking and client request management, while Unbound performs the actual recursive resolution. Clients query Pi-hole, Pi-hole checks its blocklists and cache, then forwards cache misses to Unbound on localhost. Unbound either returns a cached response or performs recursive resolution starting from the root nameservers.

The DoH and DoT Tradeoff Analysis

DNS-over-HTTPS (DoH) as specified in RFC 8484 and DNS-over-TLS (DoT) per RFC 7858 encrypt DNS queries between client and resolver, preventing network-level eavesdropping. While these protocols provide privacy from local network observers and ISPs, they introduce significant architectural and operational tradeoffs in self-hosted environments.

When using DoH or DoT with upstream public resolvers, you shift trust from your ISP to the resolver operator–??typically Cloudflare, Google, or Quad9. These providers still have complete visibility into your DNS queries, including the timing and metadata. The encryption protects against passive observation on the network path but does not provide end-to-end privacy. The resolver operator can still log, analyze, and potentially monetize query data.

In a self-hosted Pi-hole and Unbound configuration, the query never leaves your infrastructure until Unbound contacts authoritative nameservers. These queries appear to come from your resolver’s IP address and are distributed across thousands of different authoritative servers for different domains. No single third party can observe your complete DNS query profile, significantly improving privacy over centralized encrypted resolvers.

The tradeoff is that queries between Unbound and authoritative nameservers typically use traditional unencrypted DNS on port 53. However, this is a fundamentally different threat model. An observer monitoring your connection to an authoritative nameserver sees only that your resolver is querying for a specific domain, not which device or user initiated the request. With proper query aggregation and caching, a single query to an authoritative server may serve requests from multiple devices over an extended period.

DoH and DoT also complicate network management. Many operating systems and applications now include hardcoded DoH resolvers that bypass system DNS settings, breaking local filtering and split-horizon configurations. Firefox, Chrome, and Windows 11 have all implemented DoH with varying degrees of respect for local DNS infrastructure. For home networks requiring consistent DNS policy enforcement, this represents a significant operational challenge that encrypted upstream resolvers do not solve.

The optimal approach for most self-hosted scenarios is to run Unbound as a recursive resolver without encrypted upstream, then optionally implement DoT for client-to-Pi-hole communication if client devices support it. This preserves local control while providing encryption for the local network segment, which is often the highest-risk environment for passive DNS observation.

Deployment Architecture: Container vs Bare Metal

The choice between containerized and bare-metal deployment significantly impacts maintenance, resource usage, network configuration, and recovery procedures. Both approaches remain viable in 2026, with different operational characteristics.

Bare-metal installation provides the simplest network integration. Pi-hole installed directly on the host OS can bind to port 53 on the primary network interface without NAT complexity, respond to DHCP requests on the local network if needed, and integrate seamlessly with systemd for service management and logging. Resource overhead is minimal, and troubleshooting network connectivity follows standard Linux networking patterns.

Container deployment using Docker or Podman offers isolation, reproducibility, and simplified backup/restore operations. The entire Pi-hole configuration becomes portable across hardware platforms. However, container networking introduces complexity. Pi-hole must bind to port 53, which conflicts with systemd-resolved on many modern Linux distributions. DHCP functionality requires either host networking mode or macvlan networking, both of which have implications for container isolation and firewall configuration.

For production home deployments in 2026, a hybrid approach often provides the best balance: Unbound runs as a system service directly on the host, while Pi-hole runs in a container with host networking. This configuration simplifies port binding, provides process isolation for Pi-hole, and maintains straightforward network integration for Unbound.

When running both services in containers, the recommended approach uses a dedicated bridge network with published ports. Unbound listens on a non-standard port on the host (such as 5335), and Pi-hole forwards to this port. This avoids systemd-resolved conflicts while maintaining container isolation:

version: '3.8'

services:
  unbound:
    image: mvance/unbound:latest
    container_name: unbound
    restart: unless-stopped
    hostname: unbound
    volumes:
      - ./unbound:/opt/unbound/etc/unbound
    ports:
      - 5335:53/tcp
      - 5335:53/udp
    networks:
      - dns_network

  pihole:
    image: pihole/pihole:latest
    container_name: pihole
    restart: unless-stopped
    hostname: pihole
    ports:
      - 53:53/tcp
      - 53:53/udp
      - 80:80/tcp
    environment:
      TZ: 'America/New_York'
      WEBPASSWORD: 'changeme'
      PIHOLE_DNS_: '172.20.0.2#5335'
      DNSSEC: 'true'
      DNSMASQ_LISTENING: 'all'
    volumes:
      - ./pihole/etc:/etc/pihole
      - ./pihole/dnsmasq.d:/etc/dnsmasq.d
    networks:
      - dns_network
    dns:
      - 127.0.0.1
      - 1.1.1.1
    depends_on:
      - unbound

networks:
  dns_network:
    driver: bridge
    ipam:
      config:
        - subnet: 172.20.0.0/24

This configuration creates a dedicated bridge network with a static subnet. Unbound receives a predictable IP address (typically 172.20.0.2), which Pi-hole references as its upstream resolver. The PIHOLE_DNS_ environment variable specifies Unbound’s container IP and port. The depends_on directive ensures proper startup order.

Unbound Configuration for Recursive Resolution

Unbound’s configuration determines its behavior as a recursive resolver, including performance characteristics, security features, and caching strategy. The configuration file typically resides at /etc/unbound/unbound.conf or within a mounted volume for containerized deployments.

A production-grade Unbound configuration for home use prioritizes privacy, performance, and DNSSEC validation:

server:
    # Network configuration
    interface: 0.0.0.0
    port: 53
    do-ip4: yes
    do-ip6: yes
    do-udp: yes
    do-tcp: yes
    prefer-ip6: no

    # Access control
    access-control: 127.0.0.0/8 allow
    access-control: 10.0.0.0/8 allow
    access-control: 172.16.0.0/12 allow
    access-control: 192.168.0.0/16 allow
    access-control: 0.0.0.0/0 refuse

    # Performance tuning
    num-threads: 4
    msg-cache-slabs: 8
    rrset-cache-slabs: 8
    infra-cache-slabs: 8
    key-cache-slabs: 8
    rrset-cache-size: 256m
    msg-cache-size: 128m
    so-rcvbuf: 1m
    so-sndbuf: 1m
    so-reuseport: yes

    # Privacy enhancements
    hide-identity: yes
    hide-version: yes
    qname-minimisation: yes
    aggressive-nsec: yes
    
    # Security features
    harden-glue: yes
    harden-dnssec-stripped: yes
    harden-below-nxdomain: yes
    harden-referral-path: yes
    harden-algo-downgrade: yes
    use-caps-for-id: yes
    unwanted-reply-threshold: 10000
    val-clean-additional: yes

    # DNSSEC validation
    module-config: "validator iterator"
    auto-trust-anchor-file: "/var/lib/unbound/root.key"
    trust-anchor-signaling: yes
    root-key-sentinel: yes
    val-log-level: 1

    # Cache settings
    cache-min-ttl: 300
    cache-max-ttl: 86400
    prefetch: yes
    prefetch-key: yes
    serve-expired: yes
    serve-expired-ttl: 86400

    # Local network optimization
    private-address: 10.0.0.0/8
    private-address: 172.16.0.0/12
    private-address: 192.168.0.0/16
    private-address: 169.254.0.0/16
    private-address: fd00::/8
    private-address: fe80::/10

    # Rate limiting
    ratelimit: 1000
    ip-ratelimit: 50

# Root hints file for recursive resolution
include: "/etc/unbound/root.hints"

This configuration enables QNAME minimization per RFC 7816, which reduces privacy leakage by sending only the necessary portion of the query name to each nameserver in the resolution chain. The harden-dnssec-stripped option protects against downgrade attacks where DNSSEC records are stripped by malicious intermediaries. The serve-expired option allows Unbound to return cached records beyond their TTL if authoritative servers are unreachable, improving resilience during network issues.

The num-threads and cache-slabs settings should match the number of CPU cores on your system for optimal performance. Cache sizes of 128MB for message cache and 256MB for RRset cache provide good performance for typical home networks with dozens of devices. Adjust these values based on available memory and query volume observed in production.

The root.hints file contains the current list of root nameserver addresses and must be updated periodically:

#!/bin/bash
# Update root hints file for Unbound
curl -o /etc/unbound/root.hints https://www.internic.net/domain/named.root
chown unbound:unbound /etc/unbound/root.hints
systemctl restart unbound

Schedule this script monthly via cron to maintain current root server information. The InterNIC file is the authoritative source for root nameserver addresses.

Pi-hole Configuration and Integration

Pi-hole configuration determines filtering behavior, upstream resolver settings, and client management. The web interface provides most configuration options, but advanced settings require direct modification of configuration files in /etc/pihole and /etc/dnsmasq.d.

Configure Pi-hole to use Unbound as its sole upstream resolver through the web interface under Settings > DNS. Uncheck all preset upstream DNS servers and add a custom upstream: 127.0.0.1#5335 for containerized Unbound or 127.0.0.1#53 for bare-metal installations where Unbound runs locally. Enable DNSSEC validation in Pi-hole even though Unbound performs validation–??this provides defense in depth and allows Pi-hole to display DNSSEC status.

For split-horizon DNS scenarios where internal domains resolve to private IP addresses while public resolution occurs for external domains, conditional forwarding becomes necessary. Create a custom dnsmasq configuration file:

# /etc/dnsmasq.d/02-custom-dns.conf

# Forward internal domain to local DNS server
server=/home.arpa/192.168.1.1

# Forward reverse DNS for local subnet to router
server=/1.168.192.in-addr.arpa/192.168.1.1

# Forward Active Directory domain to domain controller
server=/corp.internal/10.0.10.5
server=/10.0.10.in-addr.arpa/10.0.10.5

# Expand hosts file entries to FQDN
expand-hosts
domain=home.arpa

# Local domain records
address=/homeserver.home.arpa/192.168.1.100
address=/nas.home.arpa/192.168.1.101

This configuration forwards queries for internal domains to the appropriate authoritative server while sending all other queries through the normal Unbound recursive path. The expand-hosts directive automatically appends the domain to hostnames defined in /etc/hosts, enabling short name resolution for local devices.

For environments with multiple VLANs or subnets, conditional forwarding based on source network becomes important:

# /etc/dnsmasq.d/03-network-specific.conf

# Guest network gets filtered DNS with no local domain access
dhcp-range=set:guest,192.168.2.100,192.168.2.200,12h
dhcp-option=tag:guest,6,192.168.1.2
dhcp-option=tag:guest,15,

# IoT network gets DNS but restricted local access
dhcp-range=set:iot,192.168.3.100,192.168.3.200,24h
dhcp-option=tag:iot,6,192.168.1.2
dhcp-option=tag:iot,15,iot.home.arpa

# Main network gets full DNS with local domain
dhcp-range=192.168.1.100,192.168.1.200,12h
dhcp-option=6,192.168.1.2
dhcp-option=15,home.arpa

These DHCP configurations use tags to assign different DNS settings based on the client network. The guest network receives only the DNS server address without a search domain, preventing resolution of internal names. The IoT network gets a restricted search domain for device management, while the main network receives full internal DNS access.

DHCP Options for Complete Network Integration

Proper DHCP configuration ensures clients automatically use the Pi-hole DNS infrastructure without manual configuration. DHCP option 6 specifies DNS servers, while option 252 configures Web Proxy Auto-Discovery (WPAD) for corporate environments.

If Pi-hole serves DHCP for the network, enable DHCP in the Pi-hole web interface and configure appropriate ranges and options. Disable DHCP on any existing router or server to prevent conflicts. Pi-hole’s DHCP server is based on dnsmasq and supports advanced configuration through custom files:

# /etc/dnsmasq.d/04-dhcp-options.conf

# Basic DHCP options
dhcp-option=option:router,192.168.1.1
dhcp-option=option:dns-server,192.168.1.2
dhcp-option=option:ntp-server,192.168.1.1
dhcp-option=option:domain-name,home.arpa

# Option 252 for WPAD
dhcp-option=252,"http://wpad.home.arpa/wpad.dat"

# Vendor-specific options for PXE boot
dhcp-match=set:pxe,60,PXEClient
dhcp-boot=tag:pxe,pxelinux.0,pxeserver,192.168.1.10

# Static DHCP assignments
dhcp-host=00:11:22:33:44:55,192.168.1.10,homeserver,infinite
dhcp-host=aa:bb:cc:dd:ee:ff,192.168.1.11,nas,infinite

DHCP option 6 is the standard DNS server option supported by all DHCP clients. Clients receive this configuration and use the specified server for all DNS queries. Option 252 provides WPAD configuration for automatic proxy detection, useful in corporate or filtered environments where HTTP traffic requires routing through a proxy server.

If Pi-hole does not serve DHCP and the existing DHCP server cannot be disabled, configure the router or DHCP server to advertise Pi-hole as the primary DNS server. Most enterprise routers and consumer devices support custom DHCP option configuration. For environments where DHCP modification is not possible, static DNS configuration on each client remains the fallback approach, though this prevents consistent enforcement across all devices.

Monitoring and Observability with Prometheus

Production DNS infrastructure requires monitoring to detect failures, performance degradation, and security events. Pi-hole includes a web dashboard with query statistics, but integration with Prometheus provides historical metrics, alerting, and correlation with other infrastructure components.

The pihole-exporter project exposes Pi-hole metrics in Prometheus format. Deploy the exporter as a separate container or systemd service:

version: '3.8'

services:
  pihole-exporter:
    image: ekofr/pihole-exporter:latest
    container_name: pihole-exporter
    restart: unless-stopped
    ports:
      - 9617:9617
    environment:
      PIHOLE_HOSTNAME: pihole
      PIHOLE_PORT: 80
      PIHOLE_PASSWORD: 'changeme'
    networks:
      - dns_network

networks:
  dns_network:
    external: true

Configure Prometheus to scrape the exporter endpoint:

# prometheus.yml
scrape_configs:
  - job_name: 'pihole'
    static_configs:
      - targets: ['pihole-exporter:9617']
    scrape_interval: 30s
    scrape_timeout: 10s

For Unbound monitoring, enable the statistics interface in the Unbound configuration:

# unbound.conf addition
remote-control:
    control-enable: yes
    control-interface: 127.0.0.1
    control-port: 8953
    server-key-file: "/etc/unbound/unbound_server.key"
    server-cert-file: "/etc/unbound/unbound_server.pem"
    control-key-file: "/etc/unbound/unbound_control.key"
    control-cert-file: "/etc/unbound/unbound_control.pem"

Generate the required certificates:

unbound-control-setup

Deploy the unbound-exporter to expose metrics:

version: '3.8'

services:
  unbound-exporter:
    image: svenstaro/unbound_exporter:latest
    container_name: unbound-exporter
    restart: unless-stopped
    ports:
      - 9167:9167
    command:
      - --unbound.host=unbound:8953
      - --unbound.ca=/etc/unbound/unbound_server.pem
      - --unbound.cert=/etc/unbound/unbound_control.pem
      - --unbound.key=/etc/unbound/unbound_control.key
    volumes:
      - ./unbound:/etc/unbound:ro
    networks:
      - dns_network

networks:
  dns_network:
    external: true

Key metrics to monitor include query rate (queries per second), cache hit ratio, DNSSEC validation failures, query response time distributions, and upstream server health. Create Prometheus alerts for critical conditions:

groups:
- name: dns_alerts
interval: 30s
rules:
- alert: PiHoleDown
expr: up{job="pihole"} == 0
for: 2m
labels:
severity: critical
annotations:
summary: "Pi-hole is down"
description: "Pi-hole has been unreachable for 2 minutes"

- alert: HighDNSErrorRate
expr: rate(pihole_queries_forwarded{status="failed\


Related Auburn AI Products

Building a homelab or self-hosting content site? Auburn AI has practical kits:

For general informational purposes only; not professional advice. Posts may contain affiliate links. Learn more.
Scroll to Top