Pi-hole is a Linux-based DNS sinkhole that functions as a network-wide DNS resolver. Every device on your network sends DNS queries through Pi-hole, which compares each requested domain against a local blocklist database (called Gravity) containing over 2 million known ad, tracker, and malware-serving domains. Blocked domains resolve to 0.0.0.0 — the connection never leaves your network. Clean queries are forwarded to your upstream resolver of choice (Cloudflare, Quad9, Google, or your own recursive resolver).
This guide walks through three complete installation paths: a Raspberry Pi running Raspberry Pi OS, a Docker host on any Linux system, and a fresh Ubuntu or Debian VM. All three end at the same place — a running Pi-hole with a configured admin panel, a customised blocklist, and your router pointing DNS traffic through it. Every command is production-accurate for Pi-hole v5.18+.
If you are following this on a VM or Docker host and only want to protect a single machine or lab segment, that works too — simply point that machine's DNS to the Pi-hole IP rather than your router's DHCP settings.
◈ Table of Contents
01 — What Pi-hole Does
SECURITY CONTEXT
Pi-hole intercepts DNS resolution at the network level. When a browser, app, or IoT device on your network tries to resolve a hostname, the query goes to Pi-hole first. Pi-hole checks the domain against its Gravity database. If there is a match, Pi-hole returns 0.0.0.0 (a null address), which causes the connection attempt to fail silently. No packet ever reaches the ad server, telemetry endpoint, or malware C2 domain.
Malware Domain Blocking
Blocks DNS resolution to known C2 infrastructure, phishing domains, and malware distribution hosts. Works across every device on the network including those you cannot install software on.
Full DNS Visibility
Every DNS query across your network is logged with timestamp, client IP, and resolution result. Useful for detecting suspicious lateral movement or beaconing behaviour from compromised hosts.
Tracker & Telemetry Suppression
Stops Windows telemetry, smart TV call-home traffic, and third-party trackers at the DNS layer — before a connection is established, not after.
Lightweight Footprint
Runs on a Raspberry Pi Zero 2 W (512 MB RAM). Handles thousands of DNS queries per second. Adds under 1 ms of latency for cached responses.
DNS RESOLUTION FLOW — Clean queries reach upstream; blocked domains are sunk locally.
02 — Prerequisites & Requirements
ALL PLATFORMSPi-hole has modest resource requirements. The critical constraint is a static IP address — if Pi-hole's IP changes, all clients pointing to it lose DNS resolution. Set a static IP or DHCP reservation before starting the installer.
| Platform | OS | RAM | Storage | Notes |
|---|---|---|---|---|
| Raspberry Pi | Raspberry Pi OS Lite (Bookworm 64-bit) | 512 MB min 1 GB+ rec |
8 GB SD card | Pi Zero 2 W or newer. Pi 3/4/5 all work. Headless recommended. |
| Docker | Any Linux host with Docker 20.10+ | 512 MB rec | 2 GB free disk | Port 53 must be free on host. Disable systemd-resolved on Ubuntu first. |
| VM | Ubuntu 22.04 / 24.04 LTS or Debian 12 | 1 GB rec | 10 GB disk | Virtualbox, VMware, Proxmox, Hyper-V all work. Bridged networking required. |
Use bridged network mode for VMs, not NAT. Pi-hole must be directly reachable by other devices on your LAN as a DNS server. With NAT, only the hypervisor host can reach it.
Pi-hole is not a recursive DNS resolver — it is a DNS sinkhole and forwarder. It requires an upstream DNS server to resolve clean queries. Do not point Pi-hole at itself as its own upstream.
03 — Method 1: Raspberry Pi (Bare Metal)
STEP-BY-STEPDownload Raspberry Pi Imager from raspberrypi.com. Select Raspberry Pi OS Lite (64-bit) — no desktop needed. Before writing the image, click the gear icon (advanced options) and configure:
- aEnable SSH with password authentication or a pre-loaded public key.
- bSet your hostname (e.g.
pihole.local), username, and password. - cConfigure Wi-Fi SSID and password if using wireless — wired Ethernet is strongly preferred for a DNS server.
Write the image to your SD card, insert it, power on the Pi, and wait approximately 90 seconds for first boot to complete.
Find the Pi's IP on your router's DHCP leases page or use a network scanner. SSH in with the credentials you set in Imager.
On Raspberry Pi OS Bookworm, network configuration uses nmcli (NetworkManager). Set a static IP on your active connection. Replace the example values with your network's details.
Alternatively, set a DHCP reservation on your router by binding the Pi's MAC address to a fixed IP. Either method works — what matters is the IP never changes.
The official installer script handles all dependencies, installs the Pi-hole service, sets up the web interface, and configures dnsmasq as the DNS resolver backend.
If you prefer to inspect the script before running (recommended):
- 1Select network interface: choose
eth0for wired (recommended) orwlan0for wireless. - 2Select upstream DNS: choose Cloudflare (
1.1.1.1), Quad9 (9.9.9.9), or Google (8.8.8.8). This can be changed after install. - 3Install default blocklists: select Yes — this adds the StevenBlack hosts list as a starting point.
- 4Install Admin Web Interface: select Yes.
- 5Install lighttpd: select Yes — this is the web server for the admin panel.
- 6Enable query logging: select Yes. Logs are stored locally; configure retention below.
- 7Privacy level: select "Show everything" for full query logging, or a higher privacy mode if desired.
- The installer prints the admin panel URL: http://pi.hole/admin or http://[your-IP]/admin
- The installer prints a randomly generated admin password — copy it now
- Pi-hole is immediately running and serving DNS on port 53
04 — Method 2: Docker Installation
STEP-BY-STEPThese commands install Docker Engine from Docker's official repository on Ubuntu or Debian. Skip if Docker is already installed.
Ubuntu 18.04+ and Debian 12 run systemd-resolved, which occupies port 53 on localhost. Pi-hole cannot bind to port 53 while this is active. You must disable it before starting the container.
After Pi-hole is running, it will serve as your host's DNS resolver too. Do not leave /etc/resolv.conf pointing at 127.0.0.53 — that is the stub resolver address for the now-disabled systemd-resolved and will cause DNS failures.
Create a dedicated directory for Pi-hole's Docker deployment. The compose file below maps persistent volumes for the Pi-hole configuration and dnsmasq settings so your configuration survives container restarts and image updates.
PIHOLE_DNS_ accepts semicolon-separated upstream resolvers. Use two entries for redundancy: e.g. Quad9 (9.9.9.9;149.112.112.112) or Cloudflare (1.1.1.1;1.0.0.1). Full timezone list: en.wikipedia.org/wiki/List_of_tz_database_time_zones
- ./etc-pihole — stores Pi-hole's config, blocklists, and Gravity database
- ./etc-dnsmasq.d — stores custom dnsmasq configuration overrides
- Both directories are created automatically by Docker Compose in ~/pihole/
Unlike bare-metal installs, Docker Pi-hole updates are done by pulling the latest image. Your configuration persists in the named volumes.
05 — Method 3: Ubuntu / Debian VM
STEP-BY-STEPMinimum spec: 1 vCPU, 1 GB RAM, 10 GB disk. Use Ubuntu 22.04 LTS or 24.04 LTS (or Debian 12). Set the network adapter to Bridged mode — this gives the VM an IP on your LAN segment, making it reachable by all other devices.
- 1In VirtualBox: Settings → Network → Adapter 1 → Attached to: Bridged Adapter
- 2In VMware Workstation: VM Settings → Network Adapter → Bridged: Connected directly to the physical network
- 3In Proxmox: set the bridge to
vmbr0(or whichever bridge is on your LAN VLAN) - 4In Hyper-V: use an External Virtual Switch connected to your LAN NIC
Ubuntu 22.04 and 24.04 both run systemd-resolved. This must be disabled before Pi-hole can bind to port 53. The procedure is identical to the Docker method above.
The chattr +i command makes /etc/resolv.conf immutable. After Pi-hole is running, it will handle your DNS. If you ever need to edit it, run sudo chattr -i /etc/resolv.conf first.
On Ubuntu 22.04/24.04, use Netplan to assign a static IP. Find your interface name first, then edit the Netplan config.
Identical to the Raspberry Pi install. The interactive installer detects Ubuntu and installs all required dependencies automatically.
Follow the same installer prompts as described in Phase 03, Step 4. Select your static interface (e.g. ens33), choose your upstream DNS, and enable the web interface. Note the admin password printed at the end.
06 — Admin Panel: Initial Configuration
WEB UI
Open a browser and navigate to http://[pi-hole-ip]/admin or http://pi.hole/admin (the latter only works once your device is using Pi-hole as its DNS resolver). Log in with the password shown at the end of the installer.
The installer-generated password is random and long. Replace it with something you can remember — or keep it and store it in a password manager. Change via CLI:
The admin dashboard provides real-time query metrics. Key elements after logging in:
| Panel Element | What It Shows |
|---|---|
| Queries Today | Total DNS queries handled since midnight. Click for a timeline breakdown. |
| Queries Blocked | Count and percentage blocked by Gravity. A 10–25% block rate is typical on most home/office networks. |
| Domains on Blocklist | Total unique domains in the Gravity database across all enabled lists. |
| Top Clients | Which devices are generating the most DNS queries. Useful for spotting beaconing or anomalous devices. |
| Top Blocked Domains | Most frequently sinkholed domains. Usually ad networks and telemetry endpoints. |
| Query Log | Real-time per-query log with client IP, domain, result (Forwarded / Blocked / Cached), and upstream used. |
07 — Upstream DNS & Blocklists
CONFIGURATIONNavigate to Settings → DNS in the admin panel. Pi-hole ships with several upstream provider presets. The choice affects query privacy, DNSSEC support, and which threat feeds are available as upstream. DNSSEC is enabled by default on Quad9.
| Provider | Primary | Secondary | Features |
|---|---|---|---|
| Cloudflare | 1.1.1.1 | 1.0.0.1 | Fast, no-log (1.1.1.1 policy), DNSSEC |
| Quad9 | 9.9.9.9 | 149.112.112.112 | Threat-blocking upstream, DNSSEC, no-log |
| 8.8.8.8 | 8.8.4.4 | Fast, widely tested, logs queries | |
| OpenDNS | 208.67.222.222 | 208.67.220.220 | Additional malware filtering upstream |
Combining Pi-hole with Quad9 as upstream gives you two layers of malware blocking: Pi-hole's Gravity database (DNS sinkhole) and Quad9's threat feed (upstream resolver filter). These are complementary, not redundant.
Navigate to Adlists in the sidebar. Add blocklist URLs — Pi-hole downloads these, deduplicates them, and imports them into the Gravity SQLite database. The curated Firebog lists are a well-maintained starting point used by most practitioners.
- 1StevenBlack Unified (included by default):
https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts— ~220 K domains - 2Hagezi Multi Pro:
https://raw.githubusercontent.com/hagezi/dns-blocklists/main/hosts/pro.txt— ~600 K domains, high accuracy - 3OISD Big:
https://big.oisd.nl— 1 M+ domains, low false positive rate, actively maintained - 4Malware domains (Firebog):
https://malware-filter.gitlab.io/malware-filter/urlhaus-filter-hosts.txt— URLhaus active malware hosting domains - 5Phishing Army:
https://phishing.army/download/phishing_army_blocklist_extended.txt— active phishing domains updated daily
- Domain count jumps from ~185 K (default StevenBlack) to 1–2 M+ with the lists above
- Update runs without interrupting DNS — existing queries continue resolving during rebuild
- Check Tools → Gravity for last update time and total domain count
Blocklists will occasionally catch legitimate domains (false positives). Whitelist them immediately — blocked false positives break applications. Similarly, manually blacklist specific domains your chosen lists do not cover.
08 — Router & Client DNS Configuration
NETWORK CONFIGFor Pi-hole to protect your entire network, every device must send DNS queries to Pi-hole's IP instead of your ISP's resolver. The cleanest approach is to change the DNS server in your router's DHCP settings — all devices that receive an IP from the router via DHCP will automatically use Pi-hole without individual configuration.
Log into your router's admin panel (typically 192.168.1.1 or 192.168.0.1). Find the DHCP server settings — the location varies by router brand:
| Router Brand | DHCP DNS Setting Location |
|---|---|
| Ubiquiti UniFi | Networks → [LAN] → DHCP → DNS Server 1 / DNS Server 2 |
| pfSense / OPNsense | Services → DHCP Server → DNS Servers |
| Asus (Merlin/stock) | LAN → DHCP Server → DNS Server 1 |
| TP-Link (stock) | DHCP → DHCP Settings → Primary DNS |
| Netgear | Advanced → Setup → LAN Setup → Use these DNS Servers |
| Eero | eero app → Network Settings → DNS — set to Custom, enter Pi-hole IP |
Set DNS Server 1 to your Pi-hole's static IP (e.g. 192.168.1.50). Leave DNS Server 2 blank or set it to a fallback (e.g. 9.9.9.9) — if you set a fallback, devices will bypass Pi-hole when it is unreachable. For full enforcement, leave the secondary blank and ensure Pi-hole runs with restart: unless-stopped.
Also disable your router's built-in DNS rebind protection for pi.hole and your local domain, or the router may intercept and modify Pi-hole's responses. In pfSense: Services → DNS Resolver → General Settings → Rebind protection exceptions.
For devices on networks you do not control (corporate Wi-Fi), or for testing Pi-hole on a single machine, set DNS manually on the device:
| Platform | Location |
|---|---|
| Windows 11 | Settings → Network & internet → [adapter] → DNS server assignment → Manual → IPv4 DNS: [Pi-hole IP] |
| macOS | System Settings → Network → [adapter] → Details → DNS → "+" → [Pi-hole IP] |
| Ubuntu Desktop | Settings → Network → [adapter] → IPv4 → DNS: [Pi-hole IP] (disable Automatic DNS) |
| Android | Wi-Fi settings → long-press SSID → Modify network → Advanced → IP settings: Static → DNS 1: [Pi-hole IP] |
| iOS / iPadOS | Settings → Wi-Fi → (i) next to SSID → Configure DNS → Manual → Add Server: [Pi-hole IP] |
After changing DNS on any device, flush the local DNS cache: Windows: ipconfig /flushdns | macOS: sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder | Linux: sudo systemd-resolve --flush-caches (if systemd-resolved still running) or restart the browser.
After configuring your router, reconnect a client device and confirm queries are routing through Pi-hole.
In the Pi-hole admin Query Log, you should now see queries arriving from multiple client IPs on your network, not just from the Pi-hole host itself.
09 — Advanced: Local DNS, DHCP & Conditional Forwarding
ADVANCEDPi-hole can resolve custom hostnames for devices on your LAN — useful for accessing services by name instead of IP. Navigate to Local DNS → DNS Records in the admin panel.
- 1Click Add. Enter the domain (e.g.
nas.home) and the target IP (e.g.192.168.1.100). - 2For subdomains, use Local DNS → CNAME Records to alias
media.home→nas.homeetc. - 3Devices using Pi-hole as DNS can now reach
http://nas.homewithout mDNS or hosts file entries.
Conditional forwarding allows Pi-hole to resolve device hostnames by asking your router for its DHCP leases. Without it, the Query Log shows client IPs but not hostnames. Enable it in Settings → DNS → Conditional Forwarding.
- 1Enable "Use Conditional Forwarding".
- 2Enter your local network CIDR: e.g.
192.168.1.0/24. - 3Enter your router's IP address: e.g.
192.168.1.1. - 4Enter your local domain name if you use one: e.g.
home.lan(leave blank if you do not).
After enabling, reverse DNS lookups for LAN IPs are forwarded to your router, which resolves them to device hostnames. These appear in the Query Log and Top Clients views.
You can disable your router's DHCP server and let Pi-hole hand out IP addresses instead. This ensures every device on the network automatically uses Pi-hole for DNS — no router DHCP configuration needed. Navigate to Settings → DHCP → DHCP Server.
Only enable Pi-hole's DHCP server after disabling your router's DHCP server. Running two DHCP servers on the same segment causes IP conflicts and network outages. Disable the router's DHCP first, wait 2 minutes, then enable Pi-hole's.
- 1In your router, disable the DHCP server.
- 2In Pi-hole: Settings → DHCP → Enable DHCP server. Set the range (e.g.
192.168.1.100–192.168.1.200) and gateway IP. - 3Set the lease time to 24 hours for a home network, 8 hours for a high-turnover environment.
- 4Under DHCP → Static Leases, bind device MAC addresses to fixed IPs for servers and printers.
10 — Monitoring & Maintenance
ONGOING OPSWhen an application breaks after deploying Pi-hole, the cause is almost always a false positive block. The Query Log is the fastest path to diagnosis.
- 1Open Tools → Query Log. Filter by the device's IP address.
- 2Look for domains marked Pi-holed (red) around the time the application broke.
- 3Click the domain → select "Whitelist" to add it immediately.
- 4Alternatively: use Tools → Whitelist to add domains in bulk.
- 5Flush DNS cache on the affected device and retest the application.
Use the Long-term Data → Query Types and Clients views to identify devices generating unusual DNS query volumes — a device querying 500+ unique domains per hour while idle is worth investigating.
11 — Sources & References
DOCUMENTATION◈ Extend Your DNS Security Further
Pi-hole blocks known-bad domains. Pair it with Unbound as a local recursive resolver to eliminate your upstream DNS provider as a trust boundary — your DNS queries never leave your network until the final authoritative nameserver lookup. CyberHawk Threat Intel covers the full Unbound + Pi-hole stack in the Courses section.
📚 Browse Courses 📝 More Guides◈ Stay Connected
Follow CyberHawk Threat Intel for threat intelligence, deployment guides and hands-on SOC tooling content.
"They can't exploit you if you are the Exploit."