The purpose of this guide is to document the steps I take to set up Pi-Hole and PiVPN on a VPS, from companies such as LogicWeb. The ultimate goal is to have an ad-blocker that will work both on my home network and on any device connected to the VPN.
Almost every tutorial I found was focused on installing Pi-Hole and PiVPN on a local Raspberry Pi instead of on a VPS. The steps are mostly the same but there are some extra steps involved in securing the VPS to deny access from bad actors.
After completing this tutorial, you will have:
- A Pi-Hole accessible from anywhere
- A VPN that will provide an encrypted connection when using public Wi-Fi, via PiVPN
In order to follow this tutorial you will need to have a VPS with at least 512 MB of memory, although I would personally recommend at least 1 GB if you plan on having a large number of blocklists. This guide assumes that you are using Ubuntu 18.04 and Pi-Hole Version 4.2. Other distros will mostly likely work, but I have only tested the steps covered in this tutorial on Ubuntu 18.04.
Initial Server Setup
We will be using
ssh to remotely log into the VPS and configure it. If you are on a Unix-based operating system, it should already be installed. If you are Windows, you will need to install PuTTY. Make sure you know your server’s IP address and login credentials.
When you have your server’s IP address and root passphrase, log into the server as the
You will be asked to create a new passphrase. Although we will be disabling password authentication, be sure to create or generate a secure passphrase anyway.
Create new user
Grant root privileges to
usermod -aG sudo pi
Public Key Authentication
Public Key Authentication provides an alternative method of identifying yourselve to a remote server and increases the overall security of your server.
If you do not already have an SSH key, you will need to create one on your local computer
Save your key in the default file (where
$user is your user)
Enter file in which to save the key (/Users/$user/.ssh/id_rsa):
Create a secure passphrase. You will need to enter this passphrase each time you utilize your SSH key
Copy the public key from your local machine to your remote server with
- If you opted to add SSH during the server creation process anyway, this method will not work.
You should repeat these steps for each device you want to access the server, including desktops, laptops, tablets, and mobile phones.
Disable Passphrase Authentication
Once you have added SSH keys from all of your devices, we can disable passphrase authentication.
Log into your server as
root, if you are not already logged in
Open the SSH daemon configuration file
sudo nano /etc/ssh/ssdh_config
- Find the line containing
PasswordAuthenticationand uncomment it by deleting the preceeding
#. Change it’s value to no
- Find the line containing
PubkeyAuthenticationand ensure it’s value is set to yes
- Find the line containing
ChallengeResponseAuthenticationand ensure it’s value is set to no
Save your changes and close the file
CTRL + X
While still logged in as
root, open a new terminal window and test logging in as
pi and verify that the public key authentication works
(Optional) Install Mosh
Mosh, or mobile shell, is a remote terminal application that allows roaming and intermittent connectivity. It’s intended as a replacement for SSH but both can be used on the same server.
# Update your sources, if necessary sudo apt update # Install mosh sudo apt install mosh
We will set up a basic firewall,
ufw, that will restrict access to certain services on the VPS. Specifically, we want to ensure that only ports needed for SSH, Pi-Hole, and PiVPN are open. Additional ports can be opened later depending on your specific needs.
We will be opening ports for secure FTP so that
.ovpn files needed for connecting to our VPN later can be retrieved via a FTP application such as Filezilla or Transmit.
To set up
ufw, enter the following commands:
# Apply basic defaults sudo ufw default deny incoming sudo ufw default allow outgoing # Open ports for OpenSSH sudo ufw allow OpenSSH # Optionally, allow all access from your IP Address sudo ufw allow from $yourIPAddress # Open ports for secure FTP sudo ufw allow sftp # Open ports for Mosh if you installed it sudo ufw allow mosh
Now that our server has been set up and is secure, we will now install the Pi-Hole software. The installation is fairly simple and requires a small amount of configuration on our part.
Please note that on a Raspberry Pi we would be asked to set a static IP address. This is important because we do not want the IP address of a DNS server to be constantly changing. However, since we are using a VPS, the static IP address has already been set for us. The networking interface will also be automatically selected as well since only one interface,
eth0, will be available to us at the time of installation.
Run the official Pi-Hole installer:
curl -sSL https://install.pi-hole.net | bash
- When asked about which protocols to use for blocking ads, select both
IPv6, even if you cannot use
IPv6yet on your home network. The justification is that more ads are now being served via
IPv6and we want to ensure all ads are blocked
- On the very last screen, you will be presented various information about your new Pi-Hole installation. Your Pi-Hole‘s IP address should match your server’s IP address.
Once you have completed the Pi-Hole installation script, you should change the passphrase to the admin panel:
pihole -a -p myawesomepassphrase
(Optional) Configure Pi-Hole
Pi-Hole allows you to customize what websites you want to block and allows to you whitelist any false positives (e.g., unblocking Netflix or Facebook). Pi-Hole developer WaLLy3K provides a popular collection of blocklists that you can add to your own blocklists. Another blocklist collection is provided by the Block List Project.
I would also recommend checking out this GitHub repository that will load commonly whitelisted domains (e.g., Facebook, Instagram, XBox Live) into your Pi-Hole.
Finally, I would suggest following this guide from the official Pi-Hole documentation to set up unbound as your own recursive DNS server (rather than using a public DNS server such as Google DNS or Cloudflare). This will help to further increase the privacy of your DNS queries.
Installing PiVPN will be just as easy as installing Pi-Hole, although there is a bit more configuration required on our part for PiVPN. PiVPN automatically installs an OpenVPN server for us as well as any additional required software. The script will also automatically open ports in
ufw so that an OpenVPN client can communicate with our VPS.
Please note that on a Raspberry Pi, we would be asked to select a network interface, but since we are on a VPS the only available interface is
eth0 and that is automatically selected for us as well as the static IP address.
Start by running the PiVPN installer
curl -L https://install.pivpn.io | bash
- When asked to choose a local user to hold your
.ovpnconfiguration files, select the user
- When asked about enabling
UnattendedUpgrades, pick yes
- When asked to select the protocol, pick
- When asked to select the port, either accept the default
1194or enter a random port (e.g.,
- When asked to set the size of your encryption key, select
- Generating the encryption key will take a few minutes
- When asked to select a Public IP or DNS, select your server’s IP address
- When asked to select a DNS provider, select the
customoption and enter the IP address of your server
Once the installer is finished, allow it to reboot your VPS
Configure Pi-Hole and PiVPN
Now that both Pi-Hole and PiVPN are installed, there are a couple of critical steps we must take before we can start generating
.ovpn configuration files and connecting to our VPS. Specifically we want to ensure that PiVPN uses Pi-Hole as it’s DNS server and that we can connect using an OpenVPN client.
First we will create a configuration file for
dnsmasq, the DNS service that powers Pi-Hole
Log into your server as
pi if you are not logged in already:
Create a new configuration file called
sudo nano /etc/02-pivpn.conf
Add the following line to the file:
listen-address=127.0.0.1, your_server_ip, 10.8.0.1
Save and exit the file, and restart Pi-Hole‘s FTL service:
We will start by modifying the
sysctl.conf file to allow IP forwarding:
sudo nano /etc/sysctl.conf
Look for the line that contains
net.ipv4.ip_forward. If there is a
# character prepended, remove it to uncomment the line. Ensure that it is set to
1 and not
Save and close the file. Then instruct
sysctl to reload it.
sudo sysctl -p
Then we will modify
ufw to allow masquerading of client connections. Before we can modify any rules, we need to find the public network interface of our VPS:
ip route | grep default
Your public interface will follow the word “
dev” in the output. For example:
default via 203.0.113.1 dev eth0 proto static metric 600
If your public interface is not
eth0, make note of what it is. We will be using that interface to modify a
ufw file that loads rules before regular rules are loaded. We will be adding a rule that will masquerade any traffic comming in from the VPN.
sudo nano /etc/ufw/before.rules
Towards the top of
before.rules add the following text, starting with
# START OPENVPN RULES:
# # rules.before # # Rules that should be run before the ufw command line added rules. Custom # rules should be added to one of these chains: # ufw-before-input # ufw-before-output # ufw-before-forward # # START OPENVPN RULES # NAT table rules *nat :POSTROUTING ACCEPT [0:0] # Allow traffic from OpenVPN client to eth0 (change to the interface you discovered!) -A POSTROUTING -s 10.8.0.0/8 -o eth0 -j MASQUERADE COMMIT # END OPENVPN RULES
Save and close the
Finally, we need to tell
ufw to allow forward packets by default. Open the
sudo nano /etc/default/ufw
Fine the line containing
DEFAULT_FORWARD_POLICY. Change the value to
ACCEPT if necessary:
Save and close
Enter the following commands to restart
ufw and OpenVPN:
# Restart ufw sudo ufw disable sudo ufw enable # Restart OpenVPN sudo service openvpn reload
The following section is optional and requires you to have your own domain name, but it will configure your Pi-Hole’s web interface to use
https courtesy of Let’s Encrypt and Certbot. It can be considered overkill just for Pi-Hole, but it certainly doesn’t hurt. First we will acquire the certificate and then we will configure
lighttdp to automatically redirect any
http requests to
https. The steps are based on this reddit post.
Acquiring the certificate
- Log into your remote server again either as
rootor with root privileges.
- Go to this Certbot page (for Ubuntu 18.04) and following the Install commands to install Certbot on your server.
- Perform a dry run to acquire a certificate for your domain. For example: certbot certonly –webroot -w /var/www/html -d example.com –dry-run
- If acquiring the certificate was successful, run the same command again without
--dry-run. For example: certbot certonly –webroot -w /var/www/html -d example.com
- Edit the file
example.comwith your own domain name: ssl.pemfile = “/etc/letsencrypt/live/example.com/combined.pem” ssl.ca-file = “/etc/letsencrypt/live/example.com/chain.pem”
- Run the following commands, replacing
example.comwith your domain name: ln -s /etc/lighttpd/conf-available/10-ssl.conf /etc/lighttpd/conf-enabled/10-ssl.conf cd /etc/letsencrypt/live/example.com/ cat privkey.pem cert.pem > combined.pem
lighttpd: sudo systemctl restart lighttpd
httpsshould now be enabled on your web interface.
- Add a cron job to automatically renew the certificate every 90 days. Open
/etc/crontaband add the following line: 47 5 * * * root certbot renew –quiet –no-self-upgrade –renew-hook “cat $RENEWED_LINEAGE/privkey.pem $RENEWED_LINEAGE/cert.pem > $RENEWED_LINEAGE/combined.pem;systemctl reload-or-try-restart lighttpd”
Configure the Redirect
lighttpd configuration file,
/etc/lighttpd/lighttpd.conf, and add the following block of code:
sudo systemctl restart lighttpd
Your web interface should now automatically redirect any
http requests to