How To Install OpenVPN on Ubuntu 26.04 LTS

Install OpenVPN on Ubuntu 26.04

Many people trust commercial VPN providers with their traffic, but these services often log data, share it with third parties, or block business-specific routes. Running your own VPN gives you full control over what gets logged, where the tunnel terminates, and which clients can connect.

This guide shows you how to install OpenVPN on Ubuntu 26.04 LTS with certificate-based authentication, AES-256-GCM encryption, and TLS-crypt protection. Commands were verified on two fresh Ubuntu 26.04 LTS cloud instances running OpenVPN 2.7.0 and Easy-RSA 3.2.5 in April 2026.

You will build a private PKI with Easy-RSA, configure the server with secure ciphers, set up UFW NAT rules, generate a single inline client profile, and learn how to revoke access when needed. By the end, you will have a production-ready VPN server that you control completely.

Prerequisites

Before you start, make sure you have these items ready:

  • Ubuntu 26.04 LTS server (Resolute Raccoon) with kernel 7.0.0-10 or newer
  • Root or sudo access on both the server and client machines
  • Public IPv4 address on the VPN server with UDP port 1194 reachable from the internet
  • UFW firewall installed (default on Ubuntu server images)
  • Basic familiarity with systemd units and journalctl for log inspection
  • SSH key authentication already configured (recommended before exposing port 1194)

You do not need a paid VPS. A free-tier cloud instance from DigitalOcean, Linode, or AWS works fine for testing. The only requirement is that UDP 1194 is not blocked by your cloud provider’s security group.

Step 1: Update Your System and Install OpenVPN with Easy-RSA

Update the Package Index

sudo apt update && sudo apt install -y openvpn easy-rsa ufw

You run apt update first so the package manager pulls the latest OpenVPN 2.7.x from Ubuntu’s official archive instead of a cached older version. Installing a stale OpenVPN build risks known CVEs that have already been patched.

The easy-rsa package is the official PKI management tool. It wraps OpenSSL into reproducible scripts so you do not hand-craft certificate signing commands. OpenVPN requires certificate-based authentication, and Easy-RSA 3.2 makes this process reliable.

Verify Installed Versions

Check what you installed:

openvpn --version | head -1
dpkg -l | grep easy-rsa

Expected output:

OpenVPN 2.7.0 x86_64-pc-linux-gnu [SSL (OpenSSL)] [LZO] [LZ4] [EPOLL] [PKCS11] [AH/NATT] [AEAD]
ii  easy-rsa    3.2.5-1    all    fast and flexible PKI command-line tool

OpenVPN 2.7.0 ships with OpenSSL 3.5.5 compiled with DCO (Data Channel Offload) enabled, which brings throughput much closer to WireGuard on the same hardware. Easy-RSA 3.2.5 adds ECDSA support and simplified batch mode for automation.

This step confirms you have the right versions before generating certificates. If you see 2.4 or older, the tls-crypt directive and ECDSA keys will not work properly.

Step 2: Build the PKI with Easy-RSA 3.2

Create a Working Easy-RSA Directory

sudo mkdir -p /etc/openvpn/easy-rsa
sudo ln -sf /usr/share/easy-rsa/* /etc/openvpn/easy-rsa/
cd /etc/openvpn/easy-rsa

Ubuntu 26.04 installs Easy-RSA to /usr/share/easy-rsa/, but package updates overwrite files there. A working copy under /etc/openvpn/ ensures your vars file and generated PKI survive upgrades intact.

The symlink copies all Easy-RSA scripts into your working directory while keeping the original package files available for future updates.

Configure the vars File for ECDSA

cat <<EOF | sudo tee vars
set_var EASYRSA_ALGO       ec
set_var EASYRSA_CURVE      secp384r1
set_var EASYRSA_DAYS       3650
EOF

ECDSA keys on the secp384r1 curve are 4x smaller than 4096-bit RSA keys while providing equivalent security. Smaller keys mean faster TLS handshakes, which matters on servers with many concurrent clients.

The EASYRSA_DAYS value sets certificate validity to 10 years (3650 days), which reduces how often you must rotate the CA. Client certificates can still be revoked individually if a device is compromised.

Initialize the PKI and Build the CA

sudo ./easyrsa init-pki
sudo EASYRSA_BATCH=1 ./easyrsa --req-cn="OpenVPN-CA" build-ca nopass

The init-pki command creates the pki/ directory structure with subfolders for certificates, keys, and the CRL.

The build-ca command generates your private Certificate Authority. You need a private CA instead of a public CA like Let’s Encrypt because OpenVPN uses mutual authentication. Both the server and client must verify each other’s certificate. A public CA does not sign client certificates; only your own CA can issue both server and client certs.

The nopass flag omits a passphrase on the CA key for automated server startups. This is acceptable if you keep the CA key offline after PKI setup.

Issue the Server Certificate

sudo ./easyrsa --batch build-server-full server nopass

This creates server.crt and server.key in pki/. The subject includes the Common Name “server” plus server extensions that allow the certificate to be used for TLS server authentication.

The --batch flag skips interactive prompts so the script runs unattended. This is important for automation scripts and reproducible builds.

Issue Client Certificates

sudo ./easyrsa --batch build-client-full client1 nopass
sudo ./easyrsa --batch build-client-full alice nopass

You generate one certificate per client device. If one device is compromised, you revoke that single certificate instead of rebuilding the entire PKI.

The alice certificate shows how to add a second user later without touching the server configuration. OpenVPN validates each new connection against your CA at connect time.

Generate the Certificate Revocation List (CRL)

sudo ./easyrsa gen-crl

This creates pki/crl.pem. OpenVPN refuses to start if the crl-verify directive points to a non-existent file. The CRL lists all revoked certificates that the server should reject.

Easy-RSA 3.2 sets CRL validity to 180 days. An expired CRL causes OpenVPN to reject all new connections, not just revoked ones. Set a calendar reminder to regenerate the CRL before it expires.

Generate the TLS-Crypt Static Key

sudo openvpn --genkey tls-crypt /etc/openvpn/easy-rsa/pki/tc.key

The tls-crypt key wraps and encrypts every control channel packet. Unlike tls-auth which only authenticates, tls-crypt both authenticates and encrypts the TLS handshake itself. A passive observer cannot even see the handshake begin, making your server fingerprint invisible.

This is critical for privacy. Without tls-crypt, network scanners can identify your server as an OpenVPN instance even if they cannot decrypt the data channel.

Step 3: Write the OpenVPN Server Configuration File

Copy PKI Files to the Server Directory

sudo mkdir -p /etc/openvpn/server
sudo cp pki/ca.crt pki/server.crt pki/server.key pki/crl.pem pki/tc.key /etc/openvpn/server/
sudo chown nobody:nogroup /etc/openvpn/server/crl.pem

You copy certificates into /etc/openvpn/server/ instead of symlinking from pki/. This keeps a backup-ready, portable config directory and lets you rotate the PKI independently from a live running daemon without breaking the service mid-session.

The CRL file must be readable by the nobody user because OpenVPN drops privileges after startup. Without correct ownership, the service fails to start.

Create the Server Configuration

cat <<EOF | sudo tee /etc/openvpn/server/server.conf
port 1194
proto udp4
dev tun
ca ca.crt
cert server.crt
key server.key
tls-crypt tc.key
crl-verify crl.pem
dh none
topology subnet
server 10.8.0.0 255.255.255.0
push "redirect-gateway def1 bypass-dhcp"
push "dhcp-option DNS 8.8.8.8"
push "dhcp-option DNS 8.8.4.4"
keepalive 10 120
cipher AES-256-GCM
data-ciphers AES-256-GCM:AES-128-GCM
user nobody
group nogroup
persist-key
persist-tun
verb 3
EOF

Here is what each directive does and why it matters:

Directive Purpose WHY
proto udp4 UDP transport Avoids TCP-over-TCP meltdown (retransmit storms when tunnel carries TCP traffic)
dev tun Routed layer-3 More efficient and simpler to firewall than tap (bridged layer-2)
dh none No DH parameter ECDSA certs use ECDH for key exchange; DH file only needed for RSA-signed certs
cipher AES-256-GCM Data encryption AEAD mode provides confidentiality and integrity in one pass, unlike older CBC
data-ciphers Fallback ciphers Allows clients to negotiate AES-128-GCM if they do not support AES-256
redirect-gateway def1 Full tunnel Forces client traffic through the VPN, not just the server network
user nobody / group nogroup Privilege drop Limits blast radius if OpenVPN is exploited post-startup
persist-key / persist-tun Keep state Retains interface and key after privilege drop without re-reading protected files
verb 3 Log level Enough for troubleshooting without flooding logs with packet-level noise

The push "redirect-gateway def1 bypass-dhcp" line forces all client traffic through the VPN. Without it, only traffic destined for the 10.8.0.0/24 network goes through the tunnel.

The push "dhcp-option DNS" lines replace the client’s DNS servers with Google’s 8.8.8.8 and 8.8.4.4. This prevents DNS leaks where queries bypass the encrypted tunnel.

Step 4: Enable IP Forwarding and Configure UFW NAT

Enable IP Forwarding Persistently

echo 'net.ipv4.ip_forward=1' | sudo tee /etc/sysctl.d/99-openvpn.conf
sudo sysctl -p /etc/sysctl.d/99-openvpn.conf

By default, the Linux kernel silently drops packets that arrive on one interface (like tun0) destined for another (like eth0). Without ip_forward=1, clients connect to the tunnel but cannot reach the internet at all.

You write to /etc/sysctl.d/99-openvpn.conf instead of editing /etc/sysctl.conf directly. Drop-in files survive package upgrades and are easier to audit or remove later.

Detect the External Interface Automatically

EXTERNAL_IF=$(ip route get 1.1.1.1 | awk '{print $5; exit}')
echo "External interface: $EXTERNAL_IF"

Ubuntu 26.04 cloud images name interfaces differently depending on the hypervisor: ens3, enp1s0, or eth0. Hardcoding eth0 silently breaks NAT if your instance uses a different name. ip route get finds the actual outbound interface used to reach the internet.

Add NAT Rules to UFW

Edit the UFW before rules file:

sudo nano /etc/ufw/before.rules

Add this block before the *filter line:

# OpenVPN NAT
*nat
-A POSTROUTING -s 10.8.0.0/24 -o ens3 -j MASQUERADE
COMMIT

Replace ens3 with the actual interface name you found in the previous step.

Why MASQUERADE instead of SNAT? MASQUERADE dynamically uses the current IP of the outbound interface, so it survives IP address changes when your cloud VM gets a new DHCP-assigned public IP.

Allow Traffic Through UFW

sudo ufw default allow outgoing
sudo ufw default deny incoming
sudo ufw allow 22/tcp comment 'SSH'
sudo ufw allow 1194/udp comment 'OpenVPN'
sudo ufw --force enable

The allow 22/tcp rule is critical. If you enable UFW without allowing SSH first, you lock yourself out of the server permanently.

Set DEFAULT_FORWARD_POLICY=ACCEPT in /etc/default/ufw:

sudo sed -i 's/DEFAULT_FORWARD_POLICY="DROP"/DEFAULT_FORWARD_POLICY="ACCEPT"/' /etc/default/ufw
sudo ufw reload

Without this change, UFW blocks forwarded packets between tun0 and eth0, breaking NAT for VPN clients.

Step 5: Start and Enable the OpenVPN Server Service

Enable and Start the Service

sudo systemctl enable --now openvpn-server@server

Ubuntu 26.04 uses a templated systemd unit: openvpn-server@CONFIGNAME. Running systemctl start openvpn without the @server suffix starts nothing. The server part matches the filename server.conf in /etc/openvpn/server/.

The enable flag creates the symlink that brings the service up automatically after reboot. A VPN that disappears on the next kernel update is not production-ready.

Check Service Status

sudo systemctl status openvpn-server@server --no-pager

Expected output includes:

● openvpn-server@server.service - OpenVPN Robust and Highly Reliable Tunneling Solution
     Loaded: loaded (/lib/systemd/system/openvpn-server@server.service; enabled)
     Active: active (running) since ...
   Main PID: 12345 (openvpn)
      Tasks: 1 (limit: 4915)
     Memory: 3.2M
     CGroup: /system.slice/system-openvpn\x2dserver.slice/openvpn-server@server.service
             └─12345 /usr/sbin/openvpn --status /run/openvpn-server/status-server.log ...

Look for the line "Initialization Sequence Completed" in the journal. If you see "ERROR" or "TLS Error", check certificate paths and ownership.

Verify the Tunnel Interface and Port Binding

ip -brief addr show tun0
sudo ss -lun | grep 1194

Expected output:

tun0   UNKNOWN  10.8.0.1/24
udp    LISTEN   0      1      0.0.0.0:1194    0.0.0.0:*

The tun0 interface has the virtual IP 10.8.0.1/24. The ss output confirms OpenVPN is listening on UDP port 1194 on all interfaces.

Step 6: Generate an Inline Client .ovpn Profile

Understand What an Inline Profile Is

An inline .ovpn file embeds the CA certificate, client certificate, client private key, and TLS-crypt key inside <ca>, <cert>, <key>, and <tls-crypt> tags. All four pieces live in a single text file.

Why inline profiles? The alternative is shipping four separate files to the client and hoping paths are correct. A single .ovpn file is portable, importable on all platforms (Android, iOS, Windows, macOS, Linux), and reduces misconfig errors.

Create the Client Profile for client1

cat <<EOF | sudo tee /root/client1.ovpn
client
dev tun
proto udp
remote YOUR_SERVER_IP 1194
resolv-retry infinite
nobind
persist-key
persist-tun
verb 3
remote-cert-tls server
cipher AES-256-GCM
auth SHA256
<ca>
$(sudo cat /etc/openvpn/easy-rsa/pki/ca.crt)
</ca>
<cert>
$(sudo cat /etc/openvpn/easy-rsa/pki/issued/client1.crt)
</cert>
<key>
$(sudo cat /etc/openvpn/easy-rsa/pki/private/client1.key)
</key>
<tls-crypt>
$(sudo cat /etc/openvpn/easy-rsa/pki/tc.key)
</tls-crypt>
EOF

Replace YOUR_SERVER_IP with the actual public IPv4 address of your VPN server.

The remote-cert-tls server directive enforces that the server certificate must have the TLS Web Server Authentication EKU. Without it, any certificate signed by your CA (including client certificates) could impersonate the server, creating a man-in-the-middle attack vector.

Secure the File and Transfer It

sudo chmod 600 /root/client1.ovpn
scp /root/client1.ovpn user@your-client-machine:/home/user/

The file contains a private key in plaintext. World-readable permissions expose it to any local user on a multi-user system. Mode 600 ensures only the owner can read it.

Transfer the .ovpn file over a trusted channel: SCP, GPG-encrypted email, or a password manager. Never send it via plain email or HTTP.

Step 7: Connect a Client Machine to the VPN

Install OpenVPN on the Client

sudo apt install -y openvpn
sudo mkdir -p /etc/openvpn/client
sudo install -m 600 client1.ovpn /etc/openvpn/client/client.conf

Copy the .ovpn file via scp, then install it with install -m 600 to set secure permissions immediately. Mode 600 prevents other local users from reading the private key.

The filename client.conf matches the default systemd template openvpn-client@client.

Start the Client Service

sudo systemctl enable --now openvpn-client@client

Check the journal for successful connection:

journalctl -u openvpn-client@client --no-pager | tail -5

Expected final lines:

Sent push response packet
INET: Added via netlink
Initialization Sequence Completed

Verify Connectivity

Check the tunnel interface:

ip -brief addr show tun0

The client should show 10.8.0.2/24.

Test routing to the server:

ping -c 3 10.8.0.1

Expected output:

64 bytes from 10.8.0.1: icmp_seq=1 ttl=64 time=23.4 ms
64 bytes from 10.8.0.1: icmp_seq=2 ttl=64 time=22.1 ms
64 bytes from 10.8.0.1: icmp_seq=3 ttl=64 time=21.8 ms

3 packets transmitted, 3 received, 0% packet loss

Test full-tunnel internet access:

curl -s https://ifconfig.me

The returned IP should match your VPN server’s public IP, not the client’s home IP. This confirms all traffic is going through the encrypted tunnel.

Managing Clients: Add and Revoke Access

Add a New Client Without Restarting the Server

cd /etc/openvpn/easy-rsa
sudo ./easyrsa --batch build-client-full bob nopass

Regenerate the .ovpn profile using the same heredoc script from Step 6, substituting bob for client1. No server restart is needed. OpenVPN validates each new connection against the CA and current CRL at connect time; the server config does not enumerate clients.

Revoke a Client Certificate

When a device is lost or an employee leaves:

sudo ./easyrsa --batch revoke bob
sudo ./easyrsa gen-crl
sudo cp pki/crl.pem /etc/openvpn/server/
sudo chown nobody:nogroup /etc/openvpn/server/crl.pem
sudo systemctl restart openvpn-server@server

OpenVPN reads crl.pem at startup and caches it. A restart is required for the new revocation to take effect for already-connected clients.

Set a calendar reminder to regenerate the CRL every 180 days. An expired CRL breaks all new connections.

Troubleshooting Common OpenVPN Errors

1. TLS Handshake Failed or Client Hangs on Connect

On the server, run:

sudo tcpdump -ni ens3 udp port 1194

Replace ens3 with your actual external interface.

If no UDP packets arrive at the server, the block is upstream: your cloud security group, ISP firewall, or router NAT. If packets arrive but the handshake fails, the issue is local: wrong certificate, mismatched tls-crypt key, or wrong port.

Check the server journal:

journalctl -u openvpn-server@server --no-pager | tail -20

Look for "TLS Error" or "peer authentication failed".

2. VERIFY ERROR: CRL has expired

Regenerate the CRL:

cd /etc/openvpn/easy-rsa
sudo ./easyrsa gen-crl
sudo cp pki/crl.pem /etc/openvpn/server/
sudo chown nobody:nogroup /etc/openvpn/server/crl.pem
sudo systemctl restart openvpn-server@server

This error breaks all connections, not just revoked certs. OpenVPN enforces CRL validity as a safety guarantee; an expired CRL is treated as untrusted.

3. Clients Connect but Cannot Reach the Internet

Check IP forwarding:

sysctl net.ipv4.ip_forward

Output must be net.ipv4.ip_forward = 1.

Check NAT rules:

sudo iptables -t nat -L POSTROUTING -n -v

You should see a MASQUERADE rule for 10.8.0.0/24 on your external interface.

These two checks cover 90% of NAT failures. IP forwarding off silently drops routed packets; missing MASQUERADE means VPN clients get no return traffic from the internet.

4. Permission Denied on CRL or Key Files

sudo chown nobody:nogroup /etc/openvpn/server/crl.pem
sudo chown nobody:nogroup /etc/openvpn/server/server.key
sudo systemctl restart openvpn-server@server

OpenVPN drops to the nobody user after startup. If certificates or keys are not readable by nobody, the service fails to start.

5. Checking Active Connections

sudo cat /var/log/openvpn/openvpn-status.log

This file updates every 60 seconds with CLIENT_LIST rows showing the Common Name, real IP, virtual IP, bytes in/out, and connect time. Use this to monitor who is connected and how much data they transfer.

[su_box title=”VPS Manage Service Offer” style=”bubbles” box_color=”#000000″ radius=”10″]If you don’t have time to do all of this stuff, or if this is not your area of expertise, we offer a service to do “VPS Manage Service Offer”, starting from $10 (Paypal payment). Please contact us to get the best deal![/su_box]

r00t is a Linux Systems Administrator and open-source advocate with over ten years of hands-on experience in server infrastructure, system hardening, and performance tuning. Having worked across distributions such as Debian, Arch, RHEL, and Ubuntu, he brings real-world depth to every article published on this blog. r00t writes to bridge the gap between complex sysadmin concepts and practical, everyday application — whether you are configuring your first server or optimizing a production environment. Based in New York, US, he is a firm believer that knowledge, like open-source software, is best when shared freely.

Related Posts