Installing Pi-Hole and PiVPN on a VPS


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.

root Login

When you have your server’s IP address and root passphrase, log into the server as the root user

ssh root@your_server_ip

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 pi

adduser pi

Grant root privileges to pi

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 ssh-copy-id

ssh-copy-id pi@your_server_ip
  • 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

ssh root@your_server_ip

Open the SSH daemon configuration file

sudo nano /etc/ssh/ssdh_config
  • Find the line containing PasswordAuthentication and uncomment it by deleting the preceeding #. Change it’s value to no
  • Find the line containing PubkeyAuthentication and ensure it’s value is set to yes
  • Find the line containing ChallengeResponseAuthentication and ensure it’s value is set to no

Save your changes and close the file

  • CTRL + X
  • Y

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

ssh pi@your_server_ip

(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

Set up ufw

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

Install Pi-Hole

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 | bash
  • When asked about which protocols to use for blocking ads, select both IPv4 and IPv6, even if you cannot use IPv6 yet on your home network. The justification is that more ads are now being served via IPv6 and 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.

Install PiVPN

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 | bash
  • When asked to choose a local user to hold your .ovpn configuration files, select the user pi
  • When asked about enabling UnattendedUpgrades, pick yes
  • When asked to select the protocol, pick UDP
  • When asked to select the port, either accept the default 1194 or enter a random port (e.g., 11948)
  • When asked to set the size of your encryption key, select 2048
    • 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 custom option 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:

ssh pi@your_server_ip

Create a new configuration file called 02-pivpn.conf:

sudo nano /etc/02-pivpn.conf

Add the following line to the file:

listen-address=, your_server_ip,

Save and exit the file, and restart Pi-Hole‘s FTL service:

pihole restartdns

Network Adjustments

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 0.


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 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.

Open the before.rules file:

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

# NAT table rules
# Allow traffic from OpenVPN client to eth0 (change to the interface you discovered!)

Save and close the before.rules.

Finally, we need to tell ufw to allow forward packets by default. Open the /etc/default/ufw file:

sudo nano /etc/default/ufw

Fine the line containing DEFAULT_FORWARD_POLICY. Change the value to ACCEPT if necessary:


Save and close /etc/default/ufw.

Enter the following commands to restart ufw and OpenVPN:

# Restart ufw
sudo ufw disable
sudo ufw enable

# Restart OpenVPN
sudo service openvpn reload

Let’s Encrypt

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

  1. Log into your remote server again either as root or with root privileges.
  2. Go to this Certbot page (for Ubuntu 18.04) and following the Install commands to install Certbot on your server.
  3. Perform a dry run to acquire a certificate for your domain. For example: certbot certonly –webroot -w /var/www/html -d –dry-run
  4. If acquiring the certificate was successful, run the same command again without --dry-run. For example: certbot certonly –webroot -w /var/www/html -d
  5. Edit the file /etc/lighttpd/conf-available/10-ssl.conf. Replace with your own domain name: ssl.pemfile = “/etc/letsencrypt/live/” = “/etc/letsencrypt/live/”
  6. Run the following commands, replacing with your domain name: ln -s /etc/lighttpd/conf-available/10-ssl.conf /etc/lighttpd/conf-enabled/10-ssl.conf cd /etc/letsencrypt/live/ cat privkey.pem cert.pem > combined.pem
  7. Restart lighttpd: sudo systemctl restart lighttpd https should now be enabled on your web interface.
  8. Add a cron job to automatically renew the certificate every 90 days. Open /etc/crontab and 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

Open the lighttpd configuration file, /etc/lighttpd/lighttpd.conf, and add the following block of code:

compress.cache-dir = "/var/cache/lighttpd/compress/"
compress.filetype = ( "application/javascript", "text/css", "text/html", "text/plain" )

# [add after the syntax above]

# Redirect HTTP to HTTPS
$HTTP["scheme"] == "http" {
    $HTTP["host"] =~ ".*" {
        url.redirect = (".*" => "https://%0$0")

Restart lighttpd again:

sudo systemctl restart lighttpd

Your web interface should now automatically redirect any http requests to https.


Tags: , , , , , ,