OpenVPN server
OpenVPN is a battle-tested protocol for running your own VPN server: it encrypts all traffic and routes it through your VPS, with client access controlled by individual certificates rather than a shared password. In this article you'll set up an OpenVPN server on an mHost VPS by hand — build a certificate authority with Easy-RSA from scratch, configure server.conf, assemble a client .ovpn file, and open the right port in the firewall.
What you'll need
- An mHost VPS on Ubuntu 22.04/24.04, Debian 11/12, or AlmaLinux/Rocky Linux 8/9.
- SSH access as root or a user with
sudorights. The commands below are written for root: if you're working as a sudo user, prefix each command withsudo— except for commands of the formcat > file <<EOF, where you should usesudo tee file <<EOF ... EOFinstead (otherwise the redirect itself doesn't run as root and fails with a permission error). - Port 1194/udp open in the firewall — how to do that is covered in Step 5.
Step 1. Install OpenVPN and Easy-RSA
Ubuntu / Debian
apt update
apt install -y openvpn easy-rsaAlmaLinux / Rocky Linux
dnf install -y epel-release
dnf install -y openvpn easy-rsaThe openvpn package isn't in AlmaLinux/Rocky's base repositories — it comes from the EPEL repository, which is why the first command enables it.
Check that OpenVPN installed:
openvpn --versionStep 2. Build the PKI: certificate authority and server/client keys
Easy-RSA is a set of scripts for running your own certificate authority (CA): it's what issues the server certificate and the client certificates that trust each other. Set up a working copy of Easy-RSA in its own directory.
Ubuntu / Debian
make-cadir /etc/openvpn/easy-rsa
cd /etc/openvpn/easy-rsaAlmaLinux / Rocky Linux
mkdir -p /etc/openvpn/easy-rsa
cp -r /usr/share/easy-rsa/3/* /etc/openvpn/easy-rsa/
cd /etc/openvpn/easy-rsaFrom here on, the commands are the same on every system:
./easyrsa init-pki
./easyrsa build-ca nopass
./easyrsa build-server-full server nopass
./easyrsa gen-dh
./easyrsa build-client-full client1 nopassinit-pkicreates an empty PKI in the current folder — apki/directory with its own structure.build-ca nopasscreates the root certificate authority: the CA's private key (pki/private/ca.key) and its self-signed certificate (pki/ca.crt). It will ask for a Common Name — you can just press Enter and accept the default.build-server-full server nopassgenerates a key and certificate for the server namedserver, signed by that same CA right away:pki/private/server.keyandpki/issued/server.crt.gen-dhgenerates Diffie-Hellman parameters (pki/dh.pem); on a low-power VPS this command can take a couple of minutes.build-client-full client1 nopassdoes the same for the first client:pki/private/client1.keyandpki/issued/client1.crt. Repeat this command with a new name (client2,phone, and so on) for every new device — give each client its own certificate, and don't reuse the same one across multiple devices.
build-ca only asks for a Common Name. build-server-full and build-client-full additionally show the request details and ask you to confirm them — type yes.
Step 3. Configure server.conf
Create a dedicated system user for OpenVPN to run as after startup (it starts as root, but is then supposed to drop its privileges):
id -u openvpn &>/dev/null || useradd --system --no-create-home --shell /usr/sbin/nologin openvpnCopy the CA certificate, the server certificate and key, and the Diffie-Hellman parameters next to the server.conf you're about to create:
mkdir -p /etc/openvpn/server
cp /etc/openvpn/easy-rsa/pki/ca.crt /etc/openvpn/server/ca.crt
cp /etc/openvpn/easy-rsa/pki/issued/server.crt /etc/openvpn/server/server.crt
cp /etc/openvpn/easy-rsa/pki/private/server.key /etc/openvpn/server/server.key
cp /etc/openvpn/easy-rsa/pki/dh.pem /etc/openvpn/server/dh.pem
chmod 600 /etc/openvpn/server/server.keyCreate the config itself:
cat > /etc/openvpn/server/server.conf <<'EOF'
port 1194
proto udp
dev tun
ca ca.crt
cert server.crt
key server.key
dh dh.pem
topology subnet
server 10.8.0.0 255.255.255.0
ifconfig-pool-persist ipp.txt
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
user openvpn
group openvpn
persist-tun
status openvpn-status.log
verb 3
explicit-exit-notify 1
EOFA quick rundown of the key directives:
port 1194/proto udp— OpenVPN's standard port and protocol (you'll need this again in Step 5 when opening the firewall).ca/cert/key/dh— paths to the PKI files, given without a leading slash because systemd starts OpenVPN with its working directory set to/etc/openvpn/server(see Step 6), where these files live.server 10.8.0.0 255.255.255.0— a dedicated subnet for VPN clients; the server itself takes address10.8.0.1in it.push "redirect-gateway def1 bypass-dhcp"— tells the client to route all of its traffic through the tunnel, not just requests to the server itself.push "dhcp-option DNS ..."— which DNS servers the client should use while the VPN is active.user openvpn/group openvpn— after initializing, OpenVPN drops its root privileges to the user created above.explicit-exit-notify 1— only works withproto udp: the client learns faster that the server restarted and reconnects sooner.
Step 4. Enable IP forwarding
By default the Linux kernel doesn't forward packets between network interfaces — and without that, the server can't pass VPN clients' traffic on to the internet. Turn forwarding on persistently, with a separate file under /etc/sysctl.d:
cat > /etc/sysctl.d/99-openvpn.conf <<'EOF'
net.ipv4.ip_forward = 1
EOF
sysctl --systemCheck that the setting took effect:
sysctl net.ipv4.ip_forwardExpected output is net.ipv4.ip_forward = 1. A setting under /etc/sysctl.d/ survives a server reboot — there's nothing extra to configure for it to persist.
Step 5. Open UDP 1194 in the firewall and enable NAT
Ubuntu / Debian (ufw)
Allow the VPN port itself:
ufw allow 1194/udpFind the name of the server's main network interface — you'll need it for the NAT rule:
ip route show defaultIn the output (something like default via 45.156.21.1 dev eth0 proto static), look at the dev field — that's the interface name. The examples below use eth0; substitute your own if it differs.
Open /etc/ufw/before.rules:
nano /etc/ufw/before.rulesand add these four lines at the very top of the file — before the *filter line:
*nat
:POSTROUTING ACCEPT [0:0]
-A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE
COMMITAllow packet forwarding at the ufw level too:
sed -i 's/DEFAULT_FORWARD_POLICY="DROP"/DEFAULT_FORWARD_POLICY="ACCEPT"/' /etc/default/ufwApply the changes:
ufw reloadAlmaLinux / Rocky Linux (firewalld)
These distributions use firewalld by default instead of ufw:
firewall-cmd --permanent --add-port=1194/udp
firewall-cmd --permanent --add-masquerade
firewall-cmd --reloadStep 6. Start OpenVPN
The configuration lives at /etc/openvpn/server/server.conf — the openvpn-server@ systemd unit takes its instance name from the file name without the extension (server), so it's started like this:
systemctl enable --now openvpn-server@server
systemctl status openvpn-server@serverThe status should show active (running). If something's wrong, check the journal:
journalctl -u openvpn-server@server -n 50 --no-pagerConfirm the virtual interface came up:
ip addr show tun0You should see a tun0 interface with address 10.8.0.1.
Step 7. Assemble the .ovpn client profile and connect
OpenVPN can bundle the CA, certificate, and key right inside a single .ovpn file — using <ca>, <cert>, and <key> blocks. That keeps the profile a single file that's easy to hand to a device.
cd /etc/openvpn/easy-rsa
{
cat <<'EOF'
client
dev tun
proto udp
remote YOUR_SERVER_IP 1194
resolv-retry infinite
nobind
persist-tun
remote-cert-tls server
verb 3
<ca>
EOF
cat pki/ca.crt
echo '</ca>'
echo '<cert>'
cat pki/issued/client1.crt
echo '</cert>'
echo '<key>'
cat pki/private/client1.key
echo '</key>'
} > /root/client1.ovpnReplace YOUR_SERVER_IP with your VPS's public IP address (or domain).
Copy the file to the client device, for example with scp:
scp root@YOUR_SERVER_IP:/root/client1.ovpn .From here it depends on the device:
- Windows / macOS / iOS / Android — install the official OpenVPN Connect app and import the
client1.ovpnfile into it. - Linux — import the profile into NetworkManager (
nmcli connection import type openvpn file client1.ovpn) or run it directly:openvpn --config client1.ovpn.
Check on the server that the client connected:
cat /etc/openvpn/server/openvpn-status.logWhat's next
Need more clients — repeat build-client-full with a new name (Step 2) and assemble a new .ovpn for it (Step 7); give every device its own certificate, and don't reuse the same one across multiple devices. A device is lost or it's time to cut it off — revoke its certificate (./easyrsa revoke-issued client1, then ./easyrsa gen-crl) and reference the resulting pki/crl.pem in server.conf with the crl-verify crl.pem directive.
The ready-made "Openvpn" recipe in VMmanager 6, or third-party scripts like angristan/openvpn-install, get you the same result in one action (see the start of this article). Want a protocol that's simpler to configure — see the article "WireGuard VPN" (also available as a ready recipe in VMmanager 6). And to keep track of whatever else is open on the server — see "The ufw firewall: managing ports".