How To Install OpenVPN on Fedora 44

Install OpenVPN on Fedora 44

Some VPN problems only show up at 2 a.m. A remote office loses its tunnel, a contractor can’t reach the staging database, or a laptop on hotel Wi-Fi refuses to connect. At that point nobody cares which tutorial looked prettiest. You need a VPN that was built correctly and that you can debug without guessing.

OpenVPN is still a very practical way to get there. WireGuard gets most of the attention these days, but OpenVPN keeps a place in real infrastructure. It runs over TCP 443 when a hostile network blocks everything else. It supports certificate-based authentication with revocation, plugs into PAM, LDAP and RADIUS, and has two decades of operational history behind it. Fedora 44 ships a current build, which also means a few behaviors have changed since the older guides were written.

This guide covers installing OpenVPN on Fedora 44 as a server, issuing client certificates with Easy-RSA, opening the firewall correctly, keeping SELinux enforcing, and connecting clients from Fedora and other systems. It also covers the Fedora-specific details that cause most failures: the systemd unit naming, the kernel data channel offload module, and firewalld forwarding. The commands apply to a fresh Fedora 44 VPS or a bare-metal box. Where Debian, Ubuntu or AlmaLinux differ, a note says so.

What Fedora 44 Ships With

Fedora 44 packages OpenVPN 2.7.x. The package page showed 2.7.0 at the start of the cycle [1], and a later security advisory moved Fedora 44 to 2.7.7 [10]. You don’t need a third-party repository. The OpenVPN community wiki states that Fedora releases already carry the latest OpenVPN release, and a Copr repository exists only if you want beta builds [4].

That affects how you work. Install from the stock repos, stay on dnf upgrade, and avoid the old habit of compiling from source “to get the latest”. Compiled builds drift out of the security update stream, which matters for a service that faces the internet.

Check what you have before you start:

cat /etc/fedora-release
uname -r
dnf info openvpn | head -n 12

The kernel version matters more than it used to. Starting with kernel 6.16, the OpenVPN data channel offload (DCO) module is part of the mainline kernel [8]. Fedora 44 runs a newer kernel than that, so DCO is available out of the box. It improves throughput and lowers CPU use, but it can also surprise you. A section further down covers the details.

Planning Before You Type Anything

Ten minutes of planning saves an hour of rebuilding. Decide these things first:

  • Role of the box. Full-tunnel gateway (all client traffic goes through it), split-tunnel access to a private network, or site-to-site.
  • Protocol and port. UDP 1194 is the default and the best performer. TCP 443 is a fallback for networks that block UDP, with a performance penalty from TCP-over-TCP behavior.
  • Tunnel subnet. Pick a range that won’t collide with the networks your clients sit on. 10.8.0.0/24 is the classic choice, and it collides with a lot of home routers. Something like 10.89.12.0/24 avoids that.
  • Where the CA lives. Ideally not on the VPN server. More on that below.
  • Public IP or DNS name. A hostname such as vpn.example.com lets you change IPs later without reissuing every client profile.

A small production example: a 2 vCPU, 2 GB RAM VPS can handle dozens of concurrent road-warrior clients, because OpenVPN’s cost is mostly crypto and packet copying. The numbers that matter are bandwidth and single-core speed, not RAM.

Step 1: Update the System and Install Packages

Start from a clean, patched system.

sudo dnf upgrade --refresh -y
sudo reboot

The reboot is not optional ceremony. If a kernel update landed, you want the running kernel to match what is installed, especially when DCO is involved.

Install the server package and the certificate tooling:

sudo dnf install -y openvpn easy-rsa firewalld policycoreutils-python-utils

policycoreutils-python-utils provides semanage, which you will need if you move OpenVPN off its default port. On a workstation that will only act as a client, you need a different set. Skip ahead to the client section for that.

Confirm the version and the features your build supports:

openvpn --version | head -n 3
openvpn --show-ciphers | head

Look at the first line of the version output for [DCO]. If it is there, the build supports data channel offload.

Step 2: Build the PKI with Easy-RSA

OpenVPN’s strength is certificate authentication. Each client gets its own certificate, so you can revoke one laptop without touching anyone else. Easy-RSA is the usual tool for that.

A recommendation from experience: build the CA on a separate, locked-down machine, even a small admin VM. If the CA private key sits on the internet-facing VPN server and that server is compromised, the attacker can mint valid client certificates. For a lab or a single small server, keeping everything on one box is acceptable. For anything that matters, split them.

Fedora installs Easy-RSA under /usr/share/easy-rsa/3/. Copy it into a working directory instead of running it in place:

mkdir -p ~/openvpn-ca
cp -r /usr/share/easy-rsa/3/* ~/openvpn-ca/
cd ~/openvpn-ca
chmod 700 ~/openvpn-ca

Initialize the PKI and create the certificate authority:

./easyrsa init-pki
./easyrsa build-ca nopass

nopass means the CA key is not encrypted with a passphrase. That is convenient but weaker. On a dedicated offline CA machine, drop nopass and type a passphrase each time you sign something. You will sign rarely, so the friction is fine. You’ll be asked for a Common Name. Use something descriptive like Example-VPN-CA.

Now the server certificate:

./easyrsa build-server-full vpn-server nopass

The server key has to be readable by the daemon at startup, so nopass is normal here. Then the first client:

./easyrsa build-client-full alice-laptop nopass

For client certificates, a passphrase is a good idea when the device is a laptop that travels. Without nopass, the user types the passphrase when connecting. With nopass, anyone who steals the profile file can use it. Choose based on the risk.

Generate a certificate revocation list now so the server config can reference it from day one:

./easyrsa gen-crl

You will rerun gen-crl every time you revoke something. The CRL also expires, by default after 180 days. If it does, every new connection fails. Put a calendar reminder on that, because it catches people months after the setup is finished.

Last, the tls-crypt key. This wraps the control channel in an extra layer of encryption and authentication, which hides the handshake from scanners and drops unauthenticated junk early:

sudo openvpn --genkey secret /etc/openvpn/server/tls-crypt.key

The --genkey secret syntax is the modern form. Older guides use --genkey --secret, which newer releases reject or warn about.

You may notice no Diffie-Hellman file in this walkthrough. Modern OpenVPN with ECDH negotiation doesn’t need dh.pem, and generating one is a relic of older tutorials. The server config below sets dh none.

Step 3: Place the Certificates

Copy the server files into the directory where Fedora’s systemd unit expects to find configs:

sudo install -d -m 0750 -o root -g openvpn /etc/openvpn/server
sudo install -m 0644 pki/ca.crt /etc/openvpn/server/ca.crt
sudo install -m 0644 pki/issued/vpn-server.crt /etc/openvpn/server/vpn-server.crt
sudo install -m 0640 -o root -g openvpn pki/private/vpn-server.key /etc/openvpn/server/vpn-server.key
sudo install -m 0640 -o root -g openvpn pki/crl.pem /etc/openvpn/server/crl.pem
sudo chgrp openvpn /etc/openvpn/server/tls-crypt.key
sudo chmod 0640 /etc/openvpn/server/tls-crypt.key

The openvpn user and group are created by the package. If getent group openvpn returns nothing on your system, create it with sudo groupadd -r openvpn.

The permissions are deliberate. The private key and tls-crypt key are readable only by root and the service group. The CRL needs to stay readable after OpenVPN drops privileges, because it is re-read when clients connect. If the CRL is root-only, you get confusing verification failures later.

SELinux matters here too. Files copied with install into /etc/openvpn/server inherit the correct label from the directory. Files moved with mv keep the label of where they came from, which can break things. If in doubt:

sudo restorecon -Rv /etc/openvpn

Step 4: Write the Server Configuration

On Fedora, server configs live in /etc/openvpn/server/ and run through openvpn-server@<name>.service. This differs from the old openvpn@<name> unit used in many older tutorials. If you follow a guide that tells you to enable openvpn@server, it was written for another distribution or an older layout. Fedora’s own naming is openvpn-server@ for servers and openvpn-client@ for clients.

Create /etc/openvpn/server/server.conf:

port 1194
proto udp
dev tun

ca ca.crt
cert vpn-server.crt
key vpn-server.key
crl-verify crl.pem
dh none
tls-crypt tls-crypt.key

topology subnet
server 10.89.12.0 255.255.255.0

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

keepalive 10 60

data-ciphers AES-256-GCM:AES-128-GCM:CHACHA20-POLY1305
data-ciphers-fallback AES-256-GCM
tls-version-min 1.2
tls-groups X25519:secp384r1

user openvpn
group openvpn
persist-key
persist-tun

status /run/openvpn-server/status-server.log
verb 3
explicit-exit-notify 1

Here is the reasoning behind the less obvious lines.

  • topology subnet gives each client a normal address in the tunnel subnet instead of the older point-to-point /30 pairs. It is cleaner, and it is the modern default for new setups.
  • redirect-gateway def1 overrides the client’s default route without deleting it, using two /1 routes. That is safer than replacing the default route, because the original route is still there when the tunnel drops.
  • data-ciphers lists what the data channel may negotiate. AES-GCM is the usual choice, and ChaCha20-Poly1305 helps on devices without AES hardware acceleration, such as some phones and older ARM boards. The old cipher AES-256-CBC plus auth SHA256 combination is legacy, and you should avoid it for new deployments.
  • tls-version-min 1.2 blocks ancient TLS versions.
  • explicit-exit-notify 1 tells UDP clients immediately when the server restarts, so they reconnect quickly instead of waiting for a keepalive timeout.
  • user openvpn and group openvpn drop privileges once the service has started. A bug in the VPN daemon then doesn’t hand over root.

If you want clients to reach only a private network and not send all traffic through the server, replace the redirect-gateway push with a route:

push "route 192.168.50.0 255.255.255.0"

Split tunneling is lighter on bandwidth and kinder to the server. Full tunneling gives better privacy on untrusted networks. Many teams run split tunnel for staff reaching internal services and keep a separate full-tunnel profile for travel.

Step 5: Turn on IP Forwarding

A VPN gateway has to forward packets between the tunnel and the real network. Make that persistent with a drop-in file, not by editing /etc/sysctl.conf:

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

The last command should print net.ipv4.ip_forward = 1. Drop-in files under /etc/sysctl.d/ survive package changes and are easier to audit than a monolithic sysctl.conf.

If you are going to serve IPv6 clients, you need net.ipv6.conf.all.forwarding = 1 as well, plus a v6 tunnel subnet. For most small deployments, leave IPv6 out of the tunnel and block it on clients to avoid leaks. Otherwise a dual-stack client may send IPv6 traffic around the VPN entirely.

Step 6: Configure firewalld

Fedora uses firewalld with an nftables backend by default. Open the OpenVPN service and set up NAT:

sudo systemctl enable --now firewalld
sudo firewall-cmd --permanent --add-service=openvpn
sudo firewall-cmd --permanent --add-masquerade
sudo firewall-cmd --permanent --zone=trusted --add-source=10.89.12.0/24
sudo firewall-cmd --reload

Predefined openvpn service in firewalld corresponds to UDP 1194. If you use TCP or another port, define your own:

sudo firewall-cmd --permanent --remove-service=openvpn
sudo firewall-cmd --permanent --add-port=443/tcp
sudo firewall-cmd --reload

Masquerading rewrites client source addresses to the server’s public address as traffic leaves, which is what lets a client at 10.89.12.6 browse the internet through the server. Adding the tunnel subnet to the trusted zone lets the traffic from VPN clients pass through the forwarding rules. If you’re tightening things down later, replace that with a firewalld policy that allows only the destinations clients need. Trusting an entire VPN subnet is fine for a small team and lazy for a large one.

Check the result:

sudo firewall-cmd --list-all
sudo firewall-cmd --zone=trusted --list-sources

On Ubuntu or Debian with UFW, the equivalent involves ufw allow 1194/udp plus a NAT rule in /etc/ufw/before.rules. On AlmaLinux, the firewalld steps are essentially identical to Fedora’s.

Step 7: Start the Service

sudo systemctl enable --now openvpn-server@server.service
sudo systemctl status openvpn-server@server.service --no-pager

The text after the @ has to match the config filename without .conf. A config called server.conf becomes openvpn-server@server. If you name it office.conf, the service is openvpn-server@office.

Confirm the listener and the tunnel interface:

sudo ss -ulpn | grep 1194
ip addr show tun0
journalctl -u openvpn-server@server -n 30 --no-pager

In the log, you want to see Initialization Sequence Completed. If the service is flapping, read the log from the top and fix the first error, not the last. The last error is usually a consequence.

Step 8: Build a Client Profile

Clients need the CA certificate, their own certificate and key, and the tls-crypt key. The most portable approach is a single .ovpn file with everything inlined. Here is a small script that assembles one:

#!/usr/bin/env bash
set -euo pipefail

CLIENT="$1"
PKI="$HOME/openvpn-ca/pki"
SRV="/etc/openvpn/server"
OUT="$HOME/${CLIENT}.ovpn"

cat > "$OUT" <<EOF
client
dev tun
proto udp
remote vpn.example.com 1194
resolv-retry infinite
nobind
persist-key
persist-tun
remote-cert-tls server
data-ciphers AES-256-GCM:AES-128-GCM:CHACHA20-POLY1305
tls-version-min 1.2
verb 3
<ca>
$(cat "$PKI/ca.crt")
</ca>
<cert>
$(openssl x509 -in "$PKI/issued/${CLIENT}.crt")
</cert>
<key>
$(cat "$PKI/private/${CLIENT}.key")
</key>
<tls-crypt>
$(sudo cat "$SRV/tls-crypt.key")
</tls-crypt>
EOF

chmod 600 "$OUT"
echo "Wrote $OUT"

Save it as make-client.sh, make it executable, and run ./make-client.sh alice-laptop. Replace vpn.example.com with your actual hostname.

The remote-cert-tls server line matters more than it looks. It makes the client check that the server certificate was issued for server use. Without it, any holder of a valid client certificate from your CA could stand up a rogue server and impersonate yours. It is one line and it closes a real man-in-the-middle hole.

Transfer the .ovpn file over a trusted channel (scp, an encrypted vault, or in person). Do not email it as plain text. The profile contains a private key.

Step 9: Connect from a Fedora Client

On a Fedora 44 desktop, the friendliest route goes through NetworkManager:

sudo dnf install -y openvpn NetworkManager-openvpn NetworkManager-openvpn-gnome

Then import the profile from the command line:

nmcli connection import type openvpn file alice-laptop.ovpn
nmcli connection up alice-laptop

Or use the GUI: Settings, Network, add a VPN, import from file. For headless clients, a systemd unit is more appropriate. Place the file at /etc/openvpn/client/office.conf and run:

sudo systemctl enable --now openvpn-client@office.service

SELinux deserves a mention on the client side. The confined openvpn_t domain is not allowed to read arbitrary files in a user’s home directory. If a NetworkManager-imported profile references certs in ~/ and the connection fails with AVC denials, a reported fix is to restore the right file context on the certificate directory [7]. Run restorecon on the folder first. If the denial persists, generate a local policy module from the audit log rather than disabling SELinux:

sudo ausearch -m avc -ts recent | audit2allow -M local-openvpn
sudo semodule -i local-openvpn.pp

Read the generated .te file before loading it. Blindly loading audit2allow output is how a “fix” turns into a permissive hole.

Troubleshooting Common Failures

Real deployments rarely fail in the textbook way. These are the cases that recur.

The service won’t start

Start with journalctl -u openvpn-server@server -b --no-pager. Typical causes:

  • Wrong unit name. openvpn@server doesn’t use /etc/openvpn/server/. Use openvpn-server@server.
  • File not found or permission denied. Check the paths in the config and ownership of the key files. If the daemon drops to the openvpn group, the key and CRL must be group-readable.
  • Options error. Old directives like cipher combined with new ones, or leftover comp-lzo, can trigger warnings or errors. Remove anything deprecated.

Clients connect but have no internet

This is almost always forwarding or NAT. Check in order:

sysctl net.ipv4.ip_forward
sudo firewall-cmd --list-all
sudo nft list ruleset | grep -i masq

If forwarding is 0, the sysctl drop-in wasn’t applied. If masquerade is missing in the active zone, add it and reload. Also confirm the server’s own outbound interface and zone, because masquerade applies to the zone of the outgoing interface. A VPS with its NIC in the public zone needs masquerade there.

Connection works by IP but names don’t resolve

Your pushed DNS isn’t being applied. On Fedora clients using systemd-resolved, the OpenVPN client doesn’t always update DNS on its own. NetworkManager’s plugin handles it correctly, which is another reason to prefer it on desktops. On a headless client, add the update-systemd-resolved helper or configure the resolver explicitly.

TLS handshake failed

The log will say TLS key negotiation failed to occur within 60 seconds. This means packets aren’t getting through, or the tls-crypt keys don’t match. Work through it:

  1. Confirm UDP 1194 is open at the cloud provider’s security group, not just in firewalld. This is the most common cause on VPS platforms.
  2. Verify the server is listening with ss -ulpn.
  3. Compare the tls-crypt key on both sides with sha256sum.
  4. Check the system clock on both ends. Certificates have validity windows, and a client with a wrong clock fails strangely.

Certificate verification errors

VERIFY ERROR messages point to the cause. A revoked or expired certificate, an expired CRL, or a missing remote-cert-tls match all appear here. The CRL expiry catches teams out. Regenerate it with ./easyrsa gen-crl, copy the new crl.pem into /etc/openvpn/server/, and the server picks it up for new connections without a restart.

Port changes break under SELinux

If you move the listener to a non-default port and the daemon is denied the bind, SELinux is blocking it. Label the port instead of disabling SELinux. This approach is documented in the eduVPN guide [3]:

sudo semanage port -a -t openvpn_port_t -p udp 1195
sudo semanage port -l | grep openvpn

Use -p tcp for TCP. For port 443 over TCP, it may already be labeled for another service such as HTTP. In that case, -m (modify) rather than -a can be required, or run OpenVPN on 1194/tcp and redirect 443 to it with firewalld.

A tunnel that worked before the upgrade to Fedora 44 now fails

Check the DCO section above first. Then look at the OpenVPN version on the other end. Anything old will behave differently with modern defaults, because the legacy cipher fallbacks have been gradually removed. Run the client with verb 4 and read the negotiation lines.

r00t is an experienced Linux enthusiast and technical writer with a passion for open-source software. With years of hands-on experience in various Linux distributions, r00t has developed a deep understanding of the Linux ecosystem and its powerful tools. He holds certifications in SCE and has contributed to several open-source projects. r00t is dedicated to sharing her knowledge and expertise through well-researched and informative articles, helping others navigate the world of Linux with confidence.

Related Posts