--- title: "How to Set Up an OpenVPN Server on Ubuntu 24.04" description: "Install OpenVPN and Easy-RSA on Ubuntu 24.04, build a certificate authority, route client traffic through the server with UFW and connect your first client." category: "Network Configuration" language: en source: https://cubepath.com/docs/network-configuration/vpn-openvpn-configuration updated: 2026-09-25 --- # How to Set Up an OpenVPN Server on Ubuntu 24.04 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: ```bash sudo apt update sudo apt install openvpn easy-rsa ``` Check the installed version: ```bash openvpn --version | head -n 1 ``` ```output 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: ```bash make-cadir ~/easy-rsa cd ~/easy-rsa ``` Initialize the public key infrastructure (PKI) directory: ```bash ./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: ```bash ./easyrsa build-ca ``` ```output 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. > Tip: For 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: ```bash ./easyrsa gen-req server nopass ``` Sign the request as a server certificate. Type `yes` to confirm and enter the CA passphrase: ```bash ./easyrsa sign-req server server ``` ```output 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: ```bash 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: ```bash 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 `openvpn-server@.service` unit reads its configuration from `/etc/openvpn/server/.conf`. Create the configuration file: ```bash sudo nano /etc/openvpn/server/server.conf ``` Add the following content: ```text 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: ```bash sudo nano /etc/sysctl.d/99-openvpn.conf ``` Add this line: ```ini net.ipv4.ip_forward = 1 ``` Load the new setting and confirm it: ```bash sudo sysctl --system sysctl net.ipv4.ip_forward ``` ```output 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: ```bash ip route show default ``` ```output 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: ```bash 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: ```text # 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: ```bash sudo ufw route allow in on tun0 out on eth0 sudo ufw allow 1194/udp sudo ufw reload ``` Check the result: ```bash sudo ufw status ``` ```output 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`: ```bash sudo systemctl enable --now openvpn-server@server ``` Check that it is running: ```bash sudo systemctl status openvpn-server@server ``` ```output ● openvpn-server@server.service - OpenVPN service for server Loaded: loaded (/usr/lib/systemd/system/openvpn-server@.service; enabled; preset: enabled) Active: active (running) ``` The log must end with `Initialization Sequence Completed`: ```bash 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: ```bash ip -br addr show tun0 ``` ```output 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: ```bash 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: ```bash 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): ```text 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: ```bash nano ~/clients/make-client.sh ``` ```bash #!/usr/bin/env bash set -euo pipefail name="${1:?usage: make-client.sh }" pki="$HOME/easy-rsa/pki" out="$HOME/clients/${name}.ovpn" umask 077 { cat "$HOME/clients/base.conf" echo "" cat "$pki/ca.crt" echo "" echo "" openssl x509 -in "$pki/issued/${name}.crt" echo "" echo "" cat "$pki/private/${name}.key" echo "" echo "" sudo cat /etc/openvpn/server/ta.key echo "" } > "$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`: ```bash chmod +x ~/clients/make-client.sh ~/clients/make-client.sh client1 ``` ```output Created /home/your_user/clients/client1.ovpn ``` Copy the file to the client device over a secure channel, for example from your local machine: ```bash 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: ```bash 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: ```bash 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: ```bash curl -4 https://icanhazip.com ``` ```output your_server_ip ``` On the server, list the connected clients from the status file the service maintains: ```bash 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): ```bash 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`: ```bash 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: ```text crl-verify crl.pem ``` ```bash 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.