PublicDNS.info Live-tested public DNS
Retested every 72 hours.

Complete Guide to Deploying Pi-hole DNS Filtering Server

Deploy a Pi-hole DNS filtering server for network-wide ad blocking. Covers Debian, RHEL, Docker, Raspberry Pi, Unbound integration, DNSSEC, and blocklist management.

Pi-hole DNS Filtering Flow

Client Devices Router Pi-hole DNS Server Upstream DNS Resolver Blocked Domains Sinkhole (0.0.0.0) DNS DNS Allowed Blocked

1. Introduction to Pi-hole DNS filtering

Pi-hole is a network-wide DNS sinkhole that blocks advertising, tracking, and malware domains at the DNS level. Instead of installing ad blockers on every device, you point your network's DNS at Pi-hole and it filters queries for the entire LAN -- phones, tablets, smart TVs, IoT devices, everything.

Pi-hole was originally designed for the Raspberry Pi but runs on any Linux box, VM, or Docker container. It sits between your clients and your upstream DNS resolver (Cloudflare, Google, Quad9, or your own recursive resolver), intercepting queries for known-bad domains and returning a null response.

The project is open source (GPL v2), maintained by a small team, and funded by donations. The web dashboard gives you real-time visibility into every DNS query on your network, which is useful for both ad blocking and network troubleshooting.

This guide covers everything from installation to production hardening. If you already have a working Pi-hole, skip to the sections on blocklists, Unbound integration, or security hardening.

For upstream resolver options, check the PublicDNS.info server directory -- every server listed there is live-tested and verified.

2. How DNS sinkhole technology works

A DNS sinkhole works by intercepting DNS queries and returning a false answer for domains on a blocklist. When a client asks "what is the IP for ads.tracker.com?", Pi-hole checks its gravity database. If the domain is listed, Pi-hole responds with 0.0.0.0 (or :: for AAAA queries) instead of the real IP. The client's browser tries to connect to 0.0.0.0, which immediately fails, and the ad never loads.

This is fundamentally different from browser-based ad blockers. A browser extension parses HTML and removes ad elements after they download. A DNS sinkhole prevents the connection entirely -- the HTTP request never leaves your network. This saves bandwidth and works on devices where you cannot install browser extensions.

