Skip to main contentSkip to navigation
[email protected]
Client AreaSupport
Hosting Mammoth
HostingMammothYour Data, Our Responsibility
Home
Solutions
Hosting Services
Store
Pricing
About
Blog
API
Contact

Stay Ahead of the Curve

Get the latest insights on cybersecurity, AI innovations, and enterprise data solutions delivered to your inbox.

Hosting Mammoth
HostingMammothEnterprise Solutions

Enterprise-grade data solutions. Hosting, recovery, cybersecurity, and AI-powered services for businesses worldwide.

[email protected]
Sun - Fri, 9:00am - 5:00pm

Services

  • Cloud Hosting
  • Data Recovery
  • Cybersecurity
  • Legal Support
  • MSP Services
  • Web Development
  • AI Services
  • Free Server Migration

Hosting

  • VPS Hosting (NVMe SSD)
  • VDS Hosting (NVMe)
  • Storage VPS (High SSD)
  • GPU Servers
  • Managed Services
  • Cloud Firewall
  • Load Balancer
  • One-Click Apps
  • n8n Hosting
  • Object Storage
  • FAQ

Company

  • Store
  • Pricing
  • About Us
  • Locations
  • Blog
  • Testimonials
  • Contact
  • Affiliate Program
  • White-Label
  • Terms of Service
  • Privacy Policy
  • Browser Cookies
  • SLA

Support

  • Client Area
  • Submit Ticket
  • Knowledge Base
  • Server Status
  • API Documentation

© 2026 Hosting Mammoth. All rights reserved.

Knowledge Base
Getting StartedAccount ManagementVPS HostingGPU ServersStorage VPSCloud FirewallLoad BalancerServer ManagementBilling & PaymentsSupport & TicketsAffiliate ProgramReseller ProgramMarketplace & Appsn8n HostingManaged ServicesServer MigrationAPI & DevelopersSecurityTroubleshootingGlossaryInstall Guides
  1. Home
  2. /
  3. Support
  4. /
  5. Install Guides
  6. /
  7. How To Install Openconnect Ubuntu
GUIDEInstall Guides

How to Install OpenConnect Server (ocserv) on Ubuntu 24.04 VPS: Cisco AnyConnect-Compatible SSL VPN

27 min read

How to Install OpenConnect Server (ocserv) on Ubuntu 24.04 VPS: Cisco AnyConnect-Compatible SSL VPN

Running your own SSL VPN on port 443 is the most reliable way to stay connected from hotel Wi-Fi, conference networks, airport lounges, and restrictive corporate firewalls. This guide walks you through installing OpenConnect Server (ocserv) on an Ubuntu 24.04 VPS from scratch: fresh install, Let's Encrypt certificate, TCP and UDP on port 443, DTLS acceleration, TOTP-based two-factor authentication, split-tunnel and full-tunnel routing, kernel forwarding, nftables NAT, and client connection tests with both the open-source OpenConnect client and the official Cisco Secure Client (AnyConnect).

Looking for an easy starting point? A CloudCore Starter VPS is more than enough horsepower to run ocserv for a small team, and you get a clean static IP that you fully control.

Table of Contents

  • What is OpenConnect / ocserv?
  • Why Self-Host a Port 443 SSL VPN?
  • Prerequisites
  • Step 1: Update Ubuntu and Harden the Server
  • Step 2: Install ocserv
  • Step 3: Obtain a Let's Encrypt Certificate
  • Step 4: Configure /etc/ocserv/ocserv.conf
  • Step 5: Enable IP Forwarding and NAT with nftables
  • Step 6: Create VPN Users
  • Step 7: Start and Verify ocserv
  • Step 8: Connect from an OpenConnect Client
  • Step 9: Connect from Cisco AnyConnect
  • Step 10: Enable TOTP 2FA
  • Step 11: PAM, RADIUS, and LDAP Authentication
  • Step 12: Split-Tunnel vs Full-Tunnel
  • Post-Install Tuning
  • Troubleshooting
  • FAQ
  • Next Steps
  • What is OpenConnect / ocserv?

    ocserv is the OpenConnect VPN server: a free, GPL-licensed SSL/TLS VPN server originally written as a compatible reimplementation of the Cisco AnyConnect protocol. It speaks the same HTTPS-over-TLS handshake and DTLS data channel that Cisco's commercial AnyConnect Secure Mobility Client uses, which means you can connect to it from any official Cisco client or from the open-source OpenConnect client on practically every desktop and mobile platform.

    Under the hood, ocserv listens on a single TCP port (usually 443) and negotiates the control channel over HTTPS. Once the session is established, it opens a parallel UDP channel (also on 443 in this guide) using DTLS for the bulk data path. DTLS avoids the TCP-over-TCP "meltdown" problem where a VPN tunnel stalls because both the outer and inner protocols retransmit simultaneously. If UDP is blocked, ocserv transparently falls back to TLS over TCP so the tunnel still works, just with lower peak throughput.

    Typical deployments include: remote-worker access to an internal subnet, secure browsing from hostile Wi-Fi networks, bypassing captive portals and restrictive firewalls that whitelist only HTTPS, provisioning managed-device VPN profiles for iPhones and Androids using the ubiquitous AnyConnect client, and acting as a drop-in open-source replacement for legacy Cisco ASA head-ends.

    Why Self-Host a Port 443 SSL VPN?

    Many VPN protocols work great on a home network but fall apart the moment you step into a hotel, coworking space, airport lounge, client site, or country with aggressive traffic filtering. Running ocserv on TCP 443 solves this:

    • Port 443 is never blocked. If HTTPS were blocked, the network would be useless. ocserv rides the exact same TLS envelope that every website on the planet uses, so deep packet inspection sees what looks like normal encrypted web traffic.
    • True AnyConnect client compatibility. Cisco Secure Client is pre-approved by corporate IT departments, notarized by Apple, signed for Windows, and available in both the App Store and Google Play. You can onboard users without installing anything "weird."
    • DTLS performance, TLS reliability. You get the throughput of UDP-based VPNs when the network allows it, and graceful fallback to TCP when it doesn't.
    • Built-in user management and 2FA. ocserv ships with ocpasswd, supports PAM, RADIUS, LDAP, and certificates out of the box, and natively integrates OATH/TOTP so you can require Google Authenticator for every login.
    • Fine-grained routing. Push specific routes, DNS servers, and split-DNS domains per user or group. Force full-tunnel for some users, split-tunnel for others, and use the same server for both.
    • Full control of exit IP. A self-hosted VPN gives you a stable, dedicated static IP that you can whitelist in internal dashboards, banking portals, cloud consoles, and geofenced services.

    Why a dedicated VPS beats a "free" VPN

    FactorCommercial VPNSelf-hosted ocserv on VPS
    Port 443 availabilityVaries by providerAlways available
    AnyConnect compatibilityAlmost neverYes (native)
    Logs policy"No-log" (trust us)You own the logs
    Static, dedicated IPUsually extra costIncluded with VPS
    Custom routes / split-DNSRarely configurableFully configurable
    Typical cost for 1 teamEUR 8-15/user/monthEUR 7.99/month flat
    For teams of 3-50 users, a single CloudCore Starter VPS costs less than one seat of most commercial VPN services and gives you complete operational control.

    Prerequisites

    Before starting, make sure you have:

    • A VPS running Ubuntu 24.04 LTS with root or sudo access.
    • A public IPv4 address that is not already serving HTTPS on port 443 (ocserv will take that port).
    • A domain or subdomain pointing to the VPS public IP (for example, vpn.example.com). An A record pointing to your VPS is sufficient.
    • DNS propagated before you run certbot. Verify with dig +short vpn.example.com.
    • SSH access to the server.
    • Ports 443/tcp and 443/udp reachable from the internet (open them in the provider's firewall panel if applicable).
    Recommended Plan: CloudCore Starter
    >
    For a VPN serving a small team (up to roughly 30 concurrent users), we recommend the CloudCore Starter plan:
    >
    - 2 vCPU cores
    - 4 GB RAM
    - 50 GB NVMe SSD
    - Unmetered bandwidth
    - Static, dedicated IPv4
    >
    ocserv is very lightweight and spends most of its CPU time on TLS and DTLS crypto, so even the smallest plan handles dozens of concurrent users comfortably. If you expect more than 100 simultaneous clients, step up one tier for additional vCPUs.

    Connect to the VPS:

    bash
    ssh root@your-server-ip

    Step 1: Update Ubuntu and Harden the Server

    Start with a clean, patched system:

    bash
    sudo apt update && sudo apt upgrade -y
    sudo apt install -y curl ca-certificates ufw gnupg lsb-release

    Set a proper hostname so certificate issuance and logs make sense:

    bash
    sudo hostnamectl set-hostname vpn.example.com

    If the kernel was updated, reboot:

    bash
    sudo reboot

    Reconnect via SSH after a minute.

    Step 2: Install ocserv

    ocserv is packaged directly in the Ubuntu 24.04 repositories, so you do not need a third-party PPA.

    bash
    sudo apt install -y ocserv gnutls-bin iptables

    This installs:

    • ocserv -- the server binary and default systemd unit.
    • gnutls-bin -- provides certtool for generating test certificates and ocpasswd for user management.
    • iptables -- used as a fallback for some ocserv scripts even when you run nftables as your primary firewall.
    Confirm the version and that the systemd service exists:

    bash
    ocserv --version
    systemctl list-unit-files | grep ocserv

    Expected output (abbreviated):

    text
    OpenConnect VPN Server 1.3.0

    ocserv.service enabled enabled

    Do not start the service yet -- the default configuration ships with placeholder certificates and will fail to authenticate real clients. We will configure everything before the first start.

    Step 3: Obtain a Let's Encrypt Certificate

    ocserv runs fine with self-signed certificates for lab use, but self-signed certs trigger scary warnings in the Cisco AnyConnect client and flat-out refuse to connect on strict mobile clients. A free Let's Encrypt certificate removes that friction.

    Install certbot:

    bash
    sudo apt install -y certbot

    Because certbot's standalone mode needs to bind port 80 briefly, and ocserv is not running yet, you can safely request the certificate right now:

    bash
    sudo certbot certonly --standalone \
      --preferred-challenges http \
      --agree-tos \
      --email [email protected] \
      -d vpn.example.com

    Expected output:

    text
    Successfully received certificate.
    Certificate is saved at: /etc/letsencrypt/live/vpn.example.com/fullchain.pem
    Key is saved at:         /etc/letsencrypt/live/vpn.example.com/privkey.pem
    This certificate expires on 2026-07-15.

    Automatic renewal hook

    Let's Encrypt certificates last 90 days. Configure a renewal hook so ocserv picks up the new certificate automatically:

    bash
    sudo mkdir -p /etc/letsencrypt/renewal-hooks/deploy
    sudo tee /etc/letsencrypt/renewal-hooks/deploy/ocserv.sh > /dev/null <<'EOF'
    #!/bin/sh
    systemctl restart ocserv
    EOF
    sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/ocserv.sh

    Certbot already installs a systemd timer (certbot.timer) that runs twice daily. Verify it is enabled:

    bash
    systemctl list-timers | grep certbot

    Step 4: Configure /etc/ocserv/ocserv.conf

    The default config at /etc/ocserv/ocserv.conf is heavily commented and over 700 lines long. Rather than edit in place, make a backup and write a clean, production-ready configuration.

    bash
    sudo cp /etc/ocserv/ocserv.conf /etc/ocserv/ocserv.conf.orig
    sudo tee /etc/ocserv/ocserv.conf > /dev/null <<'EOF'
    

    --- ocserv 1.3.x production config ---

    Authentication: local password file created with ocpasswd

    auth = "plain[passwd=/etc/ocserv/ocpasswd]"

    Listen on port 443 for both the TLS control channel (TCP)

    and the DTLS data channel (UDP). Clients fall back to TCP

    automatically if UDP is filtered by the network.

    tcp-port = 443 udp-port = 443

    TLS certificate (Let's Encrypt)

    server-cert = /etc/letsencrypt/live/vpn.example.com/fullchain.pem server-key = /etc/letsencrypt/live/vpn.example.com/privkey.pem

    Connection limits

    max-clients = 128 max-same-clients = 4 keepalive = 32400 dpd = 90 mobile-dpd = 1800

    Run as an unprivileged user after binding port 443

    run-as-user = nobody run-as-group = daemon

    Process isolation

    isolate-workers = true

    Socket for the occtl management tool

    socket-file = /run/ocserv.sock

    PID

    pid-file = /run/ocserv.pid

    Cookie & auth lifetimes

    auth-timeout = 240 cookie-timeout = 300 rekey-time = 172800 rekey-method = ssl

    Compression off (security best practice, CRIME attack mitigation)

    compression = false

    TLS hardening (modern profile, no TLS 1.0/1.1, no SSLv3)

    tls-priorities = "NORMAL:%SERVER_PRECEDENCE:%COMPAT:-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1:-ARCFOUR-128"

    MTU — 1400 is a safe default over most internet paths.

    Clients fragment above this and you avoid path MTU discovery black holes.

    try-mtu-discovery = true default-mtu = 1400

    Hostname advertised to clients

    default-domain = vpn.example.com

    IPv4 pool handed out to clients (must not collide with your LAN)

    ipv4-network = 10.10.10.0 ipv4-netmask = 255.255.255.0

    DNS pushed to clients (Cloudflare + Quad9)

    dns = 1.1.1.1 dns = 9.9.9.9

    Uncomment to force full-tunnel (all traffic via VPN).

    Commented = split-tunnel; add explicit routes below.

    route = default

    Example split-tunnel routes (clients only send these through the VPN):

    route = 10.0.0.0/8 route = 172.16.0.0/12 route = 192.168.0.0/16

    Do not route these subnets through the VPN (e.g. client's LAN)

    no-route = 192.168.1.0/24

    Ban brute-force attackers

    max-ban-score = 80 ban-points-wrong-password = 10 ban-points-connection = 1 ban-points-kkdcp = 1 min-reauth-time = 300 ban-reset-time = 1200

    Client stats & logging (disable to reduce disk I/O if not needed)

    stats-report-time = 360 log-level = 1

    Optional: per-user and per-group config

    config-per-user = /etc/ocserv/config-per-user/ config-per-group = /etc/ocserv/config-per-group/ EOF

    Replace vpn.example.com with your actual hostname. The important pieces:

    • tcp-port = 443 + udp-port = 443 -- ocserv binds TLS on TCP 443 and DTLS on UDP 443. This is what makes the VPN survive restrictive firewalls.
    • server-cert / server-key -- paths to the Let's Encrypt files. Do not symlink; point directly.
    • ipv4-network / ipv4-netmask -- the virtual IP pool handed out to clients. Pick something that does not overlap any LAN you plan to connect from.
    • route -- each line is a subnet pushed to the client's routing table. Without any route lines the client gets no routes at all. With route = default it becomes a full-tunnel VPN.
    • default-mtu = 1400 -- starting MTU. ocserv also negotiates path MTU dynamically when try-mtu-discovery = true.
    • tls-priorities -- explicitly disables TLS 1.0/1.1 and RC4. This matches Mozilla's modern profile.
    Create the per-user and per-group directories (referenced above):

    bash
    sudo mkdir -p /etc/ocserv/config-per-user /etc/ocserv/config-per-group

    Step 5: Enable IP Forwarding and NAT with nftables

    Without IP forwarding and NAT, your VPN clients will connect but have no internet access.

    Enable kernel IP forwarding

    bash
    echo 'net.ipv4.ip_forward = 1' | sudo tee /etc/sysctl.d/99-ocserv.conf
    echo 'net.ipv6.conf.all.forwarding = 1' | sudo tee -a /etc/sysctl.d/99-ocserv.conf
    sudo sysctl --system

    Confirm:

    bash
    sysctl net.ipv4.ip_forward

    Expected output:

    text
    net.ipv4.ip_forward = 1

    Configure nftables for NAT and firewall

    Ubuntu 24.04 ships with nftables by default. Identify your primary outbound interface:

    bash
    ip -br route | grep default

    Expected output (the interface name after dev):

    text
    default via 203.0.113.1 dev eth0 proto static

    Note eth0 (or ens3, enp0s3, etc.) for your system. Write the nftables configuration:

    bash
    sudo tee /etc/nftables.conf > /dev/null <<'EOF'
    #!/usr/sbin/nft -f
    flush ruleset

    table inet filter { chain input { type filter hook input priority 0; policy drop;

    ct state established,related accept iif "lo" accept

    # SSH (change the port if you moved SSH) tcp dport 22 accept

    # ocserv on 443 — both TCP (TLS) and UDP (DTLS) tcp dport 443 accept udp dport 443 accept

    # ICMP / ping icmp type echo-request accept icmpv6 type { echo-request, nd-neighbor-solicit, nd-router-advert, nd-neighbor-advert } accept }

    chain forward { type filter hook forward priority 0; policy drop;

    ct state established,related accept

    # Allow VPN clients out iifname "vpns+" accept oifname "vpns+" ct state new accept }

    chain output { type filter hook output priority 0; policy accept; } }

    table ip nat { chain postrouting { type nat hook postrouting priority 100;

    # NAT the VPN pool behind the server's public IP. # Change eth0 to match your primary interface. ip saddr 10.10.10.0/24 oifname "eth0" masquerade } } EOF

    Replace eth0 if your interface is named differently. Then apply and enable:

    bash
    sudo nft -c -f /etc/nftables.conf   # syntax check
    sudo systemctl enable --now nftables
    sudo systemctl restart nftables

    Verify:

    bash
    sudo nft list ruleset | head -40

    ocserv creates virtual interfaces named vpns0, vpns1, etc. -- the vpns+ wildcard in the forward chain matches all of them.

    Step 6: Create VPN Users

    With auth = "plain[passwd=/etc/ocserv/ocpasswd]" in the config, user accounts live in a flat file managed by the ocpasswd utility.

    Create the password file and add a user:

    bash
    sudo touch /etc/ocserv/ocpasswd
    sudo chmod 600 /etc/ocserv/ocpasswd
    sudo ocpasswd -c /etc/ocserv/ocpasswd alice

    You will be prompted for a password twice. To add a second user, omit the -c flag (which means "create new file"):

    bash
    sudo ocpasswd /etc/ocserv/ocpasswd bob

    To list users:

    bash
    sudo cat /etc/ocserv/ocpasswd

    Expected output:

    text
    alice:*:$5$rounds=...
    bob:*:$5$rounds=...

    To disable a user without deleting them:

    bash
    sudo ocpasswd -l /etc/ocserv/ocpasswd alice   # lock
    sudo ocpasswd -u /etc/ocserv/ocpasswd alice   # unlock

    To delete:

    bash
    sudo ocpasswd -d /etc/ocserv/ocpasswd alice

    Per-user configuration

    You can override any config setting per user. For example, to force full-tunnel only for alice while other users keep split-tunnel, create:

    bash
    sudo tee /etc/ocserv/config-per-user/alice > /dev/null <<'EOF'
    route = default
    dns = 1.1.1.1
    EOF

    Step 7: Start and Verify ocserv

    Test the configuration first:

    bash
    sudo ocserv -f -c /etc/ocserv/ocserv.conf

    The -f flag runs in the foreground so you can see startup errors. If it prints listening on ... without errors, press Ctrl+C and start it as a service:

    bash
    sudo systemctl enable --now ocserv
    sudo systemctl status ocserv

    Expected output:

    text
    ● ocserv.service - OpenConnect SSL VPN server
         Loaded: loaded (/lib/systemd/system/ocserv.service; enabled; preset: enabled)
         Active: active (running) since Wed 2026-04-16 10:00:00 UTC; 5s ago
       Main PID: 12345 (ocserv-main)
          Tasks: 2
         Memory: 8.2M

    Check that the ports are listening:

    bash
    sudo ss -tulpn | grep 443

    Expected output:

    text
    tcp   LISTEN 0  128   0.0.0.0:443   0.0.0.0:*   users:(("ocserv-main",pid=12345))
    udp   UNCONN 0  0     0.0.0.0:443   0.0.0.0:*   users:(("ocserv-main",pid=12345))

    Both TCP and UDP on 443 are correct. The management tool occtl gives you real-time insight:

    bash
    sudo occtl show status
    sudo occtl show users
    sudo occtl show ip bans

    Step 8: Connect from an OpenConnect Client

    On any Linux, macOS, or Windows machine, install the OpenConnect client:

    Ubuntu / Debian:

    bash
    sudo apt install -y openconnect

    macOS (Homebrew):

    bash
    brew install openconnect

    Windows: Download the openconnect-gui installer.

    Connect from the command line:

    bash
    sudo openconnect --protocol=anyconnect --user=alice vpn.example.com

    The client negotiates TLS, you enter Alice's password, it switches to DTLS over UDP if reachable, and assigns a virtual IP from the pool.

    Expected output (abbreviated):

    text
    POST https://vpn.example.com/
    Connected to HTTPS on vpn.example.com with ciphersuite (TLS1.3)-(ECDHE-SECP256R1)-(CHACHA20-POLY1305)
    XML POST enabled
    Please enter your username and password.
    Username: alice
    Password:
    Got CONNECT response: HTTP/1.1 200 CONNECTED
    CSTP connected. DPD 90, Keepalive 32400
    Connected tun0 as 10.10.10.2, using SSL + DTLSv1.2

    Verify you have a VPN-assigned address and that the server is reachable from inside the tunnel:

    bash
    ip addr show tun0
    ping -c 3 10.10.10.1
    curl ifconfig.me

    curl ifconfig.me returns the VPS's public IP when full-tunnel is on, or your own ISP's IP in split-tunnel mode.

    GUI client (NetworkManager)

    On GNOME, install the plugin:

    bash
    sudo apt install -y network-manager-openconnect-gnome

    Add a connection in Settings -> Network -> VPN -> + and pick Cisco AnyConnect Compatible VPN (openconnect). Fill in:

    • Gateway: vpn.example.com
    • CA Certificate: leave blank (Let's Encrypt is in the system trust store)

    Step 9: Connect from Cisco AnyConnect

    This is where ocserv really shines -- you get full compatibility with the official Cisco Secure Client (AnyConnect) on every OS Cisco supports.

  • Install Cisco Secure Client from your employer, the Cisco website, or the App Store / Google Play.
  • Launch the client. In the "Connect to" field type: vpn.example.com
  • Click Connect. It fetches the login portal, which is served directly by ocserv.
  • Enter the username and password you created with ocpasswd (for example, alice).
  • The client establishes TLS, negotiates DTLS, and brings up the utun (macOS/iOS) or Ethernet (Windows) virtual adapter.
  • Because you issued a real Let's Encrypt certificate in Step 3, AnyConnect trusts the server without any manual CA import or security override. This is critical for iOS and Android, which refuse to save VPN profiles with self-signed certificates.

    Pushing a preconfigured profile

    For managed device fleets, you can pre-seed the connection by distributing a profile.xml through MDM (Jamf, Intune, Workspace ONE). Minimal example:

    xml
    <?xml version="1.0" encoding="UTF-8"?>
    <AnyConnectProfile xmlns="http://schemas.xmlsoap.org/encoding/">
      <ServerList>
        <HostEntry>
          <HostName>Company VPN</HostName>
          <HostAddress>vpn.example.com</HostAddress>
        </HostEntry>
      </ServerList>
    </AnyConnectProfile>

    Step 10: Enable TOTP 2FA

    Passwords alone are not enough for anything internet-facing. ocserv's plain backend supports TOTP via the gnutls-bin / oath-toolkit integration.

    Install oath-toolkit:

    bash
    sudo apt install -y oath-toolkit libpam-oath

    Generate a TOTP secret for user Alice:

    bash
    head -c 20 /dev/urandom | xxd -p -c 20

    Expected output (example):

    text
    9f1a2b3c4d5e6f708192a3b4c5d6e7f809112233

    Save that hex secret and convert it to Base32 (what Google Authenticator accepts):

    bash
    echo -n "9f1a2b3c4d5e6f708192a3b4c5d6e7f809112233" | xxd -r -p | base32

    Add Alice to /etc/users.oath:

    bash
    sudo tee -a /etc/users.oath > /dev/null <<'EOF'
    HOTP/T30/6 alice - 9f1a2b3c4d5e6f708192a3b4c5d6e7f809112233
    EOF
    sudo chmod 600 /etc/users.oath
    sudo chown root:root /etc/users.oath

    Switch ocserv from plain passwd auth to stacked auth -- password and TOTP:

    bash
    sudo sed -i 's|^auth = .*|auth = "plain[passwd=/etc/ocserv/ocpasswd,otp=/etc/users.oath]"|' /etc/ocserv/ocserv.conf
    sudo systemctl restart ocserv

    Now when Alice connects, the client prompts first for her password, then for the 6-digit code from her authenticator app. Add the Base32 secret to her Google Authenticator, Authy, or 1Password, and you have full 2FA.

    Step 11: PAM, RADIUS, and LDAP Authentication

    For larger deployments with existing identity, swap the plain backend for one of these.

    PAM (use system Unix users)

    text
    auth = "pam"

    Add ocserv to PAM's config:

    bash
    sudo tee /etc/pam.d/ocserv > /dev/null <<'EOF'
    auth    required pam_unix.so
    account required pam_unix.so
    EOF

    Add system users with adduser alice --shell /usr/sbin/nologin.

    RADIUS (FreeRADIUS, Duo Security, Microsoft NPS)

    Install the ocserv RADIUS support:

    bash
    sudo apt install -y ocserv-radius

    In ocserv.conf:

    text
    auth = "radius[config=/etc/radiusclient/radiusclient.conf,groupconfig=true]"
    acct = "radius[config=/etc/radiusclient/radiusclient.conf]"

    Edit /etc/radiusclient/servers and /etc/radiusclient/radiusclient.conf to point at your RADIUS server. This is how you integrate with Duo Security, Microsoft NPS, Okta RADIUS, or JumpCloud.

    LDAP / Active Directory (via SSSD or PAM)

    Install and configure SSSD against Active Directory, then use auth = "pam". SSSD handles the LDAP or AD lookup transparently through PAM's pam_sss.so module. This is the cleanest approach for AD-joined servers.

    You can stack multiple backends:

    text
    auth = "pam"
    auth = "certificate"

    With both lines present, ocserv accepts either a valid password or a valid client certificate.

    Step 12: Split-Tunnel vs Full-Tunnel

    The difference comes down to one config directive.

    Split-tunnel (default in our config)

    Only the routes you list are pushed to clients; the rest of the client's traffic uses its normal internet connection.

    text
    # In ocserv.conf
    route = 10.0.0.0/8
    route = 172.16.0.0/12
    route = 192.168.0.0/16
    no-route = 192.168.1.0/24   # exclude client LAN

    Use when: you only need clients to reach internal resources (databases, admin panels, monitoring) and want their general web traffic to stay local.

    Full-tunnel

    Every packet from the client flows through the VPN.

    text
    # In ocserv.conf
    route = default

    Use when: you want to protect users on hostile Wi-Fi, enforce egress through a known static IP, or bypass ISP-level filtering.

    You can mix modes: leave the global config in split-tunnel and override per user:

    bash
    sudo tee /etc/ocserv/config-per-user/ceo > /dev/null <<'EOF'
    route = default
    EOF

    The CEO now gets full-tunnel; everyone else stays split-tunnel.

    DNS in split-tunnel: split-DNS

    When split-tunneling, you usually want internal hostnames (e.g. intranet.example.com) resolved by the VPN's DNS and everything else by the client's normal DNS. Use split-dns:

    text
    split-dns = example.com
    split-dns = internal.lan

    Only queries for those domains go through the tunneled DNS; everything else uses the client's normal resolver.

    Post-Install Tuning

    MTU tuning

    If users on certain networks report slow transfers or stalled downloads, lower the MTU:

    text
    default-mtu = 1360

    Mobile carriers (especially on IPv6 transition networks) often have effective MTUs below 1400. Start at 1400, drop by 40 at a time if needed.

    DTLS tuning

    DTLS dramatically improves throughput for large transfers. If clients keep falling back to TCP-only mode, double-check:

    • UDP 443 is open on both the VPS firewall and the provider's network firewall.
    • udp-port = 443 is present in ocserv.conf.
    • The client's network isn't rate-limiting or reordering UDP (common on cheap hotel Wi-Fi).

    Logging and monitoring

    ocserv logs to the journal by default:

    bash
    sudo journalctl -u ocserv -f

    To see live session data:

    bash
    sudo occtl show users
    sudo occtl show sessions all
    sudo occtl show iroutes

    To disconnect an abusive user:

    bash
    sudo occtl disconnect user alice

    Fail2ban

    Add a basic fail2ban jail to block IPs hammering the login page:

    bash
    sudo apt install -y fail2ban
    sudo tee /etc/fail2ban/jail.d/ocserv.local > /dev/null <<'EOF'
    [ocserv]
    enabled  = true
    port     = 443
    filter   = ocserv
    logpath  = /var/log/syslog
    maxretry = 5
    findtime = 600
    bantime  = 3600
    EOF

    ocserv already has built-in brute-force protection through its max-ban-score settings, but fail2ban operates at the firewall layer for defense in depth.

    Troubleshooting

    ProblemCauseSolution
    Bad file descriptor on startupCertificate files unreadable by nobodysudo chmod -R 755 /etc/letsencrypt/live /etc/letsencrypt/archive
    Clients connect but have no internetIP forwarding off or NAT missingsudo sysctl net.ipv4.ip_forward must be 1; verify nft list table ip nat
    AnyConnect on iOS rejects the serverSelf-signed cert, or cert does not cover hostnameUse a real Let's Encrypt certificate; the hostname in the client must exactly match the certificate CN/SAN
    Tunnel works but is very slowFalling back to TCP because UDP 443 is blockedCheck with occtl show users — using: TLS means no DTLS; verify UDP 443 is open end to end
    error: Could not open tunnel devicetun module not loaded (rare on modern kernels)sudo modprobe tun and add tun to /etc/modules
    Connects, drops after a few minutesDPD misconfig or NAT timeout on carrierLower dpd to 60 and mobile-dpd to 900
    MTU errors / partial page loadsPath MTU discovery brokenSet default-mtu = 1360 and restart ocserv
    Error: failed to verify peer on certbot renewalPort 80 still bound by nftables dropAllow 80/tcp temporarily or use DNS-01 challenge
    TOTP always failsSystem clock drift > 30sInstall and enable chrony or systemd-timesyncd

    Useful debug commands

    bash
    sudo journalctl -u ocserv -n 100 --no-pager
    sudo occtl show status
    sudo occtl show users
    sudo nft list ruleset
    sudo ss -tulpn | grep 443

    FAQ

    Is OpenConnect ocserv compatible with the official Cisco AnyConnect / Secure Client?

    Yes. ocserv implements the AnyConnect protocol faithfully enough that the official Cisco Secure Client connects, authenticates, and passes traffic without any modification. This is useful in environments where only "approved" VPN clients are allowed on endpoints. You can also use the open-source OpenConnect client, NetworkManager's OpenConnect plugin, or the mobile OpenConnect apps on iOS and Android.

    Why run the VPN on port 443 specifically?

    Port 443 carries HTTPS, which is essentially impossible to block on a modern network -- blocking it would disable every website. Running ocserv on 443 means that from the network's perspective the VPN looks like normal encrypted web traffic. Hotel Wi-Fi, airport networks, corporate guest SSIDs, and most country-level firewalls all allow outbound 443, so your VPN works where OpenVPN on 1194 or WireGuard on 51820 would fail.

    How is ocserv different from OpenVPN, WireGuard, or strongSwan?

    OpenVPN is another SSL VPN but uses its own custom protocol on UDP 1194 by default. You can force it onto TCP 443, but that loses performance and still does not interoperate with AnyConnect clients.

    WireGuard is a much newer, smaller, faster UDP-only protocol. It is excellent for point-to-point tunnels and personal use, but does not work over TCP, has no official corporate client, and is blocked on many restrictive networks that only allow 443.

    strongSwan is an IPsec/IKEv2 implementation that integrates with the native VPN clients on iOS, macOS, and Windows. Great for always-on device VPNs, but IPsec uses UDP 500/4500 and ESP, which are frequently filtered on hostile networks.

    ocserv is the right choice when AnyConnect compatibility, port-443 firewall traversal, or drop-in replacement of a Cisco ASA head-end is important.

    Can I combine password auth with TOTP or certificates?

    Yes. ocserv supports stacked authentication with auth = "plain[passwd=...,otp=...]" for password+TOTP, or multiple auth = lines to allow either a password or a client certificate. For enterprise SSO, point the radius backend at Duo, Okta RADIUS, or Microsoft NPS.

    How many concurrent users can a small VPS handle?

    ocserv is very efficient. A 2 vCPU / 4 GB VPS comfortably handles 30-50 active users with mixed traffic patterns; the bottleneck is usually TLS/DTLS crypto on the vCPUs and available bandwidth, not memory. For 100+ concurrent users, scale to 4 vCPUs or more.

    Does ocserv support IPv6?

    Yes. Add ipv6-network directives to hand out IPv6 addresses and ensure net.ipv6.conf.all.forwarding = 1. You will also need an ip6 nat table (or native IPv6 routing if your VPS has a /64 delegated).

    Next Steps

    You now have a production-ready SSL VPN running on port 443 with AnyConnect compatibility, a trusted Let's Encrypt certificate, TOTP 2FA, and NAT out through your VPS's static IP. From here you can:

    • Add more users with ocpasswd and distribute connection instructions via a password manager.
    • Harden SSH further and consider moving it off port 22.
    • Layer on strongSwan for IPsec/IKEv2 if you also want native iOS and Windows VPN profiles.
    • Compare with OpenVPN if you want an alternative SSL VPN that uses a custom protocol rather than AnyConnect's.
    • Or use WireGuard in parallel for maximum speed on networks that allow UDP.
    • Read the upstream ocserv documentation for advanced options like client certificate CA hierarchies, RADIUS accounting, group-based routing, and per-session scripting.

    Skip the manual setup
    >
    If you just want a clean VPS with a real static IPv4 to run ocserv on, deploy a CloudCore Starter from EUR 7.99/month. Two vCPUs, 4 GB RAM, 50 GB NVMe, unmetered bandwidth, and a dedicated IP -- enough to run this entire guide and still have room for a few more services on the same box.

    Was this article helpful?

    ← Back to Install GuidesBrowse all categories →

    Still have questions?

    Contact Support →Submit a Ticket