How to set up own VPN and DNS server with Ubuntu 18.04 on DigitalOcean

Photo by Thomas Jensen on Unsplash

Hello everyone. In this article, I will explain how to set up your private, personal VPN and DNS server in several clicks with Ubuntu 18.04 on DigitalOcean. This tutorial is a beginner-friendly. I will describe each step in details.

Things to be used:
- OpenVPN;
- PiHole + Unbound + Stubby;
- Ubuntu 18.04 VPS on DigitalOcean or any other VPS with this tutorial (Hetzner one is really cheap, €2.49 per month).

- you have spare 5$ per month to pay for a droplet;
- you have Linux-flavoured system.

When you registered with DigitalOcean you may create a project called VPN or just navigate to your user name in the left sidebar to create the first droplet. Pick the closest location in another country and your VPN will be working fast. I’m picking Amsterdam here since I live in London. See:

Step 1

When you went through all steps on the above video to continue it will be needed to open Linux terminal. It’s possible to do so utilizing a keyboard shortcut or just press and enter in a search bar. When you will have opened terminal at hand, create an ssh key pair as shown below and copy it as well because it will be needed in the next step. Worth to mention, it’s a good rule to set a passphrase so if someone stole your laptop a thief won’t be able to use SSH credentials to navigate remote machines that you have but I omit it for the sake of simplicity here.

Step 2

Assuming you have a public key copied in a buffer from step 2, follow this instruction:

Step 3

It creates a VPS with an address equals to but you need to wait for about 1–2 minutes before copying . It’s a configuration to be used by OpenVPN client on your laptop/PC.

By default, Ubuntu doesn’t have some required libraries so we need to install them. Copy and paste commands one by one to achieve a desirable result but replace the last one with a real IP address that you got when created the first droplet on DigitalOcean.

$ cd # navigates your home directory
$ sudo apt install -y openvpn network-manager-openvpn network-manager-openvpn-gnome network-manager-vpnc resolvconf # install required libs
$ sudo systemctl start openvpn resolvconf # launch OpenVPN and resolvconf
$ sudo systemctl status resolvconf.service openvpn.service # should be green
scp root@ip_address_that_you_got_on_step_3:/root/client.ovpn . # copies client.ovpn from a DigitalOcean VPN to a local one
$ cat >> client.ovpn <<EOF
script-security 2
up /etc/openvpn/update-resolv-conf
down /etc/openvpn/update-resolv-conf
# add a DNS server to /etc/resolv.conf on the start of a VPN server and delete it on the exit
Step 4

Now you need to import in network manager and specify DNS address or invoke it from a terminal via CLI. If you decide to go along with the second option here’s list of commands to be invoked:

$ sudo openvpn client.ovpn # run OpenVPN and create tun0 with IP

Open a new terminal window and enter this too:

$ sudo systemctl stop systemd-resolved # I'll explain later why do we need it, don't close your VPN connection if you picked method of connection

If you decide to go with GUI then you don’t need to stop systemd-resolv.service. See all instructions below:

Step 5

We’ve done a basic setup and in this state, VPN & DNS will work, moreover, you can have more than one config. User one config per active device, so if you are going to use VPN + DNS from your PC and phone at the same time you automatically need two configs. To create more configs, navigate your remote machine. See:

$ ssh root@ip_address_that_you_got_on_step_3 # navigate your remote machine
$ /root/ client_name # create a new config, dont' specify the .ovpn extension
$ exit # close SSH connection
$ cd # navigate home directory
$ scp root@ip_address_that_you_got_on_step_3:~/client_name.ovpn . # copy a new created config from remote machine to local one
$ cat >> client_name.ovpn <<EOF
script-security 2
up /etc/openvpn/update-resolv-conf
down /etc/openvpn/update-resolv-conf
# add a DNS server to /etc/resolv.conf on the start of a VPN server and delete it on the exit. N.B. repeat with each script if you're going to use it through openvpn CLI.

So what a deal with stopping of systemd-resolv? In nutshell, it does DNS leak.
Why? Entrypoint here is /etc/resolv.conf (symlink of /run/resolvconf/resolv.conf) and it has two nameservers so DNS query may go both (your VPN + DNS server) or (systemd-resolved stub server) that may have other nameservers. That means ISP can see your DNS queries if your /run/systemd/resolve/resolv.conf has its DNS nameservers. We definitely don’t want it. Frankly speaking, I don’t really like this solution but I didn’t find a better solution to it yet.

systemd-resolv service does what we don’t want

But, in comparison, Network Manager does its job well. It produces a correct picture.

resolvers configs when client.ovpn used in Network Manager

You can validate that you don’t have DNS leak using such approach. Be connected to your VPN + DNS server, open two terminal windows and type in them the following commands. For the second one, it’s an interface that you’re using to connect to the internet (usually wlo1 for wireless or eth0 for wired but may differ for your situation, see):

$ sudo tcpdump -n -i tun0 udp port 53 # in the first terminal window
$ sudo tcpdump -n -i wlo1/eth0 udp port 53 # in the second terminal window, pick one

Open your browser and do some surfing activity, you shouldn’t see any activity for command if you see then you have a DNS leak and you did something wrong during this setup. All activity should happen for command, you should see something like this:

example of tcpdump command for tun0 interface
example of tcpdump command for wlo1 interface

If you see IPs of other ranges than then you have a DNS leak and you did something wrong during the setup. Please, revisit carefully everything and try again.

P.S. restart systemd-resolv when you disconnected from your VPN + DNS server because you won’t have the internet connection. If you want to automate this process, see at the bottom update.

$ sudo systemctl restart systemd-resolv.service

Step 6. If you want better security then do the following as well from a terminal on your local machine:

$ ssh root@ip_address_that_you_got_on_step_3 # navigate your remote machine
$ curl -O && chmod +x && bash # install stubby, unbound, setup their configs, configure a sudo user, disable root ssh login and sync knows_hosts from root to your user on your remote machine

Don’t forget to write down a password created by a script during its runtime!

One more trick. Navigate , go to Settings > DNS, tick and specify Custom 1 (IPv4), Custom 2(IPv4), untick upstream DNS resolvers, tick Listen on all interfaces. See:

Step 7

This will provide you with a half DNS queries “DNS over TLS” what more secure and a half of to authoritative DNS servers what is more private.
That’s it. Now you have protected VPN & DNS.

P.S. to navigate your machine use now .

UPDATE: to start/stop automatically systemd-resolved.service you may place the following script to any $PATH location.

It also requires to modify your /etc/openvpn/update-resolv-conf . Use such commands:

$ sudo curl -O && chmod +x && bash
cat >> /etc/openvpn/update-resolv-conf <<EOF
bash /usr/local/bin/




Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store