The blocklist (called "gravity" in Pi-hole terminology) is a compiled database of domain names. Pi-hole downloads raw blocklists from URLs you configure, parses them, deduplicates entries, and stores them in a SQLite database at /etc/pihole/gravity.db. During a query, FTLDNS (Pi-hole's custom DNS/DHCP server) checks this database with a hash lookup -- it is extremely fast even with millions of domains.

Domains that are not on the blocklist pass through to your configured upstream DNS resolver, and the response is cached locally. Subsequent queries for the same domain are answered from cache without hitting the upstream server.

Limitations: DNS sinkholing cannot block ads served from the same domain as the content (e.g., ads.youtube.com serving from googlevideo.com). It also cannot block anything that uses hard-coded IP addresses instead of domain names, though this is rare in practice.

3. Pi-hole architecture overview

Pi-hole consists of four main components:

FTLDNS (pihole-FTL) -- A fork of dnsmasq with embedded SQLite support. This is the core DNS resolver and DHCP server. It handles all DNS queries, checks them against the gravity database, forwards allowed queries to upstream resolvers, and logs everything to /etc/pihole/pihole-FTL.db. Configuration: /etc/pihole/pihole-FTL.conf.

Gravity database -- A SQLite database at /etc/pihole/gravity.db containing all blocklist domains, whitelisted domains, regex filters, clients, and groups. Updated by running pihole -g, which fetches remote adlists and recompiles the database.

Web interface -- A PHP application served by lighttpd (or your own web server). Provides the admin dashboard at http://<pi-hole-ip>/admin. Talks to FTLDNS via a Unix socket or the embedded API. Shows real-time query statistics, top blocked domains, per-client breakdowns, and configuration options.

pihole command-line tool -- A bash utility at /usr/local/bin/pihole for managing Pi-hole from the terminal. Key commands: pihole -g (update gravity), pihole -up (update Pi-hole), pihole -w (whitelist), pihole -b (blacklist), pihole -t (tail the log), pihole -d (debug).

Configuration is split across two files: /etc/pihole/setupVars.conf stores installation choices (interface, IP, upstream DNS, etc.) and /etc/pihole/pihole-FTL.conf stores runtime tuning options for the FTL daemon.

4. Hardware requirements

Pi-hole is remarkably lightweight. Minimum specs for a small home network (under 30 devices):

  • CPU: Any single-core ARM or x86 processor. A Raspberry Pi Zero W works.
  • RAM: 512 MB minimum. 1 GB recommended if you enable long-term query logging.
  • Storage: 2 GB minimum. 4 GB+ recommended. The gravity database is typically 50-100 MB. Long-term logs consume the most space.
  • Network: Wired Ethernet strongly recommended. Wi-Fi adds latency and is a single point of failure for DNS.

For larger networks (100+ devices, offices, or if you run Unbound alongside Pi-hole), give it 2 GB RAM and a dual-core CPU. A Raspberry Pi 4 with 2 GB handles hundreds of clients without breaking a sweat.

If running Pi-hole in a VM, allocate 1 vCPU and 1 GB RAM. If running in Docker on an existing server, there is no need for a dedicated machine -- Pi-hole as a container uses roughly 100 MB of RAM at idle.

SD card longevity on Raspberry Pi: Pi-hole writes to the FTL database frequently. Use a quality SD card (SanDisk Endurance or Samsung EVO) or boot from USB/SSD to avoid SD card wear.

5. Supported operating systems

The Pi-hole automated installer officially supports these Linux distributions:

  • Raspberry Pi OS (Bullseye, Bookworm) -- the primary target platform
  • Debian 11 (Bullseye) and 12 (Bookworm)
  • Ubuntu 22.04 LTS and 24.04 LTS
  • Fedora 39, 40
  • CentOS Stream 9
  • AlmaLinux 9, Rocky Linux 9

ARM (armhf, arm64) and x86_64 are both supported. 32-bit x86 is not officially supported but may work.

For unsupported distros (Arch, Alpine, etc.), Docker is the recommended path. The pihole/pihole container image runs on anything that runs Docker.

Pi-hole does not run natively on Windows or macOS. Use Docker Desktop or a Linux VM on those platforms.

6. Network topology planning

Before installing, decide where Pi-hole fits in your network. There are three common topologies:

Topology A: Pi-hole behind the router (most common). Your router handles DHCP and hands out Pi-hole's IP as the DNS server. All clients send DNS queries to Pi-hole. Pi-hole forwards allowed queries to an external upstream (e.g., 1.1.1.1 or 8.8.8.8). Simple to set up. Downside: the query log shows all traffic as coming from the router's IP unless you configure the router to forward client IPs.

Topology B: Pi-hole as DHCP server. Disable DHCP on your router and let Pi-hole handle it. Pi-hole assigns IPs and sets itself as the DNS server. Advantage: you see individual client hostnames in the query log. Downside: if Pi-hole goes down, new DHCP leases stop.

Topology C: Pi-hole with Unbound (full recursive). Same as A or B, but instead of forwarding to an external resolver, Pi-hole sends queries to a local Unbound instance that resolves directly from root servers. Maximum privacy -- no third-party DNS provider sees your queries.

Assign Pi-hole a static IP address. If using DHCP, reserve the address in your router's DHCP settings before installing Pi-hole. Common choice: 192.168.1.53 (port 53 = DNS, easy to remember).

For redundancy, run two Pi-hole instances. Configure your router to hand out both IPs as DNS servers. Keep blocklists synchronized with Gravity Sync or a cron job that copies gravity.db between instances.

7. Installing Pi-hole on Debian/Ubuntu

Start with a clean Debian 12 or Ubuntu 24.04 installation. Update the system first:

sudo apt update && sudo apt upgrade -y

Install Pi-hole using the automated installer:

curl -sSL https://install.pi-hole.net | sudo bash

The installer walks you through a series of prompts. If you want a fully unattended install, create /etc/pihole/setupVars.conf beforehand:

sudo mkdir -p /etc/pihole
cat <<'EOF' | sudo tee /etc/pihole/setupVars.conf
PIHOLE_INTERFACE=eth0
PIHOLE_DNS_1=1.1.1.1
PIHOLE_DNS_2=8.8.8.8
QUERY_LOGGING=true
INSTALL_WEB_SERVER=true
INSTALL_WEB_INTERFACE=true
LIGHTTPD_ENABLED=true
CACHE_SIZE=10000
DNS_FQDN_REQUIRED=true
DNS_BOGUS_PRIV=true
BLOCKING_ENABLED=true
WEBPASSWORD=your_hashed_password_here
EOF

Then run the installer with --unattended:

curl -sSL https://install.pi-hole.net | sudo bash /dev/stdin --unattended

After installation, set a new admin password:

pihole -a -p

Verify the service is running:

sudo systemctl status pihole-FTL

Open a browser and navigate to http://192.168.1.53/admin (replace with your Pi-hole's IP). You should see the dashboard.

If you use systemd-resolved (common on Ubuntu), it binds to port 53 and conflicts with Pi-hole. Disable it:

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

8. Installing Pi-hole on RHEL / AlmaLinux

On RHEL 9, AlmaLinux 9, or Rocky Linux 9, a few extra steps are needed before running the installer.

Update and install prerequisites:

sudo dnf update -y
sudo dnf install -y curl tar

Open firewall ports for DNS and the web interface:

sudo firewall-cmd --permanent --add-service=dns
sudo firewall-cmd --permanent --add-port=80/tcp
sudo firewall-cmd --permanent --add-port=443/tcp
sudo firewall-cmd --reload

If SELinux is enforcing (check with getenforce), the Pi-hole installer usually handles SELinux contexts automatically. If you hit permission errors after install, check the audit log:

sudo ausearch -m avc -ts recent
sudo setsebool -P httpd_can_network_connect 1

Run the installer:

curl -sSL https://install.pi-hole.net | sudo bash

The installer detects the RHEL-family OS and uses dnf for dependencies. It installs lighttpd, php, and the required modules automatically.

Set the admin password and verify:

pihole -a -p
sudo systemctl status pihole-FTL
curl -s http://localhost/admin/ | head -5

On RHEL-based systems, /etc/resolv.conf is often managed by NetworkManager. Prevent it from overwriting your DNS settings:

sudo nmcli con mod "System eth0" ipv4.dns "127.0.0.1"
sudo nmcli con mod "System eth0" ipv4.ignore-auto-dns yes
sudo nmcli con up "System eth0"

9. Installing Pi-hole using Docker

Docker is the cleanest way to run Pi-hole on an existing server without touching the host's DNS stack. Create a directory for Pi-hole data:

mkdir -p ~/pihole/{etc-pihole,etc-dnsmasq.d}

Create a docker-compose.yml:

cat <<'EOF' > ~/pihole/docker-compose.yml
services:
  pihole:
    container_name: pihole
    image: pihole/pihole:latest
    ports:
      - "53:53/tcp"
      - "53:53/udp"
      - "80:80/tcp"
    environment:
      TZ: 'Europe/London'
      WEBPASSWORD: 'changeme_to_a_strong_password'
      PIHOLE_DNS_: '1.1.1.1;8.8.8.8'
      DNSSEC: 'true'
    volumes:
      - './etc-pihole:/etc/pihole'
      - './etc-dnsmasq.d:/etc/dnsmasq.d'
    restart: unless-stopped
    cap_add:
      - NET_ADMIN
EOF

Start the container:

cd ~/pihole
docker compose up -d

Check the logs:

docker logs pihole -f

Access the dashboard at http://<host-ip>/admin.

If port 53 is already in use (common on Ubuntu with systemd-resolved), either disable systemd-resolved or bind Pi-hole to a specific IP:

    ports:
      - "192.168.1.53:53:53/tcp"
      - "192.168.1.53:53:53/udp"

To update the container:

cd ~/pihole
docker compose pull
docker compose up -d

Your configuration persists in the mounted volumes. The container can be destroyed and recreated without losing settings or blocklists.

10. Running Pi-hole on Raspberry Pi

The Raspberry Pi is the canonical Pi-hole platform. A Pi Zero 2 W, Pi 3, Pi 4, or Pi 5 all work. The Pi 4 with 2 GB RAM is the sweet spot for most home networks.

Flash Raspberry Pi OS Lite (64-bit recommended) to an SD card using the Raspberry Pi Imager. In the imager's advanced settings (gear icon), enable SSH, set a username/password, and configure Wi-Fi if not using Ethernet.

Boot the Pi, SSH in, and assign a static IP. Edit /etc/dhcpcd.conf:

interface eth0
static ip_address=192.168.1.53/24
static routers=192.168.1.1
static domain_name_servers=1.1.1.1

Reboot and confirm the static IP:

sudo reboot
# After reboot:
ip addr show eth0

Update the system and install Pi-hole:

sudo apt update && sudo apt upgrade -y
curl -sSL https://install.pi-hole.net | sudo bash

Follow the installer prompts. When finished, set a password:

pihole -a -p

For long-term reliability, consider booting from a USB SSD instead of an SD card. SD cards degrade from repeated writes, and Pi-hole's FTL database writes frequently. On a Pi 4 or 5, USB boot is supported out of the box.

Power the Pi with the official power supply. Underpowering causes instability that is difficult to diagnose -- you will see random crashes and filesystem corruption.

11. Initial configuration wizard

The Pi-hole installer presents a text-based wizard with these key prompts:

Upstream DNS provider: Choose from presets (Google, Cloudflare, Quad9, OpenDNS) or enter custom IPs. You can change this later. For privacy, consider running your own Unbound instance (covered in section 14).

Blocklists: The installer includes Steven Black's unified hosts list by default. You will add more lists after installation.

Protocols: Enable both IPv4 and IPv6 blocking unless you have disabled IPv6 on your network.

Static IP confirmation: The installer detects your current IP and asks you to confirm it as Pi-hole's static address. Make sure your DHCP server (router) has this IP reserved.

Web admin interface: Say yes. There is no reason to skip this unless you are building a headless deployment managed entirely via CLI or API.

Web server (lighttpd): Say yes unless you plan to use an existing Nginx or Apache installation. If you say no, you must configure your own web server to serve the admin interface from /var/www/html/admin.

Query logging: Enable it. This is what makes Pi-hole useful for monitoring. You can adjust privacy levels later in /etc/pihole/pihole-FTL.conf.

Privacy mode: Level 0 shows everything (recommended for home use). Higher levels anonymize parts of the query log. Level 4 disables all logging.

After the wizard completes, the installer prints a randomly generated admin password. Write it down or immediately change it with pihole -a -p.

12. Understanding the Pi-hole web dashboard

Access the dashboard at http://<pi-hole-ip>/admin. Log in with the password you set during installation. The main page shows four key metrics:

  • Total queries: All DNS queries received in the last 24 hours.
  • Queries blocked: Count and percentage of queries that matched the blocklist.
  • Blocklist size: Number of unique domains in the gravity database.
  • Clients: Number of unique devices that have sent DNS queries.

Below the summary cards is a time series chart showing queries over the last 24 hours, with allowed and blocked queries color-coded.

Query Log -- Real-time view of every DNS query. Shows the domain, requesting client, response type (forwarded, cached, blocked, or CNAME), and timestamp. Use the search box to filter by domain or client.

Long-Term Data -- Historical query data stored in the FTL database. Graphs show trends over days, weeks, or months. Useful for spotting rogue devices or unusual traffic patterns.

Top Domains / Top Blocked -- Most frequently queried and most frequently blocked domains. If a blocked domain is causing issues, you can whitelist it directly from this page.

Top Clients -- Shows which devices generate the most DNS queries. Helpful for identifying chatty IoT devices.

Group Management -- Assign clients to groups with different blocklist configurations. For example, you can create a "kids" group with stricter filtering and an "admin" group with no filtering.

13. Configuring upstream DNS providers

Pi-hole forwards non-blocked queries to the upstream DNS servers you configure. Go to Settings > DNS in the web dashboard, or edit /etc/pihole/setupVars.conf directly.

Common upstream choices:

  • Cloudflare: 1.1.1.1 and 1.0.0.1 (IPv6: 2606:4700:4700::1111, 2606:4700:4700::1001)
  • Google: 8.8.8.8 and 8.8.4.4 (IPv6: 2001:4860:4860::8888, 2001:4860:4860::8844)
  • Quad9: 9.9.9.9 and 149.112.112.112 (malware blocking included)
  • OpenDNS: 208.67.222.222 and 208.67.220.220

Browse the PublicDNS.info directory for a complete list of live-tested public DNS servers, including servers specific to your country.

For DNS-over-HTTPS (DoH) or DNS-over-TLS (DoT) to your upstream, Pi-hole does not natively support encrypted transport. Use a local proxy:

# Install cloudflared as a DoH proxy
# Download from https://github.com/cloudflare/cloudflared/releases
sudo cloudflared service install
# Configure it to listen on 127.0.0.1:5053
# Then set Pi-hole upstream to 127.0.0.1#5053

Alternatively, use Unbound with DNS-over-TLS to forward queries. Or run Unbound as a full recursive resolver and skip third-party DNS entirely (see next section).

Enable "Use DNSSEC" in the DNS settings if your upstream supports it. This validates DNS responses cryptographically. Do not enable DNSSEC in Pi-hole if you are using Unbound for DNSSEC validation -- that would double-validate and cause failures.

14. Integrating Unbound recursive resolver

Unbound is a recursive DNS resolver that queries root servers directly instead of forwarding to a third party. Running it alongside Pi-hole gives you full control over DNS resolution with no external dependencies.

Install Unbound:

# Debian/Ubuntu
sudo apt install -y unbound

# RHEL/AlmaLinux
sudo dnf install -y unbound

Create the Pi-hole configuration file at /etc/unbound/unbound.conf.d/pi-hole.conf:

server:
    verbosity: 0
    interface: 127.0.0.1
    port: 5335
    do-ip4: yes
    do-udp: yes
    do-tcp: yes
    do-ip6: no

    # Security
    harden-glue: yes
    harden-dnssec-stripped: yes
    harden-referral-path: yes
    harden-algo-downgrade: yes

    # Privacy
    use-caps-for-id: no
    edns-buffer-size: 1232
    prefetch: yes
    num-threads: 1

    # Trust anchor for DNSSEC
    auto-trust-anchor-file: "/var/lib/unbound/root.key"

    # Deny private ranges on public queries
    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

Download the root hints (list of root DNS servers):

sudo wget -O /var/lib/unbound/root.hints https://www.internic.net/domain/named.root
sudo chown unbound:unbound /var/lib/unbound/root.hints

Start and enable Unbound:

sudo systemctl enable --now unbound

Test Unbound directly:

dig @127.0.0.1 -p 5335 example.com

You should get a valid response. Now configure Pi-hole to use Unbound as its sole upstream. In the web dashboard, go to Settings > DNS, uncheck all preset servers, and add 127.0.0.1#5335 as a custom upstream. Uncheck "Use DNSSEC" in Pi-hole since Unbound handles DNSSEC validation.

The first query for each domain will be slightly slower (Unbound must walk the DNS tree from the root), but subsequent queries are cached. Unbound's cache combined with Pi-hole's cache makes repeated lookups very fast.

15. Enabling DNSSEC

DNSSEC (DNS Security Extensions) adds cryptographic signatures to DNS responses, allowing resolvers to verify that a response has not been tampered with. This protects against DNS spoofing and cache poisoning attacks.

If using Pi-hole with an external upstream: Enable DNSSEC in the Pi-hole dashboard under Settings > DNS. Check "Use DNSSEC". This tells FTLDNS to validate DNSSEC signatures on responses from the upstream resolver. Your upstream must support DNSSEC (Cloudflare, Google, and Quad9 all do).

If using Pi-hole with Unbound: Unbound handles DNSSEC validation natively with the auto-trust-anchor-file directive in the config above. Do NOT also enable DNSSEC in Pi-hole -- this causes double validation and leads to false SERVFAIL responses.

Test that DNSSEC is working:

# This should return SERVFAIL (intentionally bad DNSSEC)
dig @192.168.1.53 dnssec-failed.org

# This should return NOERROR with the 'ad' flag
dig @192.168.1.53 +dnssec example.com

If dnssec-failed.org returns a normal A record instead of SERVFAIL, DNSSEC validation is not working. Check your Unbound configuration or ensure Pi-hole's DNSSEC option is enabled.

Common issue: the trust anchor file (/var/lib/unbound/root.key) must be writable by the Unbound user. If Unbound starts but DNSSEC fails, check ownership:

sudo chown unbound:unbound /var/lib/unbound/root.key
sudo systemctl restart unbound

16. Router DNS configuration

The simplest way to route all network traffic through Pi-hole is to set it as the DNS server in your router's DHCP settings. Every device that obtains an IP via DHCP will automatically use Pi-hole for DNS.

The exact steps vary by router, but the general process:

  1. Log into your router's admin interface (usually http://192.168.1.1).
  2. Find the DHCP settings (sometimes under LAN, Network, or DHCP Server).
  3. Set the primary DNS server to your Pi-hole's IP (e.g., 192.168.1.53).
  4. Remove any secondary DNS server, or set it to a second Pi-hole instance. Do not set a non-Pi-hole secondary -- clients will bypass Pi-hole half the time.
  5. Save and reboot the router.

Clients will pick up the new DNS on their next DHCP renewal. Force a renewal immediately:

# Linux
sudo dhclient -r && sudo dhclient

# macOS
sudo ipconfig set en0 DHCP

# Windows
ipconfig /release && ipconfig /renew

Some ISP-provided routers do not allow changing the DNS server in DHCP settings. In that case, either use Pi-hole as your DHCP server (see next section), replace the router with one you control, or configure DNS on each device individually.

Note: if your router acts as a DNS forwarder (forwarding client queries through itself), the Pi-hole query log will show all queries as coming from the router's IP. This makes per-client analysis impossible. To fix this, set the router to hand out Pi-hole's IP directly as the DNS server rather than forwarding through itself.

17. DHCP configuration

Running Pi-hole as your DHCP server has two advantages: you see individual client hostnames in the query log, and you avoid ISP routers that force their own DNS settings.

Step 1: Disable DHCP on your router. Find the DHCP server toggle in your router's admin interface and turn it off. Do not skip this step -- two DHCP servers on the same network will cause IP conflicts.

Step 2: Enable DHCP in Pi-hole. Go to Settings > DHCP in the web dashboard. Enable the DHCP server and configure:

  • Range start: 192.168.1.100
  • Range end: 192.168.1.250
  • Router (gateway): 192.168.1.1
  • Lease time: 24 hours is a reasonable default

Enable "Enable IPv6 support (SLAAC + RA)" if your network uses IPv6.

Pi-hole's DHCP server (dnsmasq under the hood) automatically sets itself as the DNS server for all clients. No additional configuration needed.

You can assign static DHCP leases in the "Static DHCP leases" section. Enter the MAC address, desired IP, and hostname for each device. This is useful for servers, printers, and other devices that need a fixed IP.

If Pi-hole goes down while running DHCP, existing clients with valid leases will continue working (they already have an IP and know the gateway). But new devices or expired leases will not get an address. This is why a second Pi-hole instance is recommended for critical environments.

18. Client DNS configuration

If you cannot change DNS at the router level, configure individual devices to use Pi-hole.

Linux (systemd-networkd or NetworkManager):

# NetworkManager
nmcli con mod "Wired Connection 1" ipv4.dns "192.168.1.53"
nmcli con mod "Wired Connection 1" ipv4.ignore-auto-dns yes
nmcli con up "Wired Connection 1"

# Verify
cat /etc/resolv.conf

macOS:

# System Settings > Network > Wi-Fi > Details > DNS
# Remove existing servers, add 192.168.1.53
# Or via command line:
sudo networksetup -setdnsservers Wi-Fi 192.168.1.53

Windows:

:: Open PowerShell as Administrator
Set-DnsClientServerAddress -InterfaceAlias "Ethernet" -ServerAddresses "192.168.1.53"
:: Verify
Get-DnsClientServerAddress -InterfaceAlias "Ethernet"

iOS: Settings > Wi-Fi > tap your network > Configure DNS > Manual > add 192.168.1.53.

Android: Settings > Network > Wi-Fi > long-press your network > Modify > Advanced > IP settings: Static > DNS 1: 192.168.1.53.

Android 9+ also supports Private DNS (DNS-over-TLS). Pi-hole does not natively support DoT, but you can put stunnel or nginx in front of it as a TLS terminator. For most home networks, plain DNS on the local LAN is fine since the traffic never leaves your network.

19. Managing blocklists

The default Pi-hole installation includes Steven Black's unified hosts list (~80,000 domains). For stronger blocking, add more lists.

Recommended blocklists:

  • Steven Black unified: https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts
  • OISD (big): https://big.oisd.nl/domainswild -- comprehensive, well-maintained, minimal false positives
  • Firebog ticked lists: https://v.firebog.net/hosts/lists.php?type=tick -- curated collection of safe-to-use lists
  • AdGuard DNS filter: https://adguardteam.github.io/AdGuardSDNSFilter/Filters/filter.txt
  • Hagezi Multi Pro: https://cdn.jsdelivr.net/gh/hagezi/dns-blocklists@latest/domains/pro.txt

Add lists via the web dashboard: Group Management > Adlists. Paste the URL and click "Add". Then update gravity:

pihole -g

This downloads all lists, parses them, deduplicates, and rebuilds the gravity database. It runs automatically via cron every Sunday, but run it manually after adding new lists.

To bulk-add lists from the command line:

sqlite3 /etc/pihole/gravity.db "INSERT INTO adlist (address, enabled) VALUES ('https://big.oisd.nl/domainswild', 1);"
pihole -g

Whitelist domains that blocklists incorrectly flag (false positives):

# Whitelist a single domain
pihole -w example.com

# Whitelist with regex
pihole --white-regex '(\.|^)example\.com$'

# Remove from whitelist
pihole -w -d example.com

Blacklist domains not covered by your lists:

pihole -b badsite.com

More is not always better with blocklists. A massive blocklist with 2 million entries will cause more false positives and break websites. Start with OISD or the Firebog ticked lists and add more only if needed.

20. Monitoring DNS queries

Pi-hole's query log is one of its most powerful features. Every DNS query on your network is recorded with the domain, client, timestamp, and response type.

Real-time tail from the command line:

pihole -t

This shows queries as they arrive. Useful for debugging -- load a website in your browser and watch the domains it contacts.

Web dashboard query log: Shows the last 100 queries by default. Use the search box to filter by domain, client IP, or status. Click a domain to see its WHOIS info or whitelist/blacklist it instantly.

Long-term data: The FTL database at /etc/pihole/pihole-FTL.db stores query history. Query it directly with SQLite:

# Top 20 blocked domains in the last 24 hours
sqlite3 /etc/pihole/pihole-FTL.db \
  "SELECT domain, COUNT(*) as cnt FROM queries WHERE status IN (1,4,5,6,7,8,9,10,11) AND timestamp > strftime('%s','now','-1 day') GROUP BY domain ORDER BY cnt DESC LIMIT 20;"

Privacy levels: If you run Pi-hole for others and want to limit logging, set the privacy level in /etc/pihole/pihole-FTL.conf:

# 0 = show everything (default)
# 1 = hide domains (show "hidden" in log)
# 2 = hide domains and clients
# 3 = anonymous mode (disable most statistics)
# 4 = no logging at all
PRIVACYLEVEL=0

Restart FTL after changing the configuration:

sudo systemctl restart pihole-FTL

For external monitoring, Pi-hole exposes a JSON API at http://<ip>/admin/api.php. Use it with Grafana, Prometheus (via pi-hole-exporter), or custom scripts.

21. Security hardening

Pi-hole handles all DNS traffic on your network. A compromised Pi-hole means an attacker controls name resolution. Harden it properly.

Set a strong admin password:

pihole -a -p

Use a long random password. The web interface is served over plain HTTP by default, so anyone on the LAN can sniff it. Consider putting the admin interface behind an HTTPS reverse proxy.

Restrict DNS to your LAN: Pi-hole should not be an open resolver accessible from the internet. Configure your firewall to block port 53 from external sources:

# iptables
sudo iptables -A INPUT -p tcp --dport 53 -s 192.168.1.0/24 -j ACCEPT
sudo iptables -A INPUT -p udp --dport 53 -s 192.168.1.0/24 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 53 -j DROP
sudo iptables -A INPUT -p udp --dport 53 -j DROP

# firewalld (RHEL/AlmaLinux)
sudo firewall-cmd --permanent --zone=public --remove-service=dns
sudo firewall-cmd --permanent --zone=internal --add-service=dns
sudo firewall-cmd --permanent --zone=internal --add-source=192.168.1.0/24
sudo firewall-cmd --reload

Restrict the web interface: Either bind lighttpd to the LAN IP only or block port 80 from external access. Edit /etc/lighttpd/lighttpd.conf:

server.bind = "192.168.1.53"

Disable remote API access if not needed: Add to /etc/pihole/pihole-FTL.conf:

LOCAL_IPV4=192.168.1.53

Keep the system updated:

sudo apt update && sudo apt upgrade -y  # Debian/Ubuntu
sudo dnf update -y                       # RHEL/AlmaLinux

Enable rate limiting to prevent DNS amplification attacks. In /etc/pihole/pihole-FTL.conf:

RATE_LIMIT=1000/60

This allows 1000 queries per 60 seconds per client before blocking that client temporarily.

SSH hardening: Disable password auth, use key-based authentication, and change the default SSH port if the Pi-hole host is exposed to the internet.

22. Performance optimization

Pi-hole handles thousands of queries per second on modest hardware. But for large networks or if you want faster response times, tune these settings.

Increase cache size. The default FTLDNS cache is 10,000 entries. For networks with many clients, increase it in /etc/pihole/setupVars.conf:

CACHE_SIZE=50000

Restart FTL after changing:

sudo systemctl restart pihole-FTL

Enable prefetching in Unbound (if using it). The prefetch: yes directive in the Unbound config causes it to refresh popular cached entries before they expire, so clients always get a cached response.

Limit long-term logging. The FTL database grows continuously. Set a retention period in /etc/pihole/pihole-FTL.conf:

MAXDBDAYS=90

This keeps 90 days of query data and prunes older entries. Reduces disk usage and speeds up long-term data queries in the dashboard.

Rate limiting: Prevent a single misbehaving client from flooding the resolver:

RATE_LIMIT=1000/60

Optimize gravity updates. With many blocklists, pihole -g can take several minutes and spike CPU usage. Schedule it during off-peak hours. Edit the Pi-hole cron job:

sudo crontab -l -u pihole
# Change the gravity update time if needed
# Default is a random time on Sunday

Monitor resource usage:

# Check FTL memory usage
pihole -c -e

# Check cache hit rate
# In the dashboard: Settings > System > Cache information
# High hit rate (>50%) means the cache is working well

If you run Pi-hole in Docker, ensure the container has enough memory allocated. The default Docker memory limit may throttle FTL under heavy load.

23. Troubleshooting

When something breaks, start with the built-in debug tool:

pihole -d

This generates a debug log and optionally uploads it to the Pi-hole support site. It checks service status, configuration, port bindings, DNS resolution, and gravity health.

DNS not resolving at all:

# Check if FTL is running
sudo systemctl status pihole-FTL

# Check if port 53 is bound
sudo ss -tlnp | grep :53

# Test local resolution
dig @127.0.0.1 example.com

# Check upstream connectivity
dig @1.1.1.1 example.com

If port 53 is occupied by another process (often systemd-resolved), disable it:

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

Website is broken (false positive blocking):

# Check the query log for the domain
pihole -q brokendomain.com

# If it is blocked, whitelist it
pihole -w brokendomain.com

Gravity update fails:

# Run gravity with verbose output
pihole -g -v

# Check if list URLs are reachable
curl -sSI https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts | head -5

# Check disk space
df -h /etc/pihole/

High CPU usage by pihole-FTL: Usually caused by an extremely large blocklist or a client flooding queries. Check the query log for unusual patterns. Enable rate limiting if a client is misbehaving.

Web interface not loading:

# Check lighttpd
sudo systemctl status lighttpd
sudo lighttpd -t -f /etc/lighttpd/lighttpd.conf

# Check if PHP is working
php -v

# Repair web interface
pihole -r  # Choose "repair"

Complete reinstall without losing settings:

# Back up first
cp /etc/pihole/setupVars.conf ~/setupVars.conf.bak
cp /etc/pihole/gravity.db ~/gravity.db.bak

# Reinstall
pihole -r  # Choose "reconfigure"

24. Testing DNS resolution

After setup, verify that Pi-hole is working correctly from a client device.

Confirm your client is using Pi-hole:

# Linux/macOS
nslookup example.com
# The "Server" line should show your Pi-hole IP (e.g., 192.168.1.53)

# Windows
nslookup example.com
# Same check -- server should be Pi-hole's IP

Test that blocking works:

# This domain should be blocked (it is on most blocklists)
dig @192.168.1.53 ads.google.com

# You should get 0.0.0.0 as the answer, or NXDOMAIN
# If you get a real IP, blocking is not working

Test that legitimate domains resolve:

dig @192.168.1.53 example.com
dig @192.168.1.53 google.com
dig @192.168.1.53 publicdns.info

Test DNSSEC validation (if enabled):

# Should return SERVFAIL (bad DNSSEC)
dig @192.168.1.53 dnssec-failed.org

# Should return a valid answer with 'ad' flag
dig @192.168.1.53 +dnssec example.com

Check Pi-hole from the command line:

# Summary statistics
pihole -c

# Check status
pihole status

# Watch queries in real time
pihole -t

Use Pi-hole's built-in debug:

pihole -d

This runs a comprehensive health check: services, ports, configuration, upstream resolution, gravity database integrity, disk space, and more. Review the output for any red flags.

Finally, browse a few ad-heavy sites (news sites are good candidates) and check that ads are replaced with blank space or missing entirely. The Pi-hole dashboard should show blocked queries increasing in real time.

25. Maintenance and updates

Updating Pi-hole:

pihole -up

This updates the Pi-hole core, the web interface, and FTLDNS. Run it periodically -- Pi-hole does not auto-update. Check for updates without installing:

pihole -up --check-only

Updating gravity (blocklists):

pihole -g

This runs automatically via cron (weekly by default). You can verify the cron schedule:

cat /etc/cron.d/pihole

Backing up configuration: Pi-hole has a built-in backup tool called Teleporter. In the web dashboard, go to Settings > Teleporter and click "Backup". This exports your settings, blocklists, whitelist, blacklist, and client/group configurations as a .tar.gz file.

From the command line, back up the critical files:

sudo tar czf pihole-backup-$(date +%Y%m%d).tar.gz \
  /etc/pihole/setupVars.conf \
  /etc/pihole/pihole-FTL.conf \
  /etc/pihole/gravity.db \
  /etc/pihole/custom.list \
  /etc/dnsmasq.d/

Restoring from backup: Use the Teleporter "Restore" function in the web dashboard, or extract the tar.gz and place files back in /etc/pihole/.

Updating the OS:

# Debian/Ubuntu
sudo apt update && sudo apt upgrade -y

# RHEL/AlmaLinux
sudo dnf update -y

Reboot after kernel updates. Pi-hole starts automatically via systemd.

Monitoring disk space: The FTL database is the main space consumer. Check its size periodically:

du -sh /etc/pihole/pihole-FTL.db

If it grows too large, reduce MAXDBDAYS in /etc/pihole/pihole-FTL.conf or set the privacy level higher to reduce logging.

Automating maintenance with cron:

# Update gravity weekly (already set by Pi-hole)
# Update root hints monthly (if using Unbound)
0 3 1 * * wget -qO /var/lib/unbound/root.hints https://www.internic.net/domain/named.root && systemctl restart unbound

Regularly review the Pi-hole dashboard for unusual patterns: spikes in blocked queries may indicate malware on a device. Drops in query volume may mean clients are bypassing Pi-hole.

Frequently Asked Questions

Does Pi-hole block ads on all devices?

Yes, when configured as the DNS server for your network (either via router DHCP or per-device settings), Pi-hole blocks ad and tracker domains for every device on the network -- phones, tablets, smart TVs, and IoT devices included. It cannot block ads served from the same domain as content (e.g., some YouTube ads) since those share the same DNS name as the video stream.

Does Pi-hole slow down my internet?

No. Pi-hole typically makes browsing faster. Blocked domains resolve instantly (returned as 0.0.0.0), so your browser never wastes time downloading ads, tracking pixels, or third-party scripts. Legitimate DNS queries are cached locally, which reduces latency compared to forwarding every query to an external resolver.

Can I use Pi-hole with a VPN?

Yes. You can run Pi-hole alongside WireGuard or OpenVPN so that remote devices tunnel their DNS through Pi-hole. This gives you ad blocking on mobile devices outside your home network. Many guides pair Pi-hole with PiVPN for this exact setup.

What happens if Pi-hole goes down?

If Pi-hole becomes unreachable, DNS resolution stops for devices using it. To mitigate this, run a second Pi-hole instance as a secondary DNS server. Some routers also let you configure a fallback DNS, though that bypasses Pi-hole when it is down.

How do I whitelist a blocked domain?

Open the web dashboard at http://pi.hole/admin, go to Whitelist (or Domains in Pi-hole v6+), and add the domain. Alternatively, run: pihole -w example.com from the command line. The change takes effect immediately without restarting services.

Does Pi-hole block malware?

Pi-hole blocks connections to known malware and phishing domains if you subscribe to blocklists that include them. Lists from firebog.net (the "ticked" category) and Steven Black unified hosts include malware domains. This is not a replacement for endpoint antivirus software, but it adds a strong layer of network-level protection.

Can Pi-hole run on something other than a Raspberry Pi?

Absolutely. Pi-hole runs on any Linux machine -- a VM, an old laptop, a cloud VPS, or a Docker container. The Raspberry Pi is popular because it is cheap and low-power, but Pi-hole has minimal resource requirements and works on any supported Linux distribution.

How much storage does Pi-hole need?

Pi-hole itself uses under 200 MB of disk space. Long-term query logging is the main storage consumer. On a busy network with full logging enabled, the FTL database can grow to several gigabytes over months. You can limit this by adjusting MAXDBDAYS in pihole-FTL.conf or disabling long-term logging.