Check out my new post instead: Block online ads, trackers and malware with Pi-hole, WireGuard, DoT and DoH servers
ToC
- Overview
- Prerequisites
- Initial Server Setup
- Set Up DNScrypt-proxy
- Set Up Pi-hole
- Set Up WireGuard Using PiVPN
- Restrict Pi-hole web admin interface
- UFW Firewall Rules
- Configure WireGuard Clients
- Add Clients To hosts File
- Full-tunnel VPN vs Split-tunnel VPN
- Set Up Searx
- Further Reading
Overview
If you want to know my rationale for writing this article, you can read my article on surveillance capitalism.
The open source projects we will be using in this article are :
- Pi-hole : Ad blocker for your entire network
- PiVPN : Simplified installer for WireGuard
- WireGuard VPN : Fast and secure VPN that's also easier on the battery
- DNSCrypt-proxy : DNS proxy with support for DNS over HTTPS, DNSCrypt and DNSSEC
- Searx : Metasearch engine with no tracking
We will need a VPS or a bare-metal server. All of our devices like mobile phones, laptops and desktops will connect to that server so it needs to have a public static IP address.
First of all, we will install and set up DNSCrypt-proxy on the server. Eventually all the DNS queries will go through DNSCrypt-proxy on our server rather than through the ISP. DNSCrypt-proxy will use DNS over HTTPS, DNSSEC and DNSCrypt protocols to privately and securely resolve domain names through an upstream DNS provider.
Then, we will install Pi-hole which will block DNS requests made for domains used for advertising, tracking, phishing and spreading malware. All other DNS queries will be forwarded to DNSCrypt-proxy. We will use blocklists prepared by volunteers for blocking harmful domains. We can also manually blacklist or whitelist domains / subdomains / wildcard domains as we wish. I urge you to add all the blocklists recommended by WaLLy3K to your Pi-hole installation to block even more bad domains.
Then, we will install WireGuard using PiVPN which will help us manage VPN clients. WireGuard on the server will establish encrypted tunnels between itself and the clients and all the traffic will flow securely through them.
Finally, we will install Searx metasearch engine. Searx will perform private search on multitude of search engines then combine the results in the order of most common results at the top and will show them to you. You can add / remove search engines later if you wish.
To recap, the flow of all the DNS requests coming from your devices will be like this. OS or applications on your computer or phone will initiate DNS queries; then the queries will go through the WireGuard tunnel to Pi-hole which will block requests for malicious domains and forward the rest to DNSCrypt-proxy which will contact an upstream DNS provider and get the answers.
This setup will be private by default. Nobody outside your network will be able to access it. Pi-hole and Searx will be available to WireGuard clients only. Everything that will happen inside a WireGuard tunnel will stay inside the WireGuard tunnel, haha. And you can access Pi-hole and Searx from wherever you are. In other words, ad blocking and private search will be available to you on the go whether you're at home or at work or vacationing somewhere.
Prerequisites
I hope that you are familiar with Linux command-line and that you're using Ubuntu 20.04 LTS as the OS of your server. Also, the server should have a static IP address attached to it. And the server should have at least 1 GB of RAM and it should be situated at or near your geographic location (for low latency).
Initial Server Setup
First of all, prepare your server. Enable firewall, install all the updates, strengthen SSH settings, etc. To do all this, you can follow instructions given in my Initial Server Setup article and then come back here.
Set Up DNSCrypt-proxy
Install dnscrypt-proxy package.
sudo apt install dnscrypt-proxy
Backup current configuration file of dnscrypt-proxy.
sudo mv /etc/dnscrypt-proxy/dnscrypt-proxy.toml /etc/dnscrypt-proxy/dnscrypt-proxy.toml.backup
Copy example configuration file as the main configuration file (which we will soon edit).
sudo cp /usr/share/doc/dnscrypt-proxy/examples/example-dnscrypt-proxy.toml /etc/dnscrypt-proxy/dnscrypt-proxy.toml
Now open /etc/dnscrypt-proxy/dnscrypt-proxy.toml in your favorite editor. Find the options in the file and modify their values to match what's given below.
# Empty listen_addresses to use systemd socket activation
listen_addresses = []
# Use servers implementing the DNSCrypt protocol
dnscrypt_servers = true
# Use servers implementing the DNS-over-HTTPS protocol
doh_servers = true
# Server must support DNS security extensions (DNSSEC)
require_dnssec = true
# Server must not log user queries (declarative)
require_nolog = true
# Server must not enforce its own blacklist (for parental control, ads blocking...)
require_nofilter = true
# DNSCrypt: Create a new, unique key for every single DNS query
# This may improve privacy but can also have a significant impact on CPU usage
# Only enable if you don't have a lot of network load
dnscrypt_ephemeral_keys = true
# DoH: Disable TLS session tickets - increases privacy but also latency
tls_disable_session_tickets = true
Notice that nowhere are we specifying any particular DNS server. Instead, we're telling DNSCrypt-proxy to select the fastest DNS server which matches all the criteria we have given.
We also need to change systemd configuration file to change the port dnscrypt-proxy.socket service listens on.
sudo systemctl edit --full dnscrypt-proxy.socket
Issue the command given above and change the port from 53 to 5353 as given below.
ListenStream=127.0.2.1:5353
ListenDatagram=127.0.2.1:5353
Restart dnscrypt-proxy.
sudo systemctl restart dnscrypt-proxy.socket
sudo systemctl restart dnscrypt-proxy
And, reboot.
sudo reboot
Then, check that dnscrypt-proxy is resolving domains.
dig +short example.com @127.0.2.1 -p 5353
Set Up Pi-hole
Download Pi-hole installation script and run it.
wget -O pihole.sh https://install.pi-hole.net
bash pihole.sh
When the Pi-hole installation starts :
- Select
Customas your upstream DNS provider and enter127.0.2.1#5353(IP address and port of dnscrypt-proxy socket) - Keep all the third party blocklists
- Keep both ipv4 and ipv6 protocols.
- Select 'Yes' when asked if you want to use your current network settings.
- Select
Onwhen asked to install web admin interface - Select
Onwhen asked to install lighttpd web server - Select
Onwhen asked to log queries - Select
0 show everythingfor the privacy mode for FTL - Note the IP address and the password of the Pi-hole web admin interface
Let's do a quick check to see if Pi-hole is actually blocking malicious domains. If it is, you should get 0.0.0.0 as output. By the way, pixel.facebook.com is a domain used by Facebook to track users across the internet.
dig +short pixel.facebook.com
Pi-hole will update your blocklists every week automatically. To update Pi-hole itself, issue following command regularly.
pihole -up
Set Up WireGuard Using PiVPN
Download PiVPN install script and install PiVPN.
curl -L https://install.pivpn.io > pivpn.sh
sudo bash pivpn.sh
When the PiVPN installation starts :
- Select the current user that will hold VPN configurations
- Select WireGuard as the VPN when asked to choose between WireGuard and OpenVPN
- Let PiVPN install WireGuard
- Enter port for WireGuard to listen on :
51820 - Select 'Yes' when asked 'We have detected a Pi-hole installation, do you want to use it as the DNS server for the VPN, so you get ad blocking on the go?'
- Select
Use this public IPoption onWill clients use a public IP or DNS name to connect to your server?screen - Now server keys will be generated by PiVPN
- Enable unattended-upgrades
- Choose to reboot
If you're doing this on a VPS, go to your VPS provider's dashboard and allow UDP traffic on port 51820 to come in. PiVPN adds UFW rule to allow incoming VPN traffic. You can verify it by running sudo ufw status verbose.
Restrict Pi-hole web admin interface
Currently, Pi-hole's web admin is listening on 0.0.0.0. But it would be much better for security if we restrict it to be accessed only from WireGuard clients. Open /etc/lighttpd/lighttpd.conf file and add the following line below server.port = 80 line :
server.bind = "10.6.0.1"
Restart Lighttpd web server.
sudo systemctl restart lighttpd
UFW Firewall Rules
To allow DNS queries from WireGuard clients in 10.6.0.0/24 subnet to reach Pi-hole listening on port 53, add these UFW rules.
sudo ufw allow proto tcp from 10.6.0.0/24 to 10.6.0.1 port 53 comment 'wg-pihole-dns-tcp'
sudo ufw allow proto udp from 10.6.0.0/24 to 10.6.0.1 port 53 comment 'wg-pihole-dns-udp'
To allow HTTP requests from WireGuard clients in 10.6.0.0/24 subnet to reach Lighttpd server listening on port 80 for accessing Pi-hole web admin, add the following UFW rule.
sudo ufw allow proto tcp from 10.6.0.0/24 to 10.6.0.1 port 80 comment 'wg-pihole-admin-http-tcp'
Configure WireGuard Clients
Time to add your devices as clients. But, first, install official WireGuard applications suitable for your devices.
Then on the server, run this command to add a new client and give it a unique name :
pivpn add
Mobile Phone As A Client
If you're adding an Android or iOS device as a client, execute pivpn qrcode on the server and select your client name. A QR code of your client WireGuard configuration will be displayed on the terminal. Open the app on the phone, tap on the + button and then on the Scan from QR code option to scan the QR code. Give it a name and import the configuration. Enable the configuration to establish an encrypted tunnel between your device and the server. Also go to Settings -> Network & Internet -> VPN -> turn on 'Always on VPS'.
Ubuntu As A Client
If you're adding a computer running Ubuntu 20.04 or one of its derivative OSes, do the following.
Open ~/configs/clientname.conf on the server and copy the contents of the file. Now, create clientname.conf on your computer and paste everything in it and save the file. Then open terminal in the same directory and execute following command to import the WireGuard connection.
nmcli connection import type wireguard file clientname.conf
Open your network connection settings. Go to IPv4 tab, select 'Automatic (DHCP) addresses only' as Method and add 10.6.0.1 as the only DNS server and click save. Reboot or disable networking then enable it. You will need to disable 'Secure DNS' in Chrome and 'DNS over HTTPS' in Firefox otherwise the queries in those browsers will circumvent Pi-hole.
Then, open a terminal window on your local computer and check if the VPN is active and reachable.
ping -c 3 10.6.0.1
Then, check if Pi-hole is working. As mentioned before, you should get 0.0.0.0 in response.
dig +short pixel.facebook.com
Now, go to http://pi.hole/admin/ or http://10.6.0.1/admin/ and login using the password you noted above. Here you can customize your Pi-hole. You can also add ready-made blocklists created by volunteers. You can also whitelist and blacklist domains.

Add Clients To hosts File
By default, Pi-hole shows IP addresses of client devices like 10.6.0.2, 10.6.0.3 in its web admin interface. But it would be better if we could show the device names instead.
To see all the clients and some details about their connections, run pivpn clients command on the server. Focus on Name and Virtual IP columns in the output. Here's my sample output.
Name Remote IP Virtual IP Bytes Received Bytes Sent Last Seen
desktop (none) 10.6.0.2 0B 0B (not yet)
laptop (none) 10.6.0.3 0B 0B (not yet)
phone (none) 10.6.0.4 0B 0B (not yet)
We will now add the same name and virtual IP mappings in /etc/hosts file to make Pi-hole use them. Open /etc/hosts file and at the end of the file, on a new line, add the IP address then a single whitespace then the name of the client. Make sure there's no whitespace in the client name. Add all the clients this way. Here's what I would append to my /etc/hosts file.
10.6.0.2 desktop
10.6.0.3 laptop
10.6.0.4 phone
You may have to reboot the server to make Pi-hole pick up the mappings.
sudo reboot
Full-tunnel VPN vs Split-tunnel VPN
There are two ways you can use this setup.
- Full-tunnel VPN (default mode)
- Split-tunnel VPN for DNS
Full-tunnel VPN
All the traffic is tunneled through the VPN. Your IP address will change to that of the server. This is the default mode.
Split-tunnel VPN for DNS
Only DNS queries will be tunneled through the VPN and other traffic will go through your ISP as before. Your IP address won't change. But, you will still get the benefit of ad blocking since your DNS traffic will go through pi-hole.
Why use split tunnel for DNS when you're using a VPS?
If you're not using a popular VPS provider, you're probably fine and you can skip this section. By default, it's a full VPN. But, if you're hosting the VPN on a popular VPS provider like AWS, Linode, OVH, etc. then it would be much better if you use a split-tunnel VPN for DNS.
Here are the reasons why :
- A lot of websites and apps won't be accessible if you use full-tunnel VPN because many websites and apps block requests coming from popular VPS providers like AWS, Linode, OVH, etc thinking it's bot traffic.
- VPS providers offer limited bandwidth (depends on the provider. typically ranges from 100GB to 2TB per month) which can be a limiting factor if you have many clients with high internet usage and if you go over the bandwidth limit, you gotta pay more.
- IP address of the VPS is static and therefore easier to track but IP address given to a client by ISP changes regularly.
For configuring WireGuard client as split-tunnel VPN for DNS :
Split-tunnel VPN for DNS on Phone
In your client configuration on your phone, change AllowedIPs to 10.6.0.1/32. Then, stop and start the VPN tunnel.
Split-tunnel VPN for DNS on Ubuntu
Open ~/configs/clientname.conf on the server and copy the contents of the file. Now, create clientname.conf on your computer and paste everything in it. Change AllowedIPs to 10.6.0.1/32 and save the file. Then open terminal in the same directory and execute following commands to delete the old, existing configuration and import the new configuration.
nmcli connection delete clientname
nmcli connection import type wireguard file clientname.conf
Open your network connection settings. Go to IPv4 tab, select 'Automatic (DHCP) addresses only' as Method and add 10.6.0.1 as the only DNS server and click save. Reboot or disable networking then enable it.
Set Up Searx
Clone Searx code repository, go into searx directory and install it.
git clone https://github.com/asciimoo/searx searx
cd searx
sudo -H ./utils/searx.sh install all
While the installation is going on,
- When asked to install necessary packages, say yes by typing
Y - Let the script install Searx
- When asked to install UWSGI, say yes by typing
Y - When asked if you want to inspect the installation, say yes by typing
Y - When asked to enable Searx debug mode, select no by typing
N - Type
Ctrl-Cwhen the log monitoring starts, it means the installation is done.
In /etc/searx/settings.yml, you can enable autocomplete using one of many available backends like qwant, duckduckgo, startpage, etc. For example, if you wish to use duckduckgo to autocomplete your search queries, make the autocomplete line in search section look like autocomplete : "duckduckgo".
Restrict access to Searx by making it available only to WireGuard clients. Open UWSGI configuration of Searx located at /etc/uwsgi/apps-available/searx.ini and modify http option as follows.
http = 10.6.0.1:8888
Restart UWSGI.
sudo systemctl restart uwsgi
Add a firewall rule to allow VPN clients to reach Searx.
sudo ufw allow proto tcp from 10.6.0.0/24 to 10.6.0.1 port 8888 comment 'wg-port-8888-searx-tcp'
Now from you client devices, access http://10.6.0.1:8888 and enjoy ad-free and private searx, i mean search, experience.

To update your Searx instance, first cd into the directory where you git-cloned Searx repository and then run update script.
cd searx
sudo -H ./utils/searx.sh update searx