How to Use Pi-hole + DNSCrypt to Block Ads and Trackers at the Source
Ad-blocking and privacy are hot topics, especially for developers who understand the underlying mechanics of tracking. While browser extensions offer some protection, they’re limited to your browser and often miss trackers or ads embedded in other applications or devices. Enter Pi-hole: a network-wide ad blocker that acts as a DNS sinkhole. But what about the privacy of your DNS queries themselves? That’s where DNSCrypt-proxy comes in, encrypting your DNS traffic to prevent eavesdropping and manipulation.
This post will guide you through setting up Pi-hole alongside DNSCrypt-proxy, creating a robust, private, and ad-free network environment. We’ll focus on CLI examples, because that’s how we roll.
Why Pi-hole and DNSCrypt Together?
Pi-hole operates as a DNS server. When a device on your network requests to resolve a domain (e.g., google-analytics.com
), Pi-hole checks its blocklists. If the domain is malicious or an ad/tracker, Pi-hole returns an invalid or your own IP address, effectively “sinking” the request. All other legitimate queries are forwarded to an upstream DNS server.
DNSCrypt-proxy is a flexible DNS proxy that adds a layer of encryption between your DNS server (Pi-hole, in this case) and the actual upstream DNS resolver. It prevents man-in-the-middle attacks, DNS spoofing, and ensures your DNS queries aren’t easily intercepted or logged by your ISP. It also supports DNSSEC validation.
The Power Couple: Pi-hole handles the ad/tracker filtering locally, while DNSCrypt-proxy ensures that Pi-hole’s outbound DNS queries are private and secure. This means you get network-wide ad blocking and encrypted DNS lookups, protecting your browsing habits from your ISP or other intermediaries.
Prerequisites
Before we dive in, make sure you have the following:
- A dedicated Linux machine: A Raspberry Pi is perfect, but any Debian/Ubuntu-based system (VM, old PC, cloud instance) will work. Ensure it has a stable internet connection.
- Static IP Address: Your Pi-hole server needs a static IP address on your local network. This is crucial as all your devices will point to it for DNS resolution.
- Sudo Privileges: You’ll need
sudo
access to install and configure software. - Basic Linux CLI knowledge: Familiarity with
apt
,curl
,systemctl
, and text editors likenano
orvim
.
Setting a Static IP Address (Example: Ubuntu/Debian with netplan
)
How you set a static IP depends on your Linux distribution. For modern Debian/Ubuntu systems, netplan
is common.
Note: Adjust enpXsY
to your actual network interface name (use ip a
to find it).
# Backup original netplan config
sudo cp /etc/netplan/00-installer-config.yaml /etc/netplan/00-installer-config.yaml.bak
# Open the netplan configuration file
sudo nano /etc/netplan/00-installer-config.yaml
Modify the file to look something like this, replacing values with your network’s specifics:
# /etc/netplan/00-installer-config.yaml
network:
renderer: networkd
ethernets:
enp0s3: # Your actual network interface name (e.g., eth0, enp3s0)
dhcp4: no
addresses:
- 192.168.1.10/24 # Your desired static IP and subnet mask
routes:
- to: default
via: 192.168.1.1 # Your router's IP address (gateway)
nameservers:
addresses: [1.1.1.1, 8.8.8.8] # Temporary DNS servers for internet access during setup
version: 2
Apply the changes:
sudo netplan try
# If no errors, apply
sudo netplan apply
# Example output from netplan apply
Configuration accepted.
Verify your IP address:
ip a show enp0s3 # Replace enp0s3 with your interface name
# Example output
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 08:00:27:xx:xx:xx brd ff:ff:ff:ff:ff:ff
inet 192.168.1.10/24 brd 192.168.1.255 scope global enp0s3
valid_lft forever preferred_lft forever
inet6 fe80::a00:27ff:fecf:xx:xx/64 scope link
valid_lft forever preferred_lft forever
Step 1: Install Pi-hole
The Pi-hole project provides a convenient one-liner for installation.
curl -sSL https://install.pi-hole.net | sudo bash
# Example output (interactive installer will follow)
[i] Root user detected
[i] Script will now continue with root privileges
.
.
.
[i] Installer will now start dialog based setup.
During the interactive setup:
- Static IP Check: Confirm your static IP.
- Upstream DNS Provider: This is important. For now, select a public, unencrypted DNS provider like Cloudflare (1.1.1.1) or Google (8.8.8.8). We will change this to point to
dnscrypt-proxy
later. - Ad Lists: Keep the default lists.
- Protocols: Select both IPv4 and IPv6 if you use both.
- Web Admin Interface: Highly recommended.
- Lighttpd web server: Recommended.
- Log Queries: Recommended.
Make a note of the Admin Web Panel Password displayed at the end of the installation.
Test Pi-hole (Initial)
After installation, your Pi-hole is running and listening on port 53.
Access the web interface at http://YOUR_PIHOLE_IP/admin
using the password noted earlier.
Now, configure a client (e.g., your computer, phone) to use your Pi-hole’s static IP address as its sole DNS server. Then, try browsing some ad-heavy websites. You should see an increase in “Queries Blocked” on the Pi-hole dashboard.
# On your Pi-hole server, you can tail the Pi-hole log:
pihole -t
# Example output when a client queries and Pi-hole blocks
Oct 27 10:30:00 dnsmasq[1234]: query[A] doubleclick.net from 192.168.1.15
Oct 27 10:30:00 dnsmasq[1234]: /etc/pihole/gravity.list doubleclick.net is 0.0.0.0
Oct 27 10:30:05 dnsmasq[1234]: query[A] example.com from 192.168.1.15
Oct 27 10:30:05 dnsmasq[1234]: forwarded example.com to 1.1.1.1
Oct 27 10:30:05 dnsmasq[1234]: reply example.com is <CNAME>
Oct 27 10:30:05 dnsmasq[1234]: reply example.com is 93.184.216.34
Step 2: Install and Configure DNSCrypt-Proxy
Now for the encryption layer. We’ll download the latest dnscrypt-proxy
binary and set it up.
Download and Extract
Visit the DNSCrypt-proxy GitHub Releases page and find the latest stable release for your architecture (e.g., linux-arm64
for Raspberry Pi 64-bit, linux-x86_64
for most desktops/servers).
# Example for linux-x86_64; adjust URL and filename as needed
cd /tmp
DN_VERSION="2.1.4" # Check for the latest version
DN_ARCH="linux-x86_64" # Or linux-arm, linux-arm64, etc.
wget "https://github.com/DNSCrypt/dnscrypt-proxy/releases/download/${DN_VERSION}/dnscrypt-proxy-${DN_ARCH}-${DN_VERSION}.tar.gz"
tar -xzf "dnscrypt-proxy-${DN_ARCH}-${DN_VERSION}.tar.gz"
cd "dnscrypt-proxy-${DN_ARCH}-${DN_VERSION}"
# Copy the executable and example config
sudo cp dnscrypt-proxy /usr/local/bin/
sudo cp example-dnscrypt-proxy.toml /etc/dnscrypt-proxy.toml
# Example output for copy
'dnscrypt-proxy' -> '/usr/local/bin/dnscrypt-proxy'
'example-dnscrypt-proxy.toml' -> '/etc/dnscrypt-proxy.toml'
Configure DNSCrypt-Proxy
Now we edit the dnscrypt-proxy.toml
configuration file. This is where you specify which encrypted DNS resolvers to use and what port DNSCrypt-proxy will listen on.
Crucial Point: Pi-hole already listens on port 53. DNSCrypt-proxy must listen on a different port, e.g., 5353
.
sudo nano /etc/dnscrypt-proxy.toml
Find and modify the following lines:
-
listen_addresses
: Change this to127.0.0.1:5353
(or any other unused port). This means DNSCrypt-proxy will listen locally on this port, and Pi-hole will send queries to it.listen_addresses = ['127.0.0.1:5353']
-
server_names
: This is a list of DNSCrypt-compatible resolvers. You can find a full list indnscrypt-proxy/dnscrypt-proxy.toml
(from the extracted archive) or online. Choose a few reliable, fast, and privacy-focused ones.server_names = ['cloudflare', 'google', 'quad9-ecs', 'opendns'] # Example, pick ones you trust
Note: You can uncomment and use the provided
require_dnssec
,require_nologs
,require_nofiltering
options to ensure chosen servers meet specific criteria. -
Optionally, review and enable/disable features like
fallback_resolvers
,keepalive
,timeout
,log_level
, etc., based on your needs. For most users, the defaults are fine oncelisten_addresses
andserver_names
are set.
Save and exit the file.
Create a Systemd Service
To ensure dnscrypt-proxy
starts automatically on boot, we’ll create a systemd service file.
sudo nano /etc/systemd/system/dnscrypt-proxy.service
Paste the following content:
[Unit]
Description=DNSCrypt-proxy client
After=network.target
[Service]
ExecStart=/usr/local/bin/dnscrypt-proxy -config /etc/dnscrypt-proxy.toml
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
[Install]
WantedBy=multi-user.target
Reload systemd daemon, enable, and start the service:
sudo systemctl daemon-reload
sudo systemctl enable dnscrypt-proxy.service
sudo systemctl start dnscrypt-proxy.service
# Example output
Created symlink /etc/systemd/system/multi-user.target.wants/dnscrypt-proxy.service → /etc/systemd/system/dnscrypt-proxy.service.
Test DNSCrypt-Proxy
Verify that DNSCrypt-proxy is running and listening on the specified port:
sudo systemctl status dnscrypt-proxy.service
# Example output
● dnscrypt-proxy.service - DNSCrypt-proxy client
Loaded: loaded (/etc/systemd/system/dnscrypt-proxy.service; enabled; vendor preset: enabled)
Active: active (running) since Fri 2023-10-27 10:45:00 UTC; 1min 2s ago
Main PID: 12345 (dnscrypt-proxy)
Tasks: 6 (limit: 4915)
Memory: 5.6M
CPU: 0.052s
CGroup: /system.slice/dnscrypt-proxy.service
└─12345 /usr/local/bin/dnscrypt-proxy -config /etc/dnscrypt-proxy.toml
You can also test it by querying directly:
dig @127.0.0.1 -p 5353 example.com
# Example output
; <<>> DiG 9.16.1-Ubuntu <<>> @127.0.0.1 -p 5353 example.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 23456
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags: do; udp: 1232
;; QUESTION SECTION:
;example.com. IN A
;; ANSWER SECTION:
example.com. 300 IN A 93.184.216.34
;; Query time: 15 msec
;; SERVER: 127.0.0.1#5353(127.0.0.1)
;; WHEN: Fri Oct 27 10:48:00 UTC 2023
;; MSG SIZE rcvd: 55
This confirms DNSCrypt-proxy is listening and resolving queries.
Step 3: Integrate Pi-hole with DNSCrypt-Proxy
This is the final, crucial step: telling Pi-hole to use your local DNSCrypt-proxy instance as its upstream DNS resolver.
Configure Pi-hole Upstream DNS
Access your Pi-hole web admin interface (e.g., http://YOUR_PIHOLE_IP/admin
).
- Go to Settings on the left sidebar.
- Navigate to the DNS tab.
- Under “Upstream DNS Servers”, uncheck any previously selected public DNS servers (e.g., Cloudflare, Google).
- Check the Custom 1 (IPv4) box.
- In the adjacent text field, enter
127.0.0.1#5353
(replace5353
if you used a different port for DNSCrypt-proxy). - Click Save at the bottom.
This tells Pi-hole: “For any non-blocked queries, send them to DNSCrypt-proxy running on this same machine, listening on port 5353.”
Verify the Integration
Now, all your network’s DNS queries will flow like this:
Client -> Pi-hole (filters ads) -> DNSCrypt-proxy (encrypts) -> External Encrypted DNS Server
Let’s confirm everything is working as expected.
-
Check Pi-hole’s DNS settings:
# On your Pi-hole server grep "PIHOLE_DNS_1" /etc/pihole/setupVars.conf
# Expected output PIHOLE_DNS_1=127.0.0.1#5353
-
Monitor DNSCrypt-proxy logs:
journalctl -u dnscrypt-proxy.service -f
This will show you real-time logs of queries being processed by DNSCrypt-proxy. When you visit a website from a client device configured to use Pi-hole, you should see corresponding queries here.
# Example output from dnscrypt-proxy logs Oct 27 10:55:00 dnscrypt-proxy[12345]: [2023-10-27 10:55:00] [NOTICE] Querying 'google.com' (A) from 127.0.0.1:45678 with provider cloudflare-dns Oct 27 10:55:00 dnscrypt-proxy[12345]: [2023-10-27 10:55:00] [NOTICE] Querying 'example.org' (A) from 127.0.0.1:45679 with provider quad9-ecs
-
Perform a DNS Leak Test: From a client device configured to use Pi-hole for DNS, visit a site like dnsleaktest.com. Run the “Extended test”.
- You should not see your ISP’s DNS servers.
- You should see the IP addresses corresponding to the encrypted DNS providers you selected in
dnscrypt-proxy.toml
(e.g., Cloudflare, Google, Quad9, OpenDNS). This confirms your queries are being encrypted and routed through DNSCrypt-proxy.
Troubleshooting Common Issues
-
No Internet Access on Clients:
- Double-check your client’s DNS settings point to the Pi-hole’s static IP.
- Ensure Pi-hole is running (
sudo systemctl status pihole-FTL.service
). - Ensure DNSCrypt-proxy is running (
sudo systemctl status dnscrypt-proxy.service
). - Verify Pi-hole’s upstream DNS is correctly set to
127.0.0.1#5353
. - Check firewall rules (
sudo ufw status
orsudo iptables -L
). Ensure port 53 (for Pi-hole) and the port DNSCrypt-proxy is using are open, if necessary (usually not needed for127.0.0.1
binding).
-
DNSCrypt-proxy not starting:
- Check
journalctl -u dnscrypt-proxy.service
for errors. - Ensure the
ExecStart
path in the systemd unit file is correct (/usr/local/bin/dnscrypt-proxy
). - Verify the
dnscrypt-proxy.toml
file has correct syntax and no conflicts (e.g., port already in use).
- Check
-
Pi-hole Dashboard not showing queries:
- Clients are likely not using Pi-hole for DNS.
- Check your router’s DHCP settings to ensure it’s advertising Pi-hole as the DNS server.
- Restart Pi-hole (
sudo pihole restartdns
).
Maintenance and Advanced Tips
- Updating Pi-hole:
pihole -up
- Updating DNSCrypt-proxy: This is a manual process. Download the latest binary from GitHub, replace the old one in
/usr/local/bin/
, and restart the service (sudo systemctl restart dnscrypt-proxy.service
). - Adding More Blocklists: In the Pi-hole web interface, go to Settings > Adlists. You can find many community-curated blocklists online (e.g., on GitHub). After adding, run
pihole -g
to update gravity. - Conditional Forwarding: If you have local hostnames (e.g., from your router’s DHCP server or a local DNS server), you can enable “Conditional Forwarding” in Pi-hole’s DNS settings. This tells Pi-hole to forward queries for your local domain (e.g.,
local
orlan
) to your router’s IP. - Set Router’s DNS: The most effective way to ensure all devices on your network use Pi-hole is to configure your router’s DHCP settings to advertise your Pi-hole’s IP address as the primary DNS server. This way, all devices (including IoT gadgets) automatically benefit from blocking and privacy.
Conclusion
By combining Pi-hole’s network-wide ad and tracker blocking with DNSCrypt-proxy’s encrypted DNS lookups, you’ve built a robust, privacy-centric shield for your entire network. You’re not just blocking annoying ads; you’re taking control of your DNS traffic, protecting your browsing habits from surveillance, and ensuring the integrity of your DNS resolutions.
This setup empowers you with a level of network control and privacy that’s often overlooked. Enjoy a cleaner, faster, and more private internet experience for all your devices.
# No output for the full blog post, but each code block has an accompanying output.