Hint: OpenVPN with a static key has no Perfect Forward Secrecy (PFS)!


mode p2p

remote <remote>
local <local>

proto <proto>

rport <rport>
lport <lport>

dev-type tun
dev <interface>

script-security 1
cipher aes-256-cbc

resolv-retry infinite


ifconfig-ipv6 <IPv6> fe80::1000

secret <secret>

Replace <remote> with the IP address of the peer and <local> with your IP address. Replace <proto> with udp4 for a connection over IPv4 or with udp6 for a connection over IPv6. Choose a port for <lport> and set <rport> to the port of your peer. <lport> on udp must be opened accordingly in the local firewall. Replace <interface> with the appropriate interface name for your peer. Replace <IPv6> with your link-local IPv6. The specification of a second link-local address is only necessary for certain functions of OpenVPN, but the specification is mandatory. Therefore the address fe80::1000 is used here. Replace <secret> with the path to the Secret Static Key.

Generate a Secret Static Key:

openvpn --genkey secret <filename>.key

Speed optimization

To increase the speed of OpenVPN, you can use the fast-io flag. To use the flag, OpenVPN must run over UDP and on a non-Windows system. This option is experimental.

A peer is not publicly reachable

It can happen that a peer is not always publicly reachable on the same IP address, for example, due to a NAT. In this case, it is sufficient if one of the peers is publicly reachable. The configuration files are adjusted accordingly.

On the public reachable peer remote, local and rport are removed. The parameter lport remains and must be publicly reachable via UDP. Furthermore the parameter float and keepalive is added.

lport <lport>
keepalive <interval> <timeout>

On the non-public peer, only lport and local are removed and the keepalive parameter is added.

remote <remote>
rport <rport>
keepalive <interval> <timeout>

For the keepalive signal, <interval> is replaced with an interval in miliseconds in which ping-like signals are sent to keep the connection alive. <timeout> specifies the number of miliseconds since the last received ping-type signal after which the peer is considered down. For example, if a keepalive signal is to be sent every 20ms and the peer is to be considered down after 2 minutes, the following configuration can be used:

keepalive 20 120


OpenVPN uses an MTU of 1500 in the tunnel by default. The problem with this, however, is that most Internet connections also have an MTU of 1500. However, since the OpenVPN packets are both encapsulated and encrypted, the MTU must be lower than that of the Internet interface. If an MTU of 1500 is used in the tunnel, this will cause the OpenVPN packets to be larger than 1500 bytes, which is the MTU of the Internet interface. This leads to IP packet fragmentation. Fragmentation is again something that you generally want to prevent. Therefore it is necessary to adjust the MTU in OpenVPN manually.

OpenVPN has provided two flags for this purpose. However, one flag is deprecated since version 2.6 and should therefore no longer be used. The other flag is tun-mtu. With this flag you can adjust the MTU of the tunnel interface. However, this must be determined beforehand. For this you can send pings with different sized payloads in the tunnel and see if the OpenVPN packet gets fragmented.

To intercept fragmented IP packets with tcpdump, you can use the following command (only works for IPv4):

tcpdump -i <interface> '((ip[6:2] > 0) and (not ip[6] = 64))'

You should replace <interface> with the name of the Internet interface. If you cannot detect fragmented packets via this command, you can modify tcpdump to filter every packet with the OpenVPN port. Use the flag -v. You will then recognize the fragmented packets by a [+] or the word frag:

tcpdump -i <interface> port <rport> or port <lport> -v

After that you can use the following command to determine the MTU.

ping <ip> -M do -s <payload>

Replace <ip> with the IP of the peer in the tunnel. -M do ensures that the ping packets are not fragmented. Replace <payload> with the size of the payload in bytes. Often the desired size is between 1300 and 1400. Now you adjust the size so that the OpenVPN packet fragments. You then reduce the size of the payload until there is no more fragmentation.

To get the size of the non-fragmented package you can use tcpdump:

tcpdump -i <ovpn> icmp or icmp6

Replace with the OpenVPN interface. The echo request should have length <len> at the end, where <len> is the length you are looking for. Now you can add the IP headers to it. This is 40 bytes for IPv6 and 20 bytes for IPv4.

This is then the size of the packet you transferred that was not fragmented. Therefore you can use this value as MTU:

tun-mtu <value>

This argument must be specified on both sides. If the circumstances of the connection, such as the protocol used, change, the MTU must be adjusted again. Since an IPv4 packet is smaller than an IPv6 packet, it is recommended that the OpenVPN connection is established over IPv4 if possible.

Automatic start with systemd

If you save the OpenVPN configuration under /etc/openvpn/<filename>.conf, you can use systemd to start the OpenVPN connection or set an automatic start:

systemctl start openvpn@<filename>
systemctl enable openvpn@<filename>