FreeBSD.software
Home/Guides/How to Set Up OpenVPN on FreeBSD
tutorial·2026-03-29·20 min read

How to Set Up OpenVPN on FreeBSD

Step-by-step guide to setting up OpenVPN on FreeBSD. Covers installation, EasyRSA PKI, server and client configuration, routing, TLS hardening, and troubleshooting.

How to Set Up OpenVPN on FreeBSD

OpenVPN remains one of the most deployed VPN solutions in production. It runs on every major platform, uses TLS for its control channel, and supports both routed and bridged tunnel modes. While newer protocols like WireGuard offer raw speed advantages, OpenVPN provides broader compatibility, well-understood certificate-based authentication, and granular configuration options that many enterprise environments still require.

This tutorial walks through the complete process: installing OpenVPN on FreeBSD, building a PKI with EasyRSA, writing server and client configurations, configuring PF firewall rules, hardening TLS, and troubleshooting common problems. By the end you will have a production-grade OpenVPN server running on FreeBSD with properly secured certificate infrastructure.

Why OpenVPN

Four properties make OpenVPN a strong choice for FreeBSD administrators.

Maturity. OpenVPN has been in active development since 2001. Its codebase has been audited multiple times, its behavior is well-documented, and its failure modes are understood. When something goes wrong, decades of community knowledge exist to help diagnose it.

TLS-based security. The control channel uses standard TLS, which means OpenVPN benefits from the same cryptographic ecosystem that secures the web. Certificate revocation, OCSP, hardware token integration, and multi-factor authentication all work because they build on X.509 PKI.

Cross-platform support. Official clients exist for Windows, macOS, Linux, iOS, and Android. The .ovpn configuration file format is universal across all of them. You generate one file per client and it works everywhere.

Flexibility. OpenVPN supports full-tunnel routing (redirect all traffic), split-tunnel routing (only specific subnets), TCP and UDP transport, custom port numbers, and both TUN (routed) and TAP (bridged) modes. This covers use cases from road-warrior remote access to site-to-site links.

For a detailed comparison of OpenVPN and WireGuard on FreeBSD, see WireGuard vs OpenVPN on FreeBSD.

Prerequisites

Before starting, confirm you have the following:

  • FreeBSD 13.0 or later -- Any recent FreeBSD release works. This guide was tested on FreeBSD 14.1-RELEASE.
  • Root access -- All commands in this guide require root privileges. Use su - or sudo as appropriate.
  • A public IP address -- Your FreeBSD server needs a publicly reachable IP or a port-forwarded UDP port. Throughout this guide, replace YOUR_SERVER_IP with your actual server IP.
  • UDP port 1194 open -- OpenVPN uses UDP 1194 by default. Ensure your hosting provider or upstream firewall permits inbound traffic on that port.
  • Basic networking knowledge -- You should be comfortable with IP addresses, subnets, routing, and editing configuration files.

Verify your FreeBSD version:

sh
freebsd-version

Expected output: 14.1-RELEASE or newer.

Step 1: Install OpenVPN and EasyRSA

Install both packages from the FreeBSD package repository:

sh
pkg install openvpn easy-rsa

This installs the openvpn binary and the easyrsa shell script for managing your PKI. Confirm the installation:

sh
openvpn --version | head -1

You should see output like OpenVPN 2.6.x with the OpenSSL library version.

Step 2: Set Up the PKI with EasyRSA

OpenVPN uses X.509 certificates for authentication. EasyRSA provides a simplified interface for creating and managing a certificate authority (CA), server certificates, and client certificates.

Initialize the PKI Directory

Create a dedicated directory for the PKI and initialize it:

sh
mkdir -p /usr/local/etc/openvpn/easy-rsa cd /usr/local/etc/openvpn/easy-rsa cp -r /usr/local/share/easy-rsa/* .

Initialize the PKI structure:

sh
./easyrsa init-pki

This creates the pki/ directory tree where all certificates, keys, and requests will be stored.

Configure EasyRSA Variables

Create a vars file to set defaults so you do not have to answer prompts repeatedly:

sh
cat > vars << 'EOF' set_var EASYRSA_REQ_COUNTRY "US" set_var EASYRSA_REQ_PROVINCE "California" set_var EASYRSA_REQ_CITY "San Francisco" set_var EASYRSA_REQ_ORG "MyOrg" set_var EASYRSA_REQ_EMAIL "admin@example.com" set_var EASYRSA_REQ_OU "VPN" set_var EASYRSA_KEY_SIZE 2048 set_var EASYRSA_ALGO ec set_var EASYRSA_CURVE secp384r1 set_var EASYRSA_CA_EXPIRE 3650 set_var EASYRSA_CERT_EXPIRE 825 set_var EASYRSA_CRL_DAYS 180 set_var EASYRSA_DIGEST "sha384" EOF

Notes on these values:

  • EASYRSA_ALGO ec and EASYRSA_CURVE secp384r1 use elliptic curve cryptography. EC keys are smaller and faster than RSA keys at equivalent security levels. If you need RSA compatibility with older clients, change EASYRSA_ALGO to rsa and set EASYRSA_KEY_SIZE to 2048 or 4096.
  • EASYRSA_CA_EXPIRE 3650 gives the CA a 10-year lifetime.
  • EASYRSA_CERT_EXPIRE 825 limits individual certificates to roughly 2 years, matching CA/Browser Forum guidelines and forcing periodic re-enrollment.

Build the Certificate Authority

sh
./easyrsa build-ca nopass

The nopass flag creates a CA key without a passphrase. For production environments where the CA machine is separate and air-gapped, omit nopass and protect the CA key with a strong passphrase.

This generates:

  • pki/ca.crt -- The CA certificate (distribute to all clients and the server).
  • pki/private/ca.key -- The CA private key (keep this secret and secure).

Generate the Server Certificate

Request and sign a server certificate:

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

Type yes when prompted to confirm the signing. This creates:

  • pki/issued/server.crt -- The server certificate.
  • pki/private/server.key -- The server private key.

Generate Diffie-Hellman Parameters

If you are using RSA keys, generate DH parameters for the key exchange:

sh
./easyrsa gen-dh

This creates pki/dh.pem. With EC keys (EASYRSA_ALGO ec), OpenVPN uses ECDH automatically and the dh directive in the server config should be set to none. If you configured EC above, you can skip this step and set dh none in the server configuration.

Generate the tls-crypt Key

The tls-crypt key adds an additional layer of authentication and encryption to the TLS control channel. It prevents unauthorized clients from even initiating a TLS handshake with your server, which mitigates denial-of-service attacks and port scanning.

sh
openvpn --genkey tls-crypt-v2-server /usr/local/etc/openvpn/easy-rsa/pki/tls-crypt-v2.key

Alternatively, for the simpler tls-crypt (v1) approach that uses a single static key shared by all clients:

sh
openvpn --genkey secret /usr/local/etc/openvpn/easy-rsa/pki/tls-crypt.key

This guide uses tls-crypt (v1) for simplicity. For large deployments with many clients, tls-crypt-v2 is preferred because each client gets a unique key derived from the server key, allowing per-client revocation of the TLS layer.

Generate a Client Certificate

Generate a certificate for your first client. Replace client1 with a meaningful name:

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

This creates:

  • pki/issued/client1.crt -- The client certificate.
  • pki/private/client1.key -- The client private key.

Repeat this step for each client that needs VPN access.

Step 3: Server Configuration

Create the server configuration directory and file:

sh
mkdir -p /usr/local/etc/openvpn

Write the server configuration:

sh
cat > /usr/local/etc/openvpn/server.conf << 'EOF' # ---- Network ---- port 1194 proto udp dev tun # ---- PKI ---- ca /usr/local/etc/openvpn/easy-rsa/pki/ca.crt cert /usr/local/etc/openvpn/easy-rsa/pki/issued/server.crt key /usr/local/etc/openvpn/easy-rsa/pki/private/server.key dh none # ---- TLS Hardening ---- tls-crypt /usr/local/etc/openvpn/easy-rsa/pki/tls-crypt.key tls-version-min 1.2 auth SHA384 tls-cipher TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384 data-ciphers AES-256-GCM:AES-128-GCM:CHACHA20-POLY1305 # ---- Tunnel ---- server 10.8.0.0 255.255.255.0 topology subnet ifconfig-pool-persist /var/log/openvpn/ipp.txt # ---- Routing ---- push "redirect-gateway def1 bypass-dhcp" push "dhcp-option DNS 1.1.1.1" push "dhcp-option DNS 1.0.0.1" # ---- Connection ---- keepalive 10 120 persist-key persist-tun # ---- Privileges ---- user nobody group nobody # ---- Logging ---- status /var/log/openvpn/openvpn-status.log log-append /var/log/openvpn/openvpn.log verb 3 mute 20 # ---- Performance ---- sndbuf 0 rcvbuf 0 # ---- Client Management ---- max-clients 50 crl-verify /usr/local/etc/openvpn/easy-rsa/pki/crl.pem explicit-exit-notify 1 EOF

Configuration Explained

Here is what each section does:

Network. Listens on UDP port 1194 using a TUN (routed) device. UDP is strongly preferred over TCP because TCP-over-TCP causes severe performance problems due to retransmission cascading.

PKI. Points to the CA certificate, server certificate, server key, and sets dh none because we are using EC keys for ECDH key exchange.

TLS Hardening. Covered in detail later in this guide. The key points: tls-crypt wraps the control channel, tls-version-min 1.2 rejects outdated TLS, and data-ciphers restricts the data channel to AEAD ciphers.

Tunnel. Allocates the 10.8.0.0/24 subnet to VPN clients. The server takes 10.8.0.1, and clients are assigned addresses from the rest of the range. topology subnet is the modern recommended setting.

Routing. push "redirect-gateway def1 bypass-dhcp" tells clients to route all internet traffic through the VPN (full tunnel). DNS is pushed as Cloudflare's resolver. For split-tunnel setups, see the routing section later.

Privileges. After initialization, OpenVPN drops to the nobody user. This limits the impact of any potential vulnerability.

CRL. crl-verify checks every connecting client against the certificate revocation list. If a client's certificate has been revoked, the connection is refused.

Create the log directory:

sh
mkdir -p /var/log/openvpn

Generate an initial CRL so OpenVPN does not fail on startup:

sh
cd /usr/local/etc/openvpn/easy-rsa ./easyrsa gen-crl

Step 4: Enable IP Forwarding and NAT

For the VPN to route traffic between clients and the internet, the FreeBSD server must forward IP packets and perform NAT.

Enable IP Forwarding

sh
sysrc gateway_enable="YES" sysctl net.inet.ip.forwarding=1

The sysrc command makes the setting persistent across reboots. The sysctl command activates it immediately without a reboot.

Configure PF Firewall Rules

If you are using PF (the recommended firewall on FreeBSD), add rules for OpenVPN. For a comprehensive PF guide, see PF Firewall on FreeBSD.

Edit /etc/pf.conf:

shell
# ---- Macros ---- ext_if = "em0" vpn_if = "tun0" vpn_net = "10.8.0.0/24" # ---- Options ---- set skip on lo0 set block-policy drop set loginterface $ext_if # ---- NAT ---- nat on $ext_if from $vpn_net to any -> ($ext_if) # ---- Filter Rules ---- block in all pass out all keep state # Allow SSH pass in on $ext_if proto tcp from any to ($ext_if) port 22 keep state # Allow OpenVPN pass in on $ext_if proto udp from any to ($ext_if) port 1194 keep state # Allow all traffic on the VPN interface pass in on $vpn_if from $vpn_net to any keep state # Allow traffic from VPN to reach the internet via ext_if pass out on $ext_if from $vpn_net to any keep state

Replace em0 with your actual external network interface name. Find it with:

sh
ifconfig | grep "^[a-z]" | cut -d: -f1

Enable PF and load the rules:

sh
sysrc pf_enable="YES" pfctl -f /etc/pf.conf pfctl -e

The NAT rule is critical: it rewrites the source address of VPN client packets from 10.8.0.0/24 to the server's external IP so they can reach the internet and return correctly.

Step 5: Enable and Start OpenVPN

Enable OpenVPN in /etc/rc.conf:

sh
sysrc openvpn_enable="YES" sysrc openvpn_configfile="/usr/local/etc/openvpn/server.conf"

Start the service:

sh
service openvpn start

Verify it is running:

sh
service openvpn status

Check that the TUN interface is up:

sh
ifconfig tun0

You should see the 10.8.0.1 address assigned to the tun0 interface. If anything went wrong, check the log:

sh
tail -50 /var/log/openvpn/openvpn.log

Step 6: Client Configuration

The cleanest approach is to create a single .ovpn file that embeds all certificates and keys. This file can be imported directly into any OpenVPN client.

Build the .ovpn File

Create a script to assemble client configurations:

sh
cat > /usr/local/etc/openvpn/build-client.sh << 'SCRIPT' #!/bin/sh # Usage: ./build-client.sh client_name server_ip CLIENT=$1 SERVER=$2 PKI="/usr/local/etc/openvpn/easy-rsa/pki" OUTPUT="/usr/local/etc/openvpn/clients/${CLIENT}.ovpn" mkdir -p /usr/local/etc/openvpn/clients cat > "$OUTPUT" << EOF client dev tun proto udp remote ${SERVER} 1194 resolv-retry infinite nobind persist-key persist-tun remote-cert-tls server auth SHA384 data-ciphers AES-256-GCM:AES-128-GCM:CHACHA20-POLY1305 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> $(cat ${PKI}/tls-crypt.key) </tls-crypt> EOF chmod 600 "$OUTPUT" echo "Client config written to: $OUTPUT" SCRIPT chmod +x /usr/local/etc/openvpn/build-client.sh

Generate the configuration for client1:

sh
/usr/local/etc/openvpn/build-client.sh client1 YOUR_SERVER_IP

Replace YOUR_SERVER_IP with your server's public IP address. The resulting file at /usr/local/etc/openvpn/clients/client1.ovpn contains everything the client needs -- no separate certificate files to manage.

Transfer the .ovpn file to the client device using a secure method such as scp or an encrypted archive. Never transfer it over unencrypted channels.

Complete Client Configuration Reference

For reference, here is what the generated .ovpn file looks like structurally:

shell
client dev tun proto udp remote 203.0.113.10 1194 resolv-retry infinite nobind persist-key persist-tun remote-cert-tls server auth SHA384 data-ciphers AES-256-GCM:AES-128-GCM:CHACHA20-POLY1305 verb 3 <ca> -----BEGIN CERTIFICATE----- [CA certificate content] -----END CERTIFICATE----- </ca> <cert> -----BEGIN CERTIFICATE----- [Client certificate content] -----END CERTIFICATE----- </cert> <key> -----BEGIN EC PRIVATE KEY----- [Client private key content] -----END EC PRIVATE KEY----- </key> <tls-crypt> -----BEGIN OpenVPN Static key V1----- [tls-crypt key content] -----END OpenVPN Static key V1----- </tls-crypt>

Step 7: Client Setup by Platform

Windows

  1. Download and install OpenVPN Connect or the community OpenVPN GUI.
  2. Copy the .ovpn file to C:\Users\\OpenVPN\config\.
  3. Right-click the OpenVPN tray icon and select the profile to connect.

macOS

  1. Install Tunnelblick (free) or OpenVPN Connect.
  2. Double-click the .ovpn file. Tunnelblick will import it automatically.
  3. Click the Tunnelblick menu bar icon and select the profile to connect.

Linux

Install the OpenVPN client:

sh
# Debian/Ubuntu sudo apt install openvpn # Fedora sudo dnf install openvpn

Connect from the command line:

sh
sudo openvpn --config client1.ovpn

Or import the .ovpn file into NetworkManager for a GUI-based connection.

iOS and Android

  1. Install OpenVPN Connect from the App Store or Google Play.
  2. Transfer the .ovpn file to the device (email, AirDrop, cloud storage, or USB).
  3. Open the file with OpenVPN Connect. It imports the profile automatically.
  4. Tap the toggle to connect.

Step 8: Routing Options

Full Tunnel (Route All Traffic)

The server configuration above already pushes redirect-gateway def1 bypass-dhcp, which instructs clients to route all internet traffic through the VPN. This is the most secure option for untrusted networks (public WiFi, hotel connections).

Split Tunnel (Route Specific Subnets)

If you only want clients to reach specific internal networks through the VPN while using their local internet for everything else, replace the redirect-gateway line with specific route pushes:

shell
# Remove or comment out: # push "redirect-gateway def1 bypass-dhcp" # Push only specific routes: push "route 192.168.1.0 255.255.255.0" push "route 10.0.0.0 255.255.0.0"

With split tunneling, only traffic destined for 192.168.1.0/24 and 10.0.0.0/16 goes through the VPN. All other traffic uses the client's local gateway. This reduces bandwidth on the VPN server but does not protect the client's general internet traffic.

Per-Client Routing

For fine-grained control, create a client configuration directory:

sh
mkdir -p /usr/local/etc/openvpn/ccd

Add this to server.conf:

shell
client-config-dir /usr/local/etc/openvpn/ccd

Then create per-client files. For example, to give client1 a static IP and push a specific route only to that client:

sh
cat > /usr/local/etc/openvpn/ccd/client1 << 'EOF' ifconfig-push 10.8.0.10 255.255.255.0 push "route 172.16.0.0 255.255.0.0" EOF

Step 9: TLS Hardening

The default OpenVPN configuration works, but production deployments should harden the TLS layer. The server.conf above already includes these settings, but here is what each one does and why it matters.

tls-crypt

shell
tls-crypt /usr/local/etc/openvpn/easy-rsa/pki/tls-crypt.key

This wraps the entire TLS control channel in an additional layer of symmetric encryption using a pre-shared key. Benefits:

  • DDoS mitigation. Unauthenticated packets are dropped before reaching the TLS stack.
  • Port obfuscation. The OpenVPN handshake is indistinguishable from random data to passive observers.
  • Privacy. The TLS certificate exchange is encrypted, so an observer cannot fingerprint the CA or learn client identities.

tls-version-min

shell
tls-version-min 1.2

Rejects connections from clients attempting TLS 1.0 or 1.1. These protocol versions have known vulnerabilities and should not be used.

Data Channel Ciphers

shell
data-ciphers AES-256-GCM:AES-128-GCM:CHACHA20-POLY1305

Restricts the data channel to AEAD (Authenticated Encryption with Associated Data) ciphers only. These ciphers provide both confidentiality and integrity in a single operation, making them faster and more secure than older CBC-mode ciphers.

  • AES-256-GCM -- Maximum security. Best performance on CPUs with AES-NI hardware acceleration (most modern x86 processors).
  • AES-128-GCM -- Slightly faster, still considered secure for the foreseeable future.
  • CHACHA20-POLY1305 -- Software-optimized. Best performance on ARM devices and older CPUs without AES-NI.

TLS Cipher Suite

shell
tls-cipher TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384

This restricts the control channel TLS cipher suite. Since our certificates use EC keys (secp384r1), we use ECDHE for key exchange and ECDSA for authentication. If you are using RSA certificates instead, change this to:

shell
tls-cipher TLS-ECDHE-RSA-WITH-AES-256-GCM-SHA384

Authentication Digest

shell
auth SHA384

Sets the HMAC authentication digest for the control channel. SHA384 pairs naturally with secp384r1 EC keys. For RSA setups, SHA256 is a reasonable alternative.

Step 10: Revoking Certificates

When a client device is lost, stolen, or decommissioned, revoke its certificate immediately:

sh
cd /usr/local/etc/openvpn/easy-rsa ./easyrsa revoke client1 ./easyrsa gen-crl

The revoke command marks the certificate as invalid. The gen-crl command regenerates the CRL file that the server checks on every connection.

Since the server configuration already includes crl-verify, OpenVPN will reject connections from the revoked certificate as soon as the CRL is regenerated. No server restart is required -- OpenVPN re-reads the CRL file periodically.

To confirm the revocation:

sh
openssl crl -in /usr/local/etc/openvpn/easy-rsa/pki/crl.pem -text -noout | grep "Serial Number"

You should see the serial number of the revoked certificate listed.

CRL Expiration Warning

The CRL has a limited lifetime set by EASYRSA_CRL_DAYS (180 days in our configuration). If the CRL expires, OpenVPN will reject all client connections -- even valid ones. Set a calendar reminder to regenerate the CRL before it expires:

sh
./easyrsa gen-crl

Or automate it with a cron job:

sh
echo '0 3 1 * * root cd /usr/local/etc/openvpn/easy-rsa && ./easyrsa gen-crl' >> /etc/crontab

This regenerates the CRL on the first day of each month at 3:00 AM.

Step 11: Logging and Monitoring

Log Files

The server configuration writes two log files:

  • /var/log/openvpn/openvpn.log -- General log output including connection events, errors, and warnings.
  • /var/log/openvpn/openvpn-status.log -- A machine-readable status file updated every 60 seconds by default. It lists all connected clients with their assigned IPs, bytes transferred, and connection timestamps.

Viewing Connected Clients

sh
cat /var/log/openvpn/openvpn-status.log

Example output:

shell
OpenVPN CLIENT LIST Updated,2026-03-29 14:30:01 Common Name,Real Address,Bytes Received,Bytes Sent,Connected Since client1,198.51.100.25:54321,1523840,982016,2026-03-29 12:15:03

Log Rotation

To prevent log files from growing indefinitely, create a newsyslog entry:

sh
cat >> /etc/newsyslog.conf << 'EOF' /var/log/openvpn/openvpn.log nobody:nobody 640 12 1000 * J /var/run/openvpn.pid 1 EOF

This rotates the log when it reaches 1000 KB, keeps 12 rotated copies, compresses them with bzip2, and sends SIGUSR1 to OpenVPN to reopen the log file.

Real-Time Monitoring

Watch connections in real time:

sh
tail -f /var/log/openvpn/openvpn.log

For a higher-level view, OpenVPN supports a management interface. Add to server.conf:

shell
management 127.0.0.1 7505

Then connect with:

sh
telnet 127.0.0.1 7505

Useful management commands:

  • status -- List connected clients.
  • kill client1 -- Disconnect a specific client.
  • load-stats -- Show traffic statistics.
  • exit -- Close the management session.

Bind the management interface to 127.0.0.1 only. Exposing it to the network without authentication would be a serious security risk.

Troubleshooting

Server Will Not Start

Symptom: service openvpn start fails silently or the process exits immediately.

Check the log:

sh
cat /var/log/openvpn/openvpn.log

Common causes:

  • Missing CRL file. If crl-verify points to a file that does not exist, OpenVPN refuses to start. Generate it with ./easyrsa gen-crl.
  • Permission errors. The nobody user must be able to read the CRL. Check permissions: ls -la /usr/local/etc/openvpn/easy-rsa/pki/crl.pem. It should be readable by all (644).
  • Port already in use. Check with sockstat -l | grep 1194. If another process holds the port, stop it first.
  • Wrong key type. If you used EC keys but set dh to a file path instead of none, OpenVPN will fail. With EC keys, use dh none.

Client Connects But Cannot Reach the Internet

Check IP forwarding:

sh
sysctl net.inet.ip.forwarding

It must return 1. If it returns 0:

sh
sysctl net.inet.ip.forwarding=1

Check NAT rules:

sh
pfctl -sn

You should see a NAT rule translating 10.8.0.0/24 to the external interface. If missing, reload PF:

sh
pfctl -f /etc/pf.conf

Check PF filter rules:

sh
pfctl -sr

Ensure rules allow traffic from $vpn_net on $vpn_if and outbound on $ext_if.

TLS Handshake Failure

Symptom: Client log shows TLS Error: TLS key negotiation failed to occur within 60 seconds.

Common causes:

  • Firewall blocking UDP 1194. Test with nc -u -z YOUR_SERVER_IP 1194 from the client. If it times out, a firewall is blocking the connection.
  • Mismatched tls-crypt key. The key embedded in the client .ovpn must match the server's key exactly. Regenerate the client configuration.
  • Clock skew. TLS certificate validation requires roughly synchronized clocks. If server or client time is significantly off, certificates may appear expired. Check with date on both systems and install ntpd if needed.

Clients Cannot Reach Each Other

By default, client-to-client traffic is routed through the server only if you add:

shell
client-to-client

to server.conf. Without this directive, each client can reach the server but not other clients.

DNS Leaks

If clients connect successfully but DNS queries bypass the VPN, ensure the push "dhcp-option DNS" lines are present in the server config. On Windows, the TAP adapter may not override the system DNS. OpenVPN Connect 3.x handles this automatically; the older community GUI may require:

shell
push "block-outside-dns"

This directive is Windows-specific and forces all DNS queries through the VPN tunnel.

Slow Performance

  • Use UDP, not TCP. TCP transport causes TCP-over-TCP, where retransmissions cascade and throughput collapses. Switch to proto udp if you are using TCP.
  • Check MTU. If you see fragmentation or packet loss, try adding fragment 1400 and mssfix 1400 to both server and client configs.
  • Check cipher. AES-GCM with hardware AES-NI is fast. If the server CPU does not support AES-NI, switch to CHACHA20-POLY1305 which is faster in software. Check AES-NI support with dmesg | grep AES.

Frequently Asked Questions

Can I run OpenVPN and WireGuard on the same FreeBSD server?

Yes. They use different interfaces (tun for OpenVPN, wg for WireGuard) and can coexist as long as they use different IP subnets and ports. Assign 10.8.0.0/24 to OpenVPN and 10.9.0.0/24 to WireGuard, for example. Add PF rules for both.

Should I use TCP or UDP for OpenVPN?

UDP in almost all cases. TCP transport suffers from the TCP-over-TCP problem: when the outer TCP connection retransmits a packet, the inner TCP connections retransmit too, causing exponential performance degradation. Use TCP only as a last resort when UDP is blocked, such as on restrictive corporate networks.

How many clients can a single OpenVPN server handle?

It depends on the server hardware and throughput per client. A modest FreeBSD server (4 cores, 4 GB RAM) can typically handle 100-200 concurrent clients at moderate bandwidth. OpenVPN uses a single process by default, so CPU speed matters more than core count. For higher concurrency, run multiple OpenVPN instances on different ports and load-balance across them.

How do I add a new client after initial setup?

Generate a new certificate and build the client configuration:

sh
cd /usr/local/etc/openvpn/easy-rsa ./easyrsa gen-req client2 nopass ./easyrsa sign-req client client2 /usr/local/etc/openvpn/build-client.sh client2 YOUR_SERVER_IP

No server restart is required. The new client can connect immediately.

Is OpenVPN still secure in 2026?

Yes. With the TLS hardening described in this guide (TLS 1.2 minimum, AEAD ciphers, tls-crypt, EC certificates), OpenVPN provides strong security. The protocol has a long track record and continues to receive security updates. For a comparison of the security models, see WireGuard vs OpenVPN on FreeBSD.

How do I change the VPN subnet?

Edit server.conf and change the server directive:

shell
server 10.10.0.0 255.255.255.0

Update PF rules to match the new subnet. Update the vpn_net macro in /etc/pf.conf, reload PF with pfctl -f /etc/pf.conf, and restart OpenVPN with service openvpn restart.

What is the difference between tls-auth and tls-crypt?

Both add a pre-shared key layer to the TLS control channel. tls-auth only authenticates control channel packets (HMAC). tls-crypt both authenticates and encrypts them. Use tls-crypt -- it provides strictly more protection with no downside. tls-crypt-v2 is the newest option, allowing per-client keys.

What Next

You now have a production-ready OpenVPN server on FreeBSD with certificate-based authentication, TLS hardening, firewall integration, and NAT routing.

For further reading:

Get more FreeBSD guides

Weekly tutorials, security advisories, and package updates. No spam.