How to Install OpenVPN Server on Ubuntu 24.04 VPS: Easy-RSA PKI, Client Profiles, Hardening
A self-hosted OpenVPN server gives you a private, encrypted gateway to the internet or to a remote office — without trusting a commercial VPN provider with your traffic logs, bandwidth, or jurisdiction. This tutorial walks you through building a production-grade OpenVPN server on Ubuntu 24.04 LTS from scratch: a complete Easy-RSA 3 PKI, TLS-crypt wrapping the control channel, AES-256-GCM on the data channel, nftables NAT, a reusable client.ovpn template, split-tunnel support, CRL-based revocation, and a hardened systemd unit.
You will see both the quick path (the community-maintained angristan/openvpn-install script, which gets you to a working VPN in five minutes) and the manual path, which is what you want for any deployment you intend to operate long-term.
Recommended plan: the CloudCore Starter VPS is more than enough for a personal or small-team OpenVPN server — 2 vCPU, 8 GB RAM, AES-NI support, and unmetered bandwidth for well under the cost of a commercial VPN subscription.
Table of Contents
Why Self-Host OpenVPN Instead of Using a Commercial VPN?
Commercial VPN services (NordVPN, ExpressVPN, Surfshark, Mullvad and the rest) are convenient, but they make four unavoidable compromises you do not have to accept when you run your own server:
- Trust. Every commercial VPN operator can see your unencrypted metadata (IPs, destinations, timestamps) at their exit node. "No-logs" is a policy, not a technical guarantee. On your own VPS, the only operator is you.
- Shared exit IPs. VPN provider IPs are aggressively rate-limited or outright blocked by banks, Cloudflare, Google, streaming services, and Stripe. A dedicated VPS IP is clean and not blacklisted.
- Bandwidth contention. Consumer VPN servers are heavily oversubscribed. A CloudCore Starter gives you unmetered bandwidth that no one else shares.
- Jurisdiction and cost. A commercial VPN at USD 10/month for a family pack costs more per year than a CloudCore Starter VPS that can host dozens of users, a WireGuard tunnel, a strongSwan IPsec gateway, and an OpenConnect/AnyConnect server at the same time.
- Site-to-site connectivity between a home lab and a cloud VPC without paying for managed VPN gateways.
- A stable, whitelisted egress IP for scraping, third-party APIs that require IP allow-lists, or remote database access.
- Certificate-based access control with per-user revocation — something no consumer VPN exposes.
- Compatibility with restrictive networks where TCP/443 masquerading is required; OpenVPN handles this natively, WireGuard does not.
Prerequisites
- Ubuntu 24.04 LTS VPS with root or sudo access.
- A public IPv4 address — note it down, you will put it in
remoteon every client profile. - UDP port 1194 open at any upstream firewall (most VPS providers leave all ports open by default).
- SSH access via key auth (recommended) and 5 minutes to generate a Certificate Authority that will live for the next 10 years.
- A CloudCore Starter or larger plan. The CloudCore Starter at 2 vCPU / 8 GB RAM handles 50+ concurrent OpenVPN clients comfortably thanks to AES-NI hardware acceleration.
ssh root@your-server-ipIf you are logged in as a non-root user, prefix every apt, mkdir, cp, systemctl and nft command below with sudo.
Step 1: Update the System
sudo apt update && sudo apt upgrade -yIf the kernel was updated, reboot before continuing — OpenVPN uses the tun kernel module and you want it matched to the running kernel:
sudo rebootReconnect after a minute.
Quick Path: angristan/openvpn-install
Before you dive into the manual install, here is the shortest path that actually works for production. The community script at github.com/angristan/openvpn-install has been maintained for a decade, has thousands of GitHub stars, and configures all the same hardening (tls-crypt, AES-256-GCM, compress disabled, SHA-256 digests) that we describe manually below.
Run it:
curl -O https://raw.githubusercontent.com/angristan/openvpn-install/master/openvpn-install.sh
chmod +x openvpn-install.sh
sudo ./openvpn-install.shThe script asks interactive questions:
IP address: <your public IPv4>
Port: 1194
Protocol: UDP
DNS: 9 (Cloudflare 1.1.1.1)
Compression: N
Customize encryption: N (accept defaults: AES-256-GCM, tls-crypt, secp521r1)
Client name: laptop
Password-protect client? 1 (no)When it finishes you get a ~/laptop.ovpn file ready to import into any OpenVPN client. To add more clients later, re-run the same script and choose Add a new user. To revoke, re-run and choose Revoke existing user.
This is a completely acceptable endpoint for a home or small-team VPN. If that is all you need, copy the .ovpn, move on to Step 12: Monitoring, and you are done. The rest of this guide builds the same thing by hand so you understand exactly what is on disk and can customize every layer.
Step 2: Install OpenVPN and Easy-RSA
Starting from a clean Ubuntu 24.04 (remove the angristan install first with ./openvpn-install.sh → Remove, if you ran it):
sudo apt install -y openvpn easy-rsaVerify the versions — Ubuntu 24.04 ships OpenVPN 2.6.x and Easy-RSA 3.1.x:
openvpn --version | head -n 1OpenVPN 2.6.x x86_64-pc-linux-gnu ...
/usr/share/easy-rsa/easyrsa --version
EasyRSA Version Information Version: 3.1.x
Create a dedicated Easy-RSA working directory outside of /etc/openvpn (the CA private key should never live next to running services):
sudo mkdir -p /etc/openvpn/easy-rsa
sudo ln -s /usr/share/easy-rsa/* /etc/openvpn/easy-rsa/
cd /etc/openvpn/easy-rsaStep 3: Initialize the Easy-RSA PKI
sudo ./easyrsa init-pkiExpected output:
Notice
------
'init-pki' complete; you may now create a CA or requests.
Your newly created PKI dir is: /etc/openvpn/easy-rsa/pkiCreate a vars file to set defaults for certificate fields (otherwise you will be prompted for each):
sudo tee /etc/openvpn/easy-rsa/vars > /dev/null <<'EOF'
set_var EASYRSA_ALGO ec
set_var EASYRSA_CURVE secp384r1
set_var EASYRSA_DIGEST sha256
set_var EASYRSA_CA_EXPIRE 3650
set_var EASYRSA_CERT_EXPIRE 825
set_var EASYRSA_CRL_DAYS 180
set_var EASYRSA_REQ_COUNTRY "US"
set_var EASYRSA_REQ_PROVINCE "N/A"
set_var EASYRSA_REQ_CITY "N/A"
set_var EASYRSA_REQ_ORG "VPS-Server.host"
set_var EASYRSA_REQ_EMAIL "[email protected]"
set_var EASYRSA_REQ_OU "OpenVPN"
EOFKey choices explained:
EASYRSA_ALGO=ecandEASYRSA_CURVE=secp384r1. Elliptic-curve keys are faster to generate, verify, and transmit than 2048-bit RSA while providing stronger security. secp384r1 is the industry sweet spot.EASYRSA_CERT_EXPIRE=825. The current browser/CA baseline is 825 days. Anything longer risks validation surprises if you later pivot to a TLS-terminating proxy.EASYRSA_CRL_DAYS=180. The CRL must be re-signed and re-published at least this often, so we will automate it later.
Step 4: Build the CA, Server Cert, and DH Parameters
Create the Certificate Authority (no password — nopass — because the CA lives on the same host; for high-security setups, move the CA offline):
sudo ./easyrsa build-ca nopassExpected output:
Notice
------
CA creation complete. Your new CA certificate is at:
pki/ca.crt
sudo ./easyrsa build-server-full server nopassGenerate Diffie-Hellman parameters. With EC keys, OpenVPN 2.6 uses ECDH automatically and classic DH is no longer strictly required, but we generate a file anyway for compatibility with older client configs:
sudo ./easyrsa gen-dhThis takes 1–3 minutes on a 2 vCPU VPS and produces pki/dh.pem.
Copy the server-side artifacts into OpenVPN's runtime directory:
sudo mkdir -p /etc/openvpn/server
sudo cp pki/ca.crt /etc/openvpn/server/
sudo cp pki/issued/server.crt /etc/openvpn/server/
sudo cp pki/private/server.key /etc/openvpn/server/
sudo cp pki/dh.pem /etc/openvpn/server/
sudo chmod 600 /etc/openvpn/server/server.keyStep 5: Generate the TLS-Crypt Key
tls-crypt wraps the entire TLS control channel in an additional symmetric cipher. This both authenticates and encrypts control packets — so port scanners see only random noise, and DoS floods against the TLS handshake are dropped before they hit OpenSSL.
sudo openvpn --genkey secret /etc/openvpn/server/tc.key
sudo chmod 600 /etc/openvpn/server/tc.keyYou can inspect it — it is a 256-bit pre-shared key in the OpenVPN static-key format.
Step 6: Write /etc/openvpn/server.conf
This is the heart of the install. Every directive below is there for a reason and the combination reflects current OpenVPN 2.6 best practice.
sudo tee /etc/openvpn/server/server.conf > /dev/null <<'EOF'
--- Listener ---
port 1194
proto udp
dev tun
topology subnet--- PKI ---
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/tc.key--- Client addressing ---
server 10.8.0.0 255.255.255.0
ifconfig-pool-persist /var/log/openvpn/ipp.txt
client-config-dir /etc/openvpn/ccd--- Push to clients ---
push "redirect-gateway def1 bypass-dhcp"
push "dhcp-option DNS 1.1.1.1"
push "dhcp-option DNS 9.9.9.9"
push "block-outside-dns"--- Data-channel crypto ---
data-ciphers AES-256-GCM:AES-128-GCM:CHACHA20-POLY1305
data-ciphers-fallback AES-256-GCM
auth SHA256
tls-version-min 1.2
tls-cipher TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384:TLS-ECDHE-RSA-WITH-AES-256-GCM-SHA384--- Compression (off is deliberate; VORACLE attack) ---
compress stub-v2--- Keepalive and tuning ---
keepalive 10 120
persist-key
persist-tun
explicit-exit-notify 1--- Drop privileges ---
user nobody
group nogroup--- Logging ---
status /var/log/openvpn/openvpn-status.log 30
log-append /var/log/openvpn/openvpn.log
verb 3
mute 20--- Revocation ---
crl-verify /etc/openvpn/server/crl.pem
EOFKey decisions:
proto udpon port 1194. UDP is the default and correct choice: lower overhead and no TCP-over-TCP meltdown when both tunneled and underlying connections retransmit.topology subnet. Modern addressing; every client gets one IP inside 10.8.0.0/24 instead of the legacy/30pairs.data-ciphers AES-256-GCM:...CHACHA20-POLY1305. AES-GCM is hardware-accelerated on any VPS with AES-NI. ChaCha20 is advertised as a fallback for older mobile clients without AES-NI.compress stub-v2. Compression is disabled due to the VORACLE attack.stub-v2tells the client explicitly "no framing, no compression" and is required for interop with OpenVPN 2.4 clients.tls-crypt(nottls-auth). Newer, encrypts as well as authenticates; protects against active probing.user nobody/group nogroup. The daemon drops to an unprivileged user immediately after binding UDP/1194.
sudo mkdir -p /etc/openvpn/ccd /var/log/openvpn
sudo touch /etc/openvpn/server/crl.pem
sudo chown -R nobody:nogroup /var/log/openvpnThe empty crl.pem placeholder prevents OpenVPN from failing to start before you have revoked anyone. We will populate it in Step 11.
Step 7: Enable IP Forwarding and nftables NAT
OpenVPN delivers packets into the tun0 interface, but without IP forwarding the kernel will not route them out to the public interface, and without NAT the destination servers will have no idea how to return replies.
Enable IPv4 forwarding permanently:
echo 'net.ipv4.ip_forward=1' | sudo tee /etc/sysctl.d/99-openvpn.conf sudo sysctl --system | grep ip_forward
net.ipv4.ip_forward = 1
Detect your public interface name — usually eth0, ens3, or enp1s0:
ip -o -4 route show to default | awk '{print $5}'
ens3
Configure nftables with a masquerade rule for the 10.8.0.0/24 subnet. Ubuntu 24.04 ships nftables by default (iptables is available in iptables-nft compat mode):
sudo tee /etc/nftables.conf > /dev/null <<'EOF' #!/usr/sbin/nft -f flush rulesettable inet filter { chain input { type filter hook input priority 0; policy drop; ct state established,related accept iifname "lo" accept iifname "tun0" accept tcp dport 22 accept udp dport 1194 accept ip protocol icmp accept } chain forward { type filter hook forward priority 0; policy drop; iifname "tun0" oifname "ens3" accept iifname "ens3" oifname "tun0" ct state established,related accept } chain output { type filter hook output priority 0; policy accept; } }
table ip nat { chain postrouting { type nat hook postrouting priority 100; ip saddr 10.8.0.0/24 oifname "ens3" masquerade } } EOF
Replace ens3 with your actual public interface name in both places. Apply and persist:
sudo nft -f /etc/nftables.conf
sudo systemctl enable --now nftables
sudo nft list ruleset | head -n 30Step 8: Start the systemd Service
OpenVPN ships a templated systemd unit — openvpn-server@<config-name>.service — that reads /etc/openvpn/server/<config-name>.conf. Because we named our file server.conf:
sudo systemctl enable --now [email protected]
sudo systemctl status [email protected]Expected output:
● [email protected] - OpenVPN service for server
Loaded: loaded (/lib/systemd/system/[email protected]; enabled)
Active: active (running) since Thu 2026-04-16 10:05:22 UTC
Main PID: 4123 (openvpn)
Status: "Initialization Sequence Completed"
Tasks: 1 (limit: 9500)
Memory: 3.8MConfirm the tun0 interface appeared:
ip -4 addr show tun0
inet 10.8.0.1/24 ...
And that UDP/1194 is listening:
sudo ss -ulnp | grep 1194
UNCONN 0 0 0.0.0.0:1194 0.0.0.0:* users:(("openvpn",pid=4123,fd=6))
Step 9: Generate Client Certificates and .ovpn Profiles
Every client gets its own certificate. Do not share a single client cert across devices — it defeats revocation.
Back in /etc/openvpn/easy-rsa:
cd /etc/openvpn/easy-rsa
sudo ./easyrsa build-client-full laptop nopassThis produces pki/issued/laptop.crt and pki/private/laptop.key.
Now assemble a single-file .ovpn profile. A helper script makes this repeatable:
sudo tee /usr/local/sbin/make-ovpn > /dev/null <<'EOF' #!/bin/bashUsage: make-ovpn <client-name> <server-public-ip-or-hostname>
set -euo pipefail CLIENT="$1" SERVER="$2" EASY_RSA=/etc/openvpn/easy-rsa OUT="/root/${CLIENT}.ovpn"cat > "$OUT" <<CFG client dev tun proto udp remote ${SERVER} 1194 resolv-retry infinite nobind persist-key persist-tun remote-cert-tls server data-ciphers AES-256-GCM:AES-128-GCM:CHACHA20-POLY1305 data-ciphers-fallback AES-256-GCM auth SHA256 verb 3 compress stub-v2 CFG
echo '<ca>' >> "$OUT" cat "${EASY_RSA}/pki/ca.crt" >> "$OUT" echo '</ca>' >> "$OUT"
echo '<cert>' >> "$OUT" sed -ne '/BEGIN CERTIFICATE/,/END CERTIFICATE/p' \ "${EASY_RSA}/pki/issued/${CLIENT}.crt" >> "$OUT" echo '</cert>' >> "$OUT"
echo '<key>' >> "$OUT" cat "${EASY_RSA}/pki/private/${CLIENT}.key" >> "$OUT" echo '</key>' >> "$OUT"
echo '<tls-crypt>' >> "$OUT" sed -ne '/BEGIN OpenVPN Static key/,/END OpenVPN Static key/p' \ /etc/openvpn/server/tc.key >> "$OUT" echo '</tls-crypt>' >> "$OUT"
chmod 600 "$OUT" echo "Wrote $OUT" EOF sudo chmod +x /usr/local/sbin/make-ovpn
Run it:
sudo make-ovpn laptop YOUR.PUBLIC.IP
Wrote /root/laptop.ovpn
Transfer laptop.ovpn to the client securely (never email it — the private key is embedded):
scp [email protected]:/root/laptop.ovpn ./Import it into:
- Linux:
sudo openvpn --config laptop.ovpn - macOS: Tunnelblick or OpenVPN Connect
- Windows: OpenVPN GUI or OpenVPN Connect
- iOS / Android: OpenVPN Connect (store apps)
/var/log/openvpn/openvpn-status.log.Step 10: Split-Tunnel vs Full-Tunnel
The server config above pushes redirect-gateway def1 — every byte the client sends goes through the VPN (full-tunnel). That is what you want for privacy and geo-egress.
For split-tunnel (VPN only reaches specific subnets, normal traffic stays local), remove that push and push explicit routes instead. Override it per-client with a CCD file rather than editing the server config:
sudo tee /etc/openvpn/ccd/laptop > /dev/null <<'EOF'
push-reset
push "route 10.8.0.0 255.255.255.0"
push "route 192.168.50.0 255.255.255.0"
push "dhcp-option DNS 1.1.1.1"
EOFpush-reset cancels any inherited pushes; then you list only the subnets that should traverse the tunnel. Reload OpenVPN:
sudo systemctl reload [email protected]The next time laptop reconnects, only 10.8.0.0/24 and 192.168.50.0/24 go through the VPN; everything else uses the client's normal ISP.
Step 11: Revoke a Client (CRL)
Say the laptop was stolen. Revoke it immediately:
cd /etc/openvpn/easy-rsa sudo ./easyrsa revoke laptoptype 'yes' when prompted
sudo ./easyrsa gen-crl sudo cp pki/crl.pem /etc/openvpn/server/crl.pem sudo chmod 644 /etc/openvpn/server/crl.pem sudo systemctl reload [email protected]
Because server.conf contains crl-verify /etc/openvpn/server/crl.pem, OpenVPN consults the CRL on every new connection and refuses any cert listed there — the laptop can no longer reconnect even if the attacker has the .ovpn file.
Because CRLs expire (we set EASYRSA_CRL_DAYS=180), automate a monthly regeneration with cron:
sudo tee /etc/cron.d/openvpn-crl > /dev/null <<'EOF'
Regenerate the OpenVPN CRL on the 1st of every month
0 3 1 root cd /etc/openvpn/easy-rsa && ./easyrsa gen-crl && cp pki/crl.pem /etc/openvpn/server/crl.pem && systemctl reload [email protected]
EOFStep 12: Monitoring and Logging
OpenVPN writes two files we care about:
/var/log/openvpn/openvpn.log— full server log (connections, renegotiations, errors)./var/log/openvpn/openvpn-status.log— refreshed every 30 seconds with the current client table.
sudo tail -f /var/log/openvpn/openvpn.logCurrent connected clients:
sudo cat /var/log/openvpn/openvpn-status.logExample output:
OpenVPN CLIENT LIST
Updated,2026-04-16 10:30:05
Common Name,Real Address,Bytes Received,Bytes Sent,Connected Since
laptop,203.0.113.15:54231,18432,91827,2026-04-16 10:12:44
ROUTING TABLE
Virtual Address,Common Name,Real Address,Last Ref
10.8.0.2,laptop,203.0.113.15:54231,2026-04-16 10:30:02For production, feed these into Prometheus via openvpn_exporter and graph in Grafana — or into Loki for log search. Log rotation is handled by the Ubuntu openvpn package's default /etc/logrotate.d/openvpn rule.
Enable the service health check in your uptime monitor (Uptime Kuma, Zabbix, or similar) by polling UDP/1194 — nmap -sU -p1194 your.server.ip should return open|filtered.
Hardening Checklist
Once it is running, walk through these:
ufwor nftablesdropon everything except 22/TCP and 1194/UDP. Already done in Step 7.- SSH with key-only auth,
PermitRootLogin prohibit-password, fail2ban on sshd. - Drop root privileges in OpenVPN — already set via
user nobody / group nogroup. - Separate CA host. For anything beyond a personal VPN, take the Easy-RSA PKI off the VPN server entirely: generate certs on a laptop, transfer only
ca.crt,server.crt,server.key, andcrl.pemback. - tls-crypt-v2. If you expect probing from state-level adversaries, upgrade from
tls-crypttotls-crypt-v2, which gives each client a unique wrap key. - Disable the legacy
compressdirective entirely if all your clients are OpenVPN 2.5+. (compress stub-v2is only for 2.4 compat.) - Review DHCP options you push — avoid leaking internal DNS suffixes.
- systemd unit hardening. Drop a unit override:
sudo systemctl edit [email protected]Add:
[Service]
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
ReadWritePaths=/var/log/openvpn /etc/openvpn /run
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_SETUID CAP_SETGID CAP_DAC_OVERRIDEThen sudo systemctl daemon-reload && sudo systemctl restart [email protected].
Troubleshooting
| Problem | Likely cause | Fix |
|---|---|---|
| Client times out connecting | UDP/1194 blocked upstream | sudo nft list ruleset on the server; check provider firewall; try TCP/443 as fallback. |
| TLS handshake fails repeatedly | tls-crypt key mismatch | The <tls-crypt> block inside .ovpn must be identical to /etc/openvpn/server/tc.key. |
VERIFY ERROR: depth=0, error=CRL has expired | CRL regeneration skipped | Re-run easyrsa gen-crl, copy to /etc/openvpn/server/crl.pem, reload service. |
| Client connects but no internet | IP forwarding off or NAT rule missing | sysctl net.ipv4.ip_forward must be 1; verify nftables masquerade rule references the right interface. |
| "TLS key negotiation failed to occur within 60 seconds" | Clock skew > 5 min between client and server | sudo timedatectl set-ntp true. |
| Slow throughput (< 30 Mbps) on a fast VPS | AES without AES-NI | grep -o aes /proc/cpuinfo — if empty, force data-ciphers CHACHA20-POLY1305. |
RESOLVE: Cannot resolve host address on client | remote in .ovpn is wrong | Ensure you passed the actual public IP to make-ovpn. |
verb 3 to verb 5 in server.conf, reloading, and tailing the log.FAQ
Should I use OpenVPN or WireGuard?
Use WireGuard if you control every client and want the simplest, fastest option. Use OpenVPN when you need TCP fallback for restrictive networks (hotels, corporate firewalls, airports), certificate-based authentication with per-user revocation, LDAP/RADIUS integration, or compatibility with corporate VPN clients that only speak OpenVPN or IPsec. Many of our customers run both on the same VPS — OpenVPN on UDP/1194 for field laptops, WireGuard on UDP/51820 for server-to-server tunnels.
Is OpenVPN on UDP 1194 fast enough for streaming?
Yes. On a CloudCore Starter VPS with AES-NI, OpenVPN 2.6 with AES-256-GCM sustains 100–300 Mbps per client and saturates a 1 Gbps uplink with 4–5 concurrent users. The bottleneck is almost never the server — it is the client CPU or the last-mile ISP. For maximum throughput on hardware without AES-NI (older ARM devices), use data-ciphers CHACHA20-POLY1305.
How do I revoke a lost client certificate?
cd /etc/openvpn/easy-rsa && sudo ./easyrsa revoke <name> && sudo ./easyrsa gen-crl, copy pki/crl.pem into /etc/openvpn/server/crl.pem, and reload the service. The server rejects the revoked cert immediately on the next connection attempt. See Step 11 for the full procedure and automated CRL refresh.
Can I run OpenVPN and WireGuard on the same VPS?
Yes, and it is a common pattern. They use different UDP ports by default (1194 vs 51820) and different kernel interfaces (tun0 vs wg0). Keep the subnets distinct — for example 10.8.0.0/24 for OpenVPN and 10.10.0.0/24 for WireGuard — and add a second masquerade rule in your nftables config for the WireGuard subnet.
How many clients can one VPS support?
The CloudCore Starter (2 vCPU, 8 GB RAM) comfortably handles 50–75 concurrent users for browsing and work-from-home loads. OpenVPN's per-client memory footprint is small (a few MB) but the user-space crypto on a single process is the ceiling. For 200+ concurrent users, scale vertically (more vCPU with AES-NI) before scaling horizontally.
Is the angristan script safe to use?
Yes. It is widely audited, used in production by thousands of operators, and produces effectively the same config we wrote by hand. The only reasons to prefer the manual path are (a) you want to version-control your config in git, (b) you need custom network topology (site-to-site, CCD overrides, multiple subnets), or (c) you need to move the CA off-host.
Next Steps
Now that OpenVPN is up:
- Add a complementary tunnel. Install WireGuard alongside for low-overhead server-to-server links, or strongSwan for IPsec/IKEv2 on iOS/macOS native clients, or OpenConnect if your users already have Cisco AnyConnect.
- Monitor it. Wire
openvpn_exporterinto Prometheus and add a Grafana dashboard so you can see bandwidth per user and alert on abnormal connection patterns. - Back up the PKI. Tar up
/etc/openvpn/easy-rsa/pkiand encrypt it offline. Losing the CA private key means every client must be reissued. - Review upstream docs. OpenVPN's own community resources are the canonical reference for every directive and edge case.
- Harden the host. Add Fail2Ban, enable unattended-upgrades, and run a CIS-benchmark scan.
Ready to host your own VPN? The CloudCore Starter is optimized for VPN workloads — AES-NI-capable CPUs, unmetered egress, a clean static IP, and DDoS protection included. Spin one up in under a minute and have OpenVPN running before your coffee gets cold.