How to set up a peer-to-peer fully-meshed VPN between servers

Matteo Contrini
Oct 21, 2017 · 5 min read
Image for post
Image for post

If you have deployed multiple servers on DigitalOcean or Vultr, you probably know that every server can be provided with a private IP address that is associated with the private network of the datacenter. This is great, because it allows to deploy distributed services (like databases) that communicate with each other with a latency that is close to zero.

The “issue” is that the private network is shared: that means that in the same subnet there are hundreds and possibly thousands of other servers. So at some point you’ll want to set up a Virtual Private Network (VPN) between your servers, so that they’ll be able to contact each other with custom private IP addresses (like 10.0.1.x) through an encrypted network.

Why you should use

One of the most common solutions for setting up a VPN is OpenVPN, but that’s not what I’m going to cover today. At BotFactory we have more than 10 servers hosted on DigitalOcean, and we wanted a relatively easy way to encrypt the traffic between our servers without spending hours and hours configuring the network. We wanted something that automatically discovers new and removed servers. That’s why we’ve chosen Here are some of the highlights of :

  • it allows to bind the VPN to a custom interface, so you can create a network like with all your hosts
  • it creates a fully-meshed peer-to-peer VPN network. That means that each of the hosts needs the IP address of just one another server (inside the VPN) to automatically discover all the nodes
  • the tunnel is of course encrypted, and AES-256 is supported
  • automatic reconnection when a connection is lost or a peer becomes temporarily unavailable
  • great performance
  • a pre-built .deb package is available
  • it’s open source
  • it’s incredibly easy to configure, you’ll be surprised
  • it’s stable. We’ve been running on 10 servers for almost a year, and we never had a crash or any issue. It just works.

I’m now going to explain how to configure a basic mesh P2P VPN network with .

Let’s go

First, you’ll need to install on each of the servers you’ll want to include in the virtual network.

I’m assuming the servers are running Ubuntu, but the following steps should be the same for all the Debian-based Linux distributions. If you use a different type of Linux distribution, follow the steps in the readme for compiling.

Run these commands:

sudo dpkg -i vpncloud_0.8.1_amd64.deb

A systemd service will be automatically created, together with a sample configuration file.


The configuration file looks like this and is placed in .

The first thing you should do is to create a copy of the sample file.

sudo cp /etc/vpncloud/ /etc/vpncloud/
sudo nano /etc/vpncloud/

Now let’s take a look at the content of the configuration file. The changes I’m describing here should be made on all the servers where you installed the VPN.

As I previously said, creates a full mesh, but to do so it doesn’t need the IP addresses of all the servers. In my configuration, I have configured 3 peers, which are the IP addresses of the 3 database servers (they should never be offline at the same time). Every VPN instance is able to use those peers to create connections with all the other hosts in the virtual network.


The following is a sample configuration for .

- privateip1:3210
- privateip2:3210

Although it is possible to put the public IP addresses of the servers in the configuration, it is recommended that you use the internal private network IP addresses (provided by DigitalOcean for eth1, for example), for better performance and lower latency. You can also use hostnames, or even put “fake” hostnames that you can then map with the private IPs in the file.

Also, make sure that the port 3210 is open on the internal network on all the servers. If you need a simple firewall that allows you to open a port only on one interface, take a look at FireHOL. It’s great!

Shared key

Next, here’s a sample configuration for the option. The same key should be set on all the servers.

shared_key: "nT4gAGSP!S9!2Rjb9%h*gdVN*8NszP"

(*don’t use this key*)

This is the key that will be used to derive the symmetric key to be used with AES-256 for encrypting the VPN traffic. The advantage of using a shared key is that you don’t need to transfer complex keys between servers. It’s like a password, and it should be a string of ASCII characters. In my case, the shared key is a random string of 30 ASCII characters.


By default, will use ChaCha20 as the encryption algorithm. On DigitalOcean servers, AES-256 is supported by the CPUs so you can set the encryption/decryption algorithm to AES-256 for better performance.

crypto: aes256

Virtual interface

Last, you should specify the and options, which specify the commands that should be run by when bringing up (and down) the network interface for the VPN.

ifup: "ifconfig $IFNAME mtu 1400"
ifdown: "ifconfig $IFNAME down"

According to the IP address (in CIDR notation) that you specify in the command, the operating system will automatically understand that you’re going to create a network whose hosts are, in this case, in the subnet .

In my case I “grouped” the servers by changing the third octet of the address, to achieve a configuration like this:

  • reserved to database servers
  • reserved to web servers

Run boy run

After applying the above configuration on all the servers, you can start the VPN service.

sudo service vpncloud@myvpn start

where should be replaced with the name of the configuration file (excluding the extension).

Check for errors with and then try to ping all the servers: on each server, ping , , etc., depending on you configuration. They should all work with a latency close to zero.

Finally, don’t forget to enable the service, so that it automatically runs at system startup.

sudo systemctl enable vpncloud@myvpn

Naming hosts

To make the hosts of the VPN more easily recognizable, you can map the IP addresses to hostnames, adding something like the following lines to the file on every server. db1.vpn db2.vpn

Wasn’t that easy?

If you found this article useful, please clap and share :)

If you liked as I did, star the repository on GitHub, and if you are able to find a bug, contribute by creating an issue or by sending a pull request (you’ll need to understand a bit of the Rust language to do that. I don’t).

Also, take a look at the wiki on GitHub if you’re interested in learning other aspects of the VPN.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

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