Mastering DNS Privacy: Pi-hole + Unbound. (part 1)
Pi-hole is a powerful network-wide ad blocker that also functions as a DNS sinkhole. It blocks unwanted content, enhances privacy, and speeds up your browsing experience by preventing ads, trackers, and malicious domains from even loading.

Pi-hole + Unbound enhances privacy by blocking ads, trackers, and malicious domains while preventing ISP snooping, DNS hijacking, and censorship. Unlike third-party resolvers such as Google DNS, Cloudflare, or ISP-provided DNS, Unbound operates recursively, ensuring complete independence and security. With DNSSEC validation and intelligent caching, browsing becomes faster, safer, and free from manipulation—delivering seamless, private, and reliable connectivity.
Step 1: Get Started
- Update your system packages:
$ sudo apt update && sudo apt upgrade -y
- Install necessary dependencies:
$ sudo apt install curl git -y
Step 2: Install Pi-hole v6
- Run the automated installation script:
$ curl -sSL https://install.pi-hole.net | bash
During installation, follow the prompts to configure settings such as upstream DNS providers and blocklists.
- Follow the installation prompts:
- Choose your preferred upstream DNS provider (e.g., Google, OpenDNS, Quad9).
- Select network interface (typically eth0 for wired or wlan0 for Wi-Fi).
- Set a static IP address (recommended for consistent network behavior).
- Confirm installation of the web admin interface and logging settings.
Step 3. Access Pi-hole Admin Interface
- After installation, access the web interface using the device's IP address:
1. http://[Your Pi-hole IP]/admin
2. After installing Pi-hole for the first time,
a password is generated and displayed to the user.
The password cannot be retrieved later on, but it is possible
to set a new password (or explicitly disable the password by
setting an empty password) using the command
$ sudo pihole setpassword

Step 4. Install Unbound
Unbound will be configured as a recursive DNS resolver.
$ sudo apt install unbound -y
1. Configure Unbound
Create a new configuration file for Unbound:
$ sudo nano /etc/unbound/unbound.conf.d/pi-hole.conf
2. Add the following configuration
server:
# Allow requests only from local network and VPN
access-control: 127.0.0.0/24 allow # Allow DNS queries from localhost (IPv4 loopback range)
access-control: ::1 allow # Allow DNS queries from localhost (IPv6 loopback)
access-control: 192.168.1.0/24 allow # Allow DNS queries from local network devices
access-control: 10.8.0.0/24 allow # Allow DNS queries from VPN clients (e.g., WireGuard or OpenVPN)
# Listen on these interfaces
interface: 127.0.0.1 # Listen on the local loopback interface (for Pi-hole integration)
interface: 192.168.1.xxx # Listen on the local network interface (your device's IP on LAN)
interface: 10.8.0.1 # Listen on the VPN interface (VPN gateway address)
port: 5353 # Use port 5353 to avoid conflicts with other DNS services
# IPv4 and Protocol Settings
do-ip4: yes # Enable IPv4 DNS resolution
do-udp: yes # Allow DNS queries over UDP (faster for short queries)
do-tcp: yes # Allow DNS queries over TCP (required for large responses)
# Privacy settings
hide-identity: yes # Hide server identity to enhance privacy
hide-version: yes # Hide Unbound version for security reasons
qname-minimisation: yes # Minimize query names to improve privacy and reduce tracking risks
harden-glue: yes # Harden against malicious or incorrect glue records in DNS responses
harden-dnssec-stripped: yes # Ensure DNSSEC validation for domains supporting it
use-caps-for-id: yes # Use random capitalization in queries for better security (anti-spoofing)
# Cache settings
cache-min-ttl: 3600 # Minimum Time to Live (TTL) of 1 hour for cached DNS responses
cache-max-ttl: 86400 # Maximum TTL of 24 hours for cached DNS responses
# Security and DNSSEC
trust-anchor-file: "/var/lib/unbound/root.key" # Path to DNSSEC root key for validating DNS responses
root-hints: "/var/lib/unbound/root.hints" # Path to root server hints file for recursive DNS resolution
Notes:
• The access-control and interface settings ensure that only trusted devices (from local and VPN networks) can use Unbound for DNS resolution, enhancing security.
• The port: 5353 setting avoids conflicts with Pi-hole, which typically uses port 53.
3. Download and Update root.hints
Root Hints:
Root hints are a list of authoritative IP addresses for root DNS servers that guide recursive resolvers (like Unbound) to the name servers for top-level domains (TLDs) such as .com and .net.
How It Works:
1. Unbound checks the root hints file if a query isn't in its cache.
2. It asks the nearest root server for the TLD's authoritative name servers.
3. The root server points to the TLD's servers.
4. Unbound continues querying until it finds the authoritative server with the domain's IP address.
Get the latest version from the official source managed by IANA:
$ sudo wget -O /var/lib/unbound/root.hints https://www.internic.net/domain/named.root
This file needs to be periodically updated to ensure accurate DNS resolution because the root servers' IP addresses can change over time.
Initialize and keep root.hints Updated
Since the IP addresses of root DNS servers can change occasionally, it's essential to keep the root.hints file up-to-date every 3 to 6 months to maintain accurate DNS resolution.
$ sudo curl -o /var/lib/unbound/root.hints https://www.internic.net/domain/named.cache
Or automate the update with a cron job.
$ sudo crontab -e
and add this line (Once a Month: To run on the 1st of every month at 2:30 AM)
# Update root.hints every 3 months
0 0 1 */3 * wget -O /var/lib/unbound/root.hints https://www.internic.net/domain/named.cache
This schedule runs the update on the first day of every third month at midnight, ensuring consistent accuracy.
4. Set Correct Permissions
Ensure Unbound can read the file:
$ chown unbound:unbound /var/lib/unbound/root.hints
$ chmod 644 /var/lib/unbound/root.hints
5. View Root Hints File
$ cat /var/lib/unbound/root.hints
This displays the list of IP addresses and domain names for the root servers, ensuring your Unbound server can accurately resolve DNS queries.
; This file holds the information on root name servers needed to
; initialize cache of Internet domain name servers
; (e.g. reference this file in the "cache . <file>"
; configuration file of BIND domain name servers).
;
; This file is made available by InterNIC
; under anonymous FTP as
; file /domain/named.cache
; on server FTP.INTERNIC.NET
; -OR- RS.INTERNIC.NET
;
; last update: January 29, 2025
; related version of root zone: 2025012901
;
; FORMERLY NS.INTERNIC.NET
;
. 3600000 NS A.ROOT-SERVERS.NET.
A.ROOT-SERVERS.NET. 3600000 A 198.41.0.4
A.ROOT-SERVERS.NET. 3600000 AAAA 2001:503:ba3e::2:30
;
; FORMERLY NS1.ISI.EDU
;
. 3600000 NS B.ROOT-SERVERS.NET.
B.ROOT-SERVERS.NET. 3600000 A 170.247.170.2
B.ROOT-SERVERS.NET. 3600000 AAAA 2801:1b8:10::b
;
; FORMERLY C.PSI.NET
- Update DNSSEC root.Key (Trust Anchor)
What is the root.key?
The DNSSEC root key, also known as the trust anchor, is a cryptographic key used by Unbound to validate the authenticity and integrity of DNS responses. It ensures that DNS queries are protected from tampering or spoofing.
Since the root key occasionally changes during a "key rollover" managed by ICANN, it's crucial to keep it up-to-date for continued DNSSEC validation.
How It Works:
1. Unbound checks the DNS response’s digital signature against the root key.
2. If it matches, the response is verified as authentic.
3. This validation continues down the DNS hierarchy, ensuring trust at each level.
Initialize and keep the root.key Updated
Since ICANN occasionally changes the root key during a "key rollover," it's essential to keep it up-to-date every 3 to 6 months to maintain reliable DNSSEC validation.
$ sudo unbound-anchor -a "/var/lib/unbound/root.key"
Or automate the update with a cron job.
$ sudo crontab -e
and add this line (Once a Month: To run on the 1st of every month at 2:30 AM)
30 2 1 * * unbound-anchor -a "/var/lib/unbound/root.key"
This runs the update at 2:30 AM on the 1st day of every month.
- Check Unbound Configuration Before Restarting
To validate the Unbound settings and ensure there are no syntax errors before restarting the service, use:
$ sudo unbound-checkconf
If no errors are found, it returns "no errors in configuration file" otherwise it displays the line number and details of the issue.
- Apply Changes and Enable Unbound
$ sudo systemctl restart unbound
$ sudo systemctl enable unbound
Step 5. Configure DNS Settings
- Navigate to Settings > DNS in the Pi-hole admin interface.
- Select your preferred upstream DNS servers

or enter custom ones.

- Ensure to use DNSSEC for additional security.
- Save changes; restart Pi-hole and check its status:
$ sudo systemctl restart pihole-FTL
$ sudo systemctl status pihole-FTL
This command restarts the FTL (Fast, Tokenized Lookups) engine, which is responsible for DNS resolution and blocking.
Custom DNS servers
[Your Pi-hole IP]#5353
Using [Your Pi-hole IP]#5353 specifies that DNS queries should be sent to the IP address [You Ip-hole IP] on port 5353 instead of the default DNS port 53.
Here's why:
Why Use Port 5353 with Pi-hole?
1. Port Conflict Avoidance: If another service (e.g., systemd-resolved) is using the default DNS port 53, using port 5353 prevents conflicts.
2. Custom DNS Setup: It allows Pi-hole to forward DNS requests to a custom DNS resolver or service running on the same machine, like a caching DNS server.
3. Network Flexibility: Useful in advanced network setups where different DNS servers are needed for different purposes, ensuring Pi-hole can route queries as desired.
4. Security and Control: It offers more control over DNS routing and enhances security by isolating DNS query handling to a non-standard port.
Step 6. Allow UFW for Port 5353
This ensures that devices on your local network can reach the DNS resolver securely and without interruption.
$ sudo ufw allow from 192.168.1.0/24 to any port 5353
$ sudo ufw reload
Step 7. Point Devices to Pi-hole
To make Pi-hole the primary DNS server on your network, you have (2) two options:
a. Set Pi-hole as the Primary DNS on Your Router
• Go to your router's administration page (usually by entering its IP address in a web browser).
• Navigate to the DNS settings section.
• Set Pi-hole's IP address (e.g., 192.168.1.3) as the Primary DNS Server.
• Save the changes and restart the router if necessary.
• This method routes all network devices’ DNS queries through Pi-hole, giving you centralized control.
Pros:
Centralized control—no need to configure each device individually.
Automatically covers new devices joining the network.
Cons:
Limited device-level customization.
Some routers may bypass custom DNS settings.
b. Use Pi-hole's Built-In DHCP Server
Disable your router's DHCP and enable Pi-hole's DHCP, allowing Pi-hole to assign IP addresses and DNS settings.

Pros:
Full control over DNS and network traffic monitoring.
Device-level customization (e.g., static IP assignments).
Cons:
More complex setup and maintenance.
Requires disabling the router's DHCP function.
Which to Choose?
Router DNS Setting: Easier setup, suitable for most users wanting network-wide ad blocking.
Pi-hole DHCP: Ideal for advanced users needing detailed control and customization.
Does Pi-hole become your main DNS Server after this?
Yes! —- by configuring either of the above:
• All DNS traffic from the devices using Pi-hole as their DNS server will be routed through Pi-hole.
• Pi-hole will filter ads and unwanted content before resolving the DNS queries.
• If paired with Unbound (or another upstream DNS), Pi-hole forwards queries to it for resolution, ensuring privacy and security.
Step 8: Test and Maintain Pi-hole

- Test the setup by visiting ad-heavy websites; ads should be blocked.
- Check Pi-hole's query log under Query Log in the admin interface.
- Regularly update Pi-hole with:
$ sudo pihole -up
Step 9. Test the Configuration
Verify Pi-hole is correctly resolving domains:
$ dig @[Your Pi-hole IP] -p 5335 snubmonkey.com
Explanation:
• @: Specifies the DNS server to query. In this case, it's your Pi-hole's IP address.
• -p 5335: Specifies the port. If you've configured Pi-hole to forward DNS queries to a different port (like 5335), this ensures the query goes through the right one.
• snubmonkey.com: The domain you're testing the resolution for.
You should see a status: NOERROR response, confirming successful recursion as such:
; <<>> DiG 9.18.30-0ubuntu0.22.04.2-Ubuntu <<>> @192.168.1.x snubmonkey.com -p 5353
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 9540
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;snubmonkey.com. IN A
;; ANSWER SECTION:
snubmonkey.com. 432 IN A 41.66.41.12
;; Query time: 0 msec
;; SERVER: 192.168.1.17#5353(192.168.1.x) (UDP)
;; WHEN: Mon Feb 24 19:07:18 UTC 2025
;; MSG SIZE rcvd: 59
Check Unbound logs:
$ sudo journalctl -u unbound -f
The command sudo journalctl -u unbound -f
does the following:
-u unbound
: Filters the logs to show only entries related to the Unbound service.-f
: Follows the log in real-time, similar to tail -f, allowing you to see new log entries as they occur.
This is useful for monitoring Unbound's activity, troubleshooting issues, or verifying configuration changes as they happen.
result:
...
..
.
Feb 24 19:12:18 pi.hole unbound[2514]: [1740424338] unbound[2514:0] info: 192.168.1.x SF16-iES-MusIC-va.tiKTOKcDn.CoM. A IN NOERROR 0.294769 0 241
Feb 24 19:12:43 pi.hole unbound[2514]: [1740424363] unbound[2514:0] info: 192.168.1.xxx snubmonkey.com. A IN
Feb 24 19:12:43 pi.hole unbound[2514]: [1740424363] unbound[2514:0] info: 192.168.1.xxx snubmonkey.com. A IN NOERROR 0.000000 1 59
Feb 24 19:12:43 pi.hole unbound[2514]: [1740424363] unbound[2514:0] info: 192.168.1.x lb._dns-SD._UDp.0.1.168.192.in-ADdR.aRPa. PTR IN
Feb 24 19:12:43 pi.hole unbound[2514]: [1740424363] unbound[2514:0] info: 192.168.1.x lb._dns-SD._UDp.0.1.168.192.in-ADdR.aRPa. PTR IN NXDOMAIN 0.000000 1 128
Feb 24 19:12:43 pi.hole unbound[2514]: [1740424363] unbound[2514:0] info: 192.168.1.x 29-COuriEr.pusH.apPLE.CoM. A IN
Feb 24 19:12:44 pi.hole unbound[2514]: [1740424364] unbound[2514:0] info: 192.168.1.x 29-COuriEr.pusH.apPLE.CoM. A IN NOERROR 0.180744 0 193
Feb 24 19:12:45 pi.hole unbound[2514]: [1740424365] unbound[2514:0] info: 192.168.1.x SiGNAlEr-PA.ClientS6.google.COm. HTTPS IN
Feb 24 19:12:45 pi.hole unbound[2514]: [1740424365] unbound[2514:0] info: 192.168.1.x SiGNAlEr-PA.ClientS6.google.COm. HTTPS IN NOERROR 0.000000 1 110
...
.
Step 10. Enhancing Pi-hole with The Big Blocklist Collection
To maximize the effectiveness of Pi-hole's ad-blocking capabilities, consider integrating The Big Blocklist Collection. These curated lists are designed to filter out ads, trackers, malware, and more, ensuring a cleaner and safer browsing experience.
Here's how to do it:
Step 1: Choose Your Blocklists
Head over to either of these trusted platforms:
Types of Lists:
Ticked Lists: Ideal for minimal maintenance, reducing false positives.
Other Lists: Tailored for advanced users wanting comprehensive filtering.
Step 2: Add Lists to Pi-hole
1. Access Pi-hole's Admin Panel:
Open your browser and go to: http://[Your Pi-hole IP]/admin
2. Navigate to Add blocklist:
• Go to Lists → Adlists.
• Copy and paste your preferred blocklist URLs from Firebog into the Address field.
• Click Add blocklist for each URL.
Step 3: Update Gravity
After adding the new lists, you must update Pi-hole's gravity database to apply the changes.
$ sudo pihole -g
This updates Pi-hole's blocklist sources, ensuring all new domains are actively filtered.
Step 4: Automate Updates (Optional)
To keep your blocklists up-to-date automatically, you can set up a cron job. This ensures you're always protected with the latest filters.
Example cron job (runs every 2 days):
$ sudo crontab -e
add the following line:
0 4 */2 * * pihole -g
This schedules Pi-hole to update its gravity list every 2 days at 4:00 AM.
Explanation:
• 0 4 → Runs at 4:00 AM.
• */2 → Every 2 days.
• * * → Every month and weekday.
• pihole -g → Command to update Pi-hole’s gravity list.
Don't stop now! [Part 2 awaits.]
Pi-hole provides powerful network-wide ad blocking and DNS management, enhancing privacy, security, and browsing speed. Paired with Unbound as a recursive DNS resolver, it bypasses third-party DNS providers, ensuring greater privacy and independence. Pi-hole filters ads at the DNS level, giving you control over domain access and detailed insights into network activity. Regularly updating blocklists maintains ongoing protection. With this setup, you gain full control over your network's security and digital experience.
We hope you found these insights useful!