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
sudoprivileges 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.
TipFor larger deployments, run the CA on a separate machine that is not reachable from the Internet and copy only signed certificates to the VPN server. Keeping it on the server is acceptable for a small setup as long as the CA key has a passphrase.
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.0creates the VPN subnet. The server takes10.8.0.1and 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 noneenables 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 nocipherline is needed.user nobodyandgroup nogroupdrop 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.shand keep a list of which certificate belongs to whom. - Remove the
redirect-gatewaypush and addpush "route ..."lines to give clients access only to your private networks. - Back up
~/easy-rsaand/etc/openvpn/serverto a secure location, since losing the CA means reissuing every certificate.
