$linuxjunkies
>

Set Up an OpenVPN Server

Build a complete OpenVPN server on Linux using Easy-RSA 3 for PKI, a hardened server.conf, client .ovpn profiles, and systemd — with a WireGuard comparison.

IntermediateUbuntuDebianFedoraArch12 min readUpdated June 7, 2026

Before you start

  • A Linux server with a public IP address and root or sudo access
  • UDP port 1194 open in any upstream firewall or cloud security group
  • Basic familiarity with systemd service management and firewall concepts
  • OpenVPN 2.4 or later (for tls-crypt and AES-256-GCM support)

OpenVPN remains one of the most widely deployed VPN solutions on Linux — battle-tested, audited, and supported everywhere from home routers to enterprise firewalls. Setting it up from scratch involves three moving parts: a small PKI (handled by Easy-RSA), a server config, and per-client credential bundles. This guide walks through all three on a single server, then touches on when WireGuard is the better call.

Install OpenVPN and Easy-RSA

Debian / Ubuntu

sudo apt update
sudo apt install openvpn easy-rsa

Fedora / RHEL 9 / Rocky Linux

sudo dnf install openvpn easy-rsa

Arch Linux

sudo pacman -S openvpn easy-rsa

Build the PKI with Easy-RSA 3

Easy-RSA 3 (the current generation) ships its own easyrsa script and keeps all state in a self-contained directory. Never use Easy-RSA 2; it is unmaintained and insecure by today's standards.

Initialise the PKI directory

mkdir -p ~/openvpn-ca
cp -r /usr/share/easy-rsa/* ~/openvpn-ca/
cd ~/openvpn-ca
./easyrsa init-pki

Create the Certificate Authority

./easyrsa build-ca nopass

You will be prompted for a Common Name (e.g. MyVPN-CA). The nopass flag skips a CA key passphrase — convenient for automation, but for production consider protecting the CA key with a strong passphrase and keeping it offline.

Generate the server certificate and key

./easyrsa gen-req server nopass
./easyrsa sign-req server server

Type yes to confirm signing. You now have pki/issued/server.crt and pki/private/server.key.

Generate Diffie-Hellman parameters and a tls-crypt key

./easyrsa gen-dh
openvpn --genkey secret ~/openvpn-ca/pki/private/ta.key

DH parameter generation can take a few minutes. The ta.key is used for tls-crypt, which authenticates and encrypts the TLS handshake, preventing port scans from identifying your server as OpenVPN and mitigating some DoS vectors.

Issue a client certificate

./easyrsa gen-req client1 nopass
./easyrsa sign-req client client1

Repeat with a different name for each additional client. Never reuse a certificate across multiple devices.

Install PKI Files on the Server

sudo install -o root -g root -m 600 \
  ~/openvpn-ca/pki/ca.crt \
  ~/openvpn-ca/pki/issued/server.crt \
  ~/openvpn-ca/pki/private/server.key \
  ~/openvpn-ca/pki/dh.pem \
  ~/openvpn-ca/pki/private/ta.key \
  /etc/openvpn/server/

Write the Server Configuration

sudo tee /etc/openvpn/server/server.conf <<'EOF'
port 1194
proto udp
dev tun

ca   /etc/openvpn/server/ca.crt
cert /etc/openvpn/server/server.crt
key  /etc/openvpn/server/server.key
dh   /etc/openvpn/server/dh.pem

tls-crypt /etc/openvpn/server/ta.key

server 10.8.0.0 255.255.255.0
ifconfig-pool-persist /var/lib/openvpn/ipp.txt

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

keepalive 10 120
cipher AES-256-GCM
tls-version-min 1.2
tls-cipher TLS-ECDHE-RSA-WITH-AES-256-GCM-SHA384
auth SHA256

user nobody
group nogroup
persist-key
persist-tun

status /var/log/openvpn/status.log
log-append /var/log/openvpn/openvpn.log
verb 3
EOF

Key decisions above: UDP port 1194 is standard and faster than TCP for VPN tunnels. AES-256-GCM with TLS 1.2 minimum is the modern baseline; older AES-CBC ciphers are still functional but offer no AEAD protection. If your clients are OpenVPN 2.5+, you can also set data-ciphers AES-256-GCM:AES-128-GCM for negotiated cipher selection.

sudo mkdir -p /var/log/openvpn /var/lib/openvpn

Enable IP Forwarding and Firewall Rules

Enable kernel forwarding persistently

sudo tee /etc/sysctl.d/99-openvpn.conf <<'EOF'
net.ipv4.ip_forward = 1
EOF
sudo sysctl --system

Firewall — nftables (universal approach)

sudo nft add table ip nat
sudo nft add chain ip nat postrouting { type nat hook postrouting priority 100 \; }
# Replace eth0 with your actual outbound interface
sudo nft add rule ip nat postrouting oifname "eth0" masquerade

Firewall — firewalld (Fedora / RHEL / Rocky)

sudo firewall-cmd --permanent --add-service=openvpn
sudo firewall-cmd --permanent --add-masquerade
sudo firewall-cmd --reload

Firewall — ufw (Ubuntu / Debian)

sudo ufw allow 1194/udp
# In /etc/ufw/before.rules, add a MASQUERADE rule above *filter
# Then:
sudo ufw reload

With ufw you must also edit /etc/default/ufw and set DEFAULT_FORWARD_POLICY="ACCEPT".

Start and Enable the OpenVPN Service

sudo systemctl enable --now openvpn-server@server

The systemd unit name follows the pattern openvpn-server@<config-name> where <config-name> matches the filename without .conf.

Create a Client Profile (.ovpn)

An .ovpn file embeds all certificates and keys inline, making distribution straightforward. Replace YOUR_SERVER_IP with the public IP or hostname of your server.

CLIENT=client1
SERVER_IP=YOUR_SERVER_IP
CA_DIR=~/openvpn-ca/pki

cat > ~/${CLIENT}.ovpn <<EOF
client
dev tun
proto udp
remote ${SERVER_IP} 1194
resolv-retry infinite
nobind
persist-key
persist-tun
remote-cert-tls server
cipher AES-256-GCM
auth SHA256
verb 3

<ca>
$(cat ${CA_DIR}/ca.crt)
</ca>
<cert>
$(cat ${CA_DIR}/issued/${CLIENT}.crt)
</cert>
<key>
$(cat ${CA_DIR}/private/${CLIENT}.key)
</key>
<tls-crypt>
$(cat ${CA_DIR}/private/ta.key)
</tls-crypt>
EOF

Transfer the .ovpn file to the client over a secure channel (SCP, encrypted email, or a one-time-link service). It contains private key material — treat it accordingly.

Verify the Server Is Running

sudo systemctl status openvpn-server@server
sudo journalctl -u openvpn-server@server -n 50
ip addr show tun0

You should see a tun0 interface with address 10.8.0.1. On the client side, after importing the profile, the client will receive an address in the 10.8.0.0/24 range and all traffic will route through the tunnel if redirect-gateway is active.

Troubleshooting

  • TLS handshake failed / AUTH_FAILED — Confirm the ta.key is identical on both server and client, and that both use tls-crypt (not tls-auth).
  • No internet through the tunnel — Verify net.ipv4.ip_forward=1 is active (sysctl net.ipv4.ip_forward) and that the masquerade/NAT rule is applied to the correct outbound interface.
  • Connection times out on port 1194 — Check your cloud provider's security group or host firewall. UDP is often blocked by default on cloud VMs.
  • Certificate verify failed — The server cert must be signed with sign-req server (not client). The remote-cert-tls server client directive enforces this.
  • Unit not found / service fails to start — On RHEL/Rocky, the group nogroup may not exist; use nobody for both user and group, or create the group first.

Modern Alternative: WireGuard

If you are starting a new deployment rather than integrating with existing OpenVPN infrastructure, seriously evaluate WireGuard. It is built into the Linux kernel since 5.6, has a dramatically smaller codebase (~4 000 lines vs OpenVPN's ~100 000+), is faster on modern hardware, and its configuration is considerably simpler — no PKI ceremony, just key pairs and [Peer] blocks in /etc/wireguard/wg0.conf.

OpenVPN still wins when you need: certificate revocation at scale, deep firewall traversal (TCP 443 mode), legacy client support, or fine-grained access control via certificates. For a lean personal VPN or a small team, WireGuard is the pragmatic modern choice.

tested on:Ubuntu 24.04Debian 12Fedora 40Rocky 9

Frequently asked questions

Can I run OpenVPN on TCP instead of UDP?
Yes — change 'proto udp' to 'proto tcp' in both server.conf and the client profile. TCP mode is useful for traversing restrictive firewalls, especially on port 443, but adds overhead from double-acknowledgement. Use it only when UDP is blocked.
How do I revoke a client certificate?
Run './easyrsa revoke client1' followed by './easyrsa gen-crl' in your PKI directory. Copy the generated pki/crl.pem to /etc/openvpn/server/ and add 'crl-verify /etc/openvpn/server/crl.pem' to server.conf, then restart the service.
What is the difference between tls-auth and tls-crypt?
Both use a pre-shared key for HMAC authentication of the TLS channel, preventing unauthorised clients from reaching the OpenVPN handshake. tls-crypt (OpenVPN 2.4+) additionally encrypts the control channel, hiding that the port is running OpenVPN at all. Prefer tls-crypt on modern deployments.
Why does traffic not route through the tunnel even though I am connected?
The two most common causes are: IP forwarding is not enabled on the server (check 'sysctl net.ipv4.ip_forward'), or the NAT/masquerade rule is missing or applied to the wrong interface. Confirm the outbound interface name with 'ip route' and recheck your firewall rules.
Should I use OpenVPN or WireGuard for a new deployment?
WireGuard is faster, simpler to configure, and kernel-native since Linux 5.6 — it is the better choice for most new personal or small-team VPNs. Stick with OpenVPN when you need certificate revocation at scale, TCP 443 firewall traversal, or compatibility with existing OpenVPN client ecosystems.

Related guides