OpenVPN is an open-source VPN that encrypts traffic between clients and a server using TLS, with each client authenticated by its own certificate. It runs on Linux, Windows, macOS, Android and iOS, and works well through NAT and restrictive networks. In this tutorial you will install OpenVPN on Ubuntu 24.04, create a private certificate authority with Easy-RSA, configure a routed VPN that sends all client traffic through the server, open the firewall and NAT with UFW, and connect a client with a single .ovpn file.

Prerequisites

To follow this guide you need:

  • A server running Ubuntu 24.04 LTS with a public IPv4 address, for example a CubePath VPS. 1 vCPU and 1 GB of RAM are enough for a small team.
  • A non-root user with sudo privileges and UFW allowing SSH (sudo ufw allow OpenSSH).
  • A client device where you can install OpenVPN or the OpenVPN Connect app.

Replace your_server_ip with the server's public IP address wherever it appears.

Step 1 - Installing OpenVPN and Easy-RSA

Both packages are in Ubuntu's main repositories. easy-rsa provides the scripts to run a small certificate authority:

sudo apt update
sudo apt install openvpn easy-rsa

Check the installed version:

openvpn --version | head -n 1
OpenVPN 2.6.x x86_64-pc-linux-gnu [SSL (OpenSSL)] [LZO] [LZ4] [EPOLL] [PKCS11] [MH/PKTINFO] [AEAD] [DCO]

Step 2 - Creating the certificate authority

Every server and client certificate is signed by a certificate authority (CA) that you control. The server only accepts clients with a certificate signed by this CA. Create an Easy-RSA working directory in your home directory, owned by your regular user:

make-cadir ~/easy-rsa
cd ~/easy-rsa

Initialize the public key infrastructure (PKI) directory:

./easyrsa init-pki

Build the CA. You will be asked for a passphrase that protects the CA's private key, and for a common name; any descriptive name such as CubePath VPN CA works:

./easyrsa build-ca
CA creation complete. Your new CA certificate is at:
* /home/your_user/easy-rsa/pki/ca.crt

Choose a strong passphrase and store it safely. You will need it every time you sign or revoke a certificate.

Step 3 - Creating the server certificate and TLS key

Generate a key and certificate request for the server. nopass leaves the server key unencrypted so that OpenVPN can start unattended. Press ENTER to accept server as the common name:

./easyrsa gen-req server nopass

Sign the request as a server certificate. Type yes to confirm and enter the CA passphrase:

./easyrsa sign-req server server
Certificate created at:
* /home/your_user/easy-rsa/pki/issued/server.crt

Copy the CA certificate, the server certificate and the server key to the OpenVPN server directory:

sudo cp pki/ca.crt pki/issued/server.crt pki/private/server.key /etc/openvpn/server/

Generate a shared tls-crypt key. It encrypts and authenticates the TLS handshake itself, so the server silently drops packets from anyone who does not have it, which hides the service from port scanners and blunts denial-of-service attempts:

sudo openvpn --genkey secret /etc/openvpn/server/ta.key

OpenVPN 2.6 uses elliptic-curve Diffie-Hellman key exchange, so you do not need to generate the slow classic Diffie-Hellman parameters.

Step 4 - Configuring the OpenVPN server

Ubuntu's [email protected] unit reads its configuration from /etc/openvpn/server/<name>.conf. Create the configuration file:

sudo nano /etc/openvpn/server/server.conf

Add the following content:

port 1194
proto udp
dev tun

ca ca.crt
cert server.crt
key server.key
dh none
tls-crypt ta.key

topology subnet
server 10.8.0.0 255.255.255.0

push "redirect-gateway def1 bypass-dhcp"
push "dhcp-option DNS 1.1.1.1"
push "dhcp-option DNS 9.9.9.9"

keepalive 10 120
user nobody
group nogroup
persist-key
persist-tun

verb 3
explicit-exit-notify 1

The important lines are:

  • server 10.8.0.0 255.255.255.0 creates the VPN subnet. The server takes 10.8.0.1 and assigns the other addresses to clients.
  • push "redirect-gateway def1 bypass-dhcp" tells clients to send all their traffic through the VPN. Remove it if you only want clients to reach the server and its private networks.
  • push "dhcp-option DNS ..." gives clients DNS resolvers to use while connected, so DNS queries do not leak outside the tunnel.
  • dh none enables elliptic-curve key exchange only. The data channel uses OpenVPN 2.6's default ciphers (AES-256-GCM, AES-128-GCM and ChaCha20-Poly1305), so no cipher line is needed.
  • user nobody and group nogroup drop root privileges after startup.

Step 5 - Enabling IP forwarding

To route client traffic to the Internet, the kernel must forward packets between the tun0 interface and the public interface. Create a sysctl drop-in file:

sudo nano /etc/sysctl.d/99-openvpn.conf

Add this line:

net.ipv4.ip_forward = 1

Load the new setting and confirm it:

sudo sysctl --system
sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 1

Step 6 - Configuring UFW for the VPN

Clients use private 10.8.0.x addresses, so their traffic must be translated (masqueraded) to the server's public IP on the way out. First find the name of the public interface:

ip route show default
default via 203.0.113.1 dev eth0 proto static

In this example it is eth0. Open the UFW rules that run before the user rules:

sudo nano /etc/ufw/before.rules

At the very top of the file, before the *filter line, add a NAT section. Replace eth0 if your interface has a different name:

# NAT for OpenVPN clients
*nat
:POSTROUTING ACCEPT [0:0]
-A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE
COMMIT

UFW drops forwarded traffic by default. Allow forwarding only from the VPN interface to the public interface, open the OpenVPN port, and reload:

sudo ufw route allow in on tun0 out on eth0
sudo ufw allow 1194/udp
sudo ufw reload

Check the result:

sudo ufw status
Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere
1194/udp                   ALLOW       Anywhere
Anywhere on eth0           ALLOW FWD   Anywhere on tun0
...

Step 7 - Starting the OpenVPN service

Enable the service so it starts at boot, and start it now. The part after @ is the name of the configuration file without .conf:

sudo systemctl enable --now openvpn-server@server

Check that it is running:

sudo systemctl status openvpn-server@server
● [email protected] - OpenVPN service for server
     Loaded: loaded (/usr/lib/systemd/system/[email protected]; enabled; preset: enabled)
     Active: active (running)

The log must end with Initialization Sequence Completed:

sudo journalctl -u openvpn-server@server --no-pager | tail -n 5

The server now has a tun0 interface with the first address of the VPN subnet:

ip -br addr show tun0
tun0             UNKNOWN        10.8.0.1/24 fe80::.../64

Step 8 - Creating a client certificate

Each client (person or device) gets its own certificate, so you can revoke one without affecting the others. Create one called client1 from the Easy-RSA directory:

cd ~/easy-rsa
./easyrsa gen-req client1 nopass
./easyrsa sign-req client client1

Type yes and enter the CA passphrase when asked. nopass means the client key is not encrypted, so the .ovpn file you create next is enough to connect: treat it like a password. Omit nopass if you want users to type a passphrase each time they connect.

Step 9 - Building the client configuration file

A single .ovpn file with the certificates embedded is the easiest format to distribute, since every OpenVPN client can import it. Create a directory for client files and a base configuration that every client shares:

mkdir -p ~/clients
nano ~/clients/base.conf

Add the following, replacing your_server_ip with your server's public IP address (or a DNS name pointing to it):

client
dev tun
proto udp
remote your_server_ip 1194
resolv-retry infinite
nobind
persist-key
persist-tun
remote-cert-tls server
verb 3

remote-cert-tls server makes the client check that it is talking to a certificate signed for server use, which prevents another client from impersonating the server.

Now create a small script that combines the base configuration with a client's certificates:

nano ~/clients/make-client.sh
#!/usr/bin/env bash
set -euo pipefail

name="${1:?usage: make-client.sh <client-name>}"
pki="$HOME/easy-rsa/pki"
out="$HOME/clients/${name}.ovpn"

umask 077
{
  cat "$HOME/clients/base.conf"
  echo "<ca>"
  cat "$pki/ca.crt"
  echo "</ca>"
  echo "<cert>"
  openssl x509 -in "$pki/issued/${name}.crt"
  echo "</cert>"
  echo "<key>"
  cat "$pki/private/${name}.key"
  echo "</key>"
  echo "<tls-crypt>"
  sudo cat /etc/openvpn/server/ta.key
  echo "</tls-crypt>"
} > "$out"

echo "Created $out"

umask 077 makes the resulting file readable only by you, and openssl x509 outputs only the PEM block of the certificate. Make the script executable and generate the file for client1:

chmod +x ~/clients/make-client.sh
~/clients/make-client.sh client1
Created /home/your_user/clients/client1.ovpn

Copy the file to the client device over a secure channel, for example from your local machine:

scp your_user@your_server_ip:clients/client1.ovpn .

Step 10 - Connecting a client

Windows, macOS, Android and iOS: install the official OpenVPN Connect app, import client1.ovpn, and connect.

Linux desktop: install the NetworkManager plugin and import the file, so the pushed DNS servers are applied automatically:

sudo apt install network-manager-openvpn
sudo nmcli connection import type openvpn file client1.ovpn
nmcli connection up client1

Linux command line: for a quick test, run OpenVPN directly. It stays in the foreground until you press Ctrl+C. In this mode the pushed DNS servers are not applied to the system resolver:

sudo apt install openvpn
sudo openvpn --config client1.ovpn

The client log must end with Initialization Sequence Completed.

Verifying the tunnel

On the client, check that your public IP address is now the server's:

curl -4 https://icanhazip.com
your_server_ip

On the server, list the connected clients from the status file the service maintains:

sudo cat /run/openvpn-server/status-server.log

The CLIENT_LIST lines show each client's common name, real address and VPN address (for example 10.8.0.2).

Step 11 - Revoking a client certificate

When a device is lost or a person leaves, revoke their certificate so the server refuses it. Revoke it and generate a certificate revocation list (CRL):

cd ~/easy-rsa
./easyrsa revoke client1
./easyrsa gen-crl

Install the CRL on the server. It must be world-readable, because OpenVPN reads it for every new connection after dropping privileges to nobody:

sudo install -m 644 pki/crl.pem /etc/openvpn/server/crl.pem

The first time, add this line to /etc/openvpn/server/server.conf and restart the service:

crl-verify crl.pem
sudo systemctl restart openvpn-server@server

A revoked client now fails to connect, and the server log shows a certificate revoked error for it. The CRL has an expiry date (180 days by default in Easy-RSA); run ./easyrsa gen-crl and install the new file before then, or all clients will be rejected.

Troubleshooting

The client times out with TLS key negotiation failed to occur within 60 seconds: the UDP packets do not reach the server. Check sudo ufw status for 1194/udp, make sure the service is running, and confirm the remote line uses the right IP. Because of tls-crypt, the server does not answer clients whose ta.key does not match, which looks the same from the client side.

The client connects but has no Internet access: forwarding or NAT is missing. Check sysctl net.ipv4.ip_forward, confirm the *nat block in /etc/ufw/before.rules uses the correct interface name, and verify the ALLOW FWD rule in sudo ufw status. Run sudo ufw reload after any change.

Websites do not load but pinging an IP address works: DNS is not being applied on the client. Use OpenVPN Connect or the NetworkManager import instead of the plain openvpn command.

The service fails to start: read the error with sudo journalctl -u openvpn-server@server -e. The usual causes are a typo in server.conf or a missing file in /etc/openvpn/server/.

All clients are suddenly rejected with a CRL error: the CRL has expired. Regenerate and reinstall it as shown in Step 11.

Conclusion

You installed OpenVPN on Ubuntu 24.04, built a certificate authority with Easy-RSA, configured a routed VPN protected by tls-crypt, set up forwarding and NAT with UFW, and connected a client with a self-contained .ovpn file. You can also revoke access for individual clients.

As next steps, you can:

  • Create one certificate per device with make-client.sh and keep a list of which certificate belongs to whom.
  • Remove the redirect-gateway push and add push "route ..." lines to give clients access only to your private networks.
  • Back up ~/easy-rsa and /etc/openvpn/server to a secure location, since losing the CA means reissuing every certificate.