For private use I only want to use a simple peer-to-peer openVPN connection without maintaining a complete Public Key Infrastructure. I have heard it can be simplified by using static keys, but how can I set this up using Raspberry Pi?


You can generate static secret keys and just preshare them to the devices using openVPN. Then this keys are used for authentication. There is no need to generate private/public keys and maintain them in an infrastructure with a certification authority.

For reference I use Raspbian Stretch Lite 2019-04-08 updated with sudo update && sudo full-upgrade && sudo reboot on 2019-05-01.

Example for this setup:

            /             vpn tunnel               ┌──────────┐     \
     peer1 ╔════════════════╗  ╔=═══════════════════════════════════ peer2
   RPi(eth0) <-----------> router <------------->  │ INTERNET │
            \    wired    /      \      wan        │          │   └──────────┘
                                   (public ip)

I assume you have a working internet connection.
On the openvpn peer1 install openvpn:

rpi ~$ sudo -Es
rpi ~# apt update
rpi ~# apt full-upgrade
rpi ~# apt install openvpn
rpi ~# systemctl disable --now openvpn.service

If you use systemd-networkd then install also

rpi ~# apt install openvpn-systemd-resolved

Then generate a static secret key:

rpi ~# openvpn --genkey --secret /etc/openvpn/static.key

Create a peer1 config file:

rpi ~# cat > /etc/openvpn/peer1.conf <<EOF
dev tun
secret static.key
cipher AES-256-CBC

Start the openvpn peer1:

rpi ~# systemctl enable --now openvpn@peer1.service
rpi ~# exit
rpi ~$

On the openvpn peer2 also install openvpn as shown above with that 5 or 6 commands. Don't generate a new static key, instead copy that one you have made on the openvpn peer1 to /etc/openvpn/ with same permission (sudo chmod 600 /etc/openvpn/static.key). Create a peer2 config file:

mngmt ~# cat > /etc/openvpn/peer2.conf <<EOF
dev tun
secret static.key
cipher AES-256-CBC

This config file is made to test the vpn tunnel on your local network first. Now start the peer2 with:

mngmt ~# exit
mngmt ~$ sudo systemctl start openvpn@peer2.service

Now you should be able to ping the peer1:

mngmt ~$ ping

If it works we can test to connect from the internet. To be sure not conflicting with local setup we have to use a complete different path to connect to the internet. For this I use my android cell phone with USB tethering to the management computer where I have disabled wifi on the phone to be sure only using 4G data uplink. I also disabled wifi on the management computer and pulled out its ethernet cord. The default port of openvpn is 1194 so you have to forward this port on your router to the local openvpn peer1 port 1194 ( It is important to use protocol udp not tcp. Look at the router what it's current public ip address is, in my example Then change the line remote in /etc/openvpn/peer2.conf to remote and

Then enable USB tethering on your mobile phone and start peer2. You can check if port forwarding is set on the remote router with:

mngmt ~$ sudo nmap -Pn -sU -p1194
Starting Nmap 7.70 ( https://nmap.org ) at 2019-08-03 16:20 BST
Nmap scan report for p57A8602E.dip0.t-ipconnect.de (
Host is up.

1194/udp open|filtered openvpn

Nmap done: 1 IP address (1 host up) scanned in 2.31 seconds

This only checks if the port forwarding is active on the router. It does not check if the openvpn peer behind is active. This you can finally check with ping:

mngmt ~$ sudo systemctl start openvpn@peer2.service
mngmt ~$ ping
PING ( 56(84) bytes of data.
64 bytes from icmp_seq=1 ttl=64 time=743 ms
64 bytes from icmp_seq=2 ttl=64 time=504 ms
64 bytes from icmp_seq=3 ttl=64 time=403 ms

If this works then you have a running vpn tunnel through the internet to your local RasPi. If you want to connect in the other direction then just comment remote <ip address> in /etc/openvpn/peer2.conf and set it in /etc/openvpn/peer1.conf.

With this simple setup with a pre shared secret key you can ensure that the environment is working (port forwarding, routing etc.). Now you can improve the setup of openvpn step by step with all its nice features like TLS public key authentication, connecting whole subnets, not only one RasPi, using tap interfaces instead of tun interfaces to remotely play games that need broadcasts and so on. But this isn't subject of this site.

[1] openvpn - Static Key Mini-HOWTO
[2] man openvpn


For peer-to-peer with static keys I would use tinc-vpn available as the apt package tinc.

It is simpler to manage than OVPN and much lighter, It is peer-to-peer with automatic routing and network forwarding and highly configurable.

It is flexible enough to be run out of a docker image for containerized VPN configurations (just mount the private key!)

  1. apt-get install tinc
  2. Follow the instructions to generate and share the keys

There are many tutorials on-line for this, and tinc supports many systems with the same configuration, for the official examples/tutorials that demonstrate the flexibility look at https://www.tinc-vpn.org/examples/

Mesh VPN topologies are also supported.


You need to create

  1. config file
  2. network up/down script
  3. host configuration
  4. Generate host keys

There are optional scripts that do thinks like host/segment available/unavailable (for logging or emailing for example if partner is offline)

Create the network config

myvpn is the name of the network and can be anything you like (it will become the name of the tunnel interface)

as root:

# Create the config directory

cd /etc/tinc && mkdir myvpn && cd myvpn `

# Create a config file (https://www.tinc-vpn.org/documentation/tinc.conf.5)

cat > tinc.conf <<-EOF
Name = host1 
AddressFamily = ipv4 
Interface = myvpn
ConnectTo = host2

# create a network up script

cat > tinc-up <<-EOF
ifconfig $INTERFACE netmask
EOF &&

chmod u+x tinc-up;

# create a network down script

cat > tinc-down <<-EOF 
ifconfig $INTERFACE down

chmod u+x tinc-down

# create the host config

mkdir hosts
cat > hosts/host1 <<-EOF
# External Address
Address = my.external.ip 
# VPN Network segment served
Subnet =

# Generate the public/private key pair

tincd -n myvpn -K

# this creates a private key and places the public key into hosts/host1

cat hosts/host1 

Repeat the process on host 2 and exchange the host configuration file myvpn/hosts/host1 and myvpn/hosts/host2

Note: Only 1 host needs to be publically routable

afterwards start the network on each one tincd -n myvpn

you can enable automatic start at boot by adding echo myvpn >> /etc/tinc/nets.boot

  • How is that easier to manage than openvpn? Seems like same amount of configuration and having to know things. Openvpn static key is a very simple config as well.
    – Cray
    Mar 8 at 8:28
  • @Cray, I agree, OpenVPN config is not difficult. But, the implementation is defined by a large config set, most of which is never touched by a user. In my experience, this tends to make "infrastructure as code" approach to system engineering more difficult than tools which leave fewer "black boxes". Contrast, a tinc deployment is defined by the configs in this answer, which are also scripted as heredocs. This approach, in my experience, improves the scalability of a system, and is more appropriate for applications requiring no configuration, like distributed pi clusters.
    – crasic
    Mar 9 at 1:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.