Free to use · Not open source · Python · UDP · Multi-Region
They said it could not be done — that a free load balancer for OpenVPN was a madman's dream,
a patchwork of impossible ideas stitched together on a stormy night.
But I, Kencypher, descended into the laboratory, surrounded by flickering terminals
and the smell of burning electrons, and I BUILT IT.
From the raw sinew of Python's asyncio, the cold bones of UDP sockets,
and the beating heart of a round-robin algorithm — I assembled life where there was none.
It breathes. It distributes. It load balances.
— Kencypher, somewhere between sane and enlightened
Cypher OpenVPN Load Balancer is a fully async, zero-dependency Python load balancer built specifically for OpenVPN UDP traffic. It sits between your VPN clients and your pool of OpenVPN servers, intelligently distributing connections across multiple servers and multiple geographic regions — entirely for free.
No expensive hardware appliances. No paid SaaS. No bloated daemons. Just Python, a text file of servers, and the will to run it.
Define server pools per region (USA, UK, Singapore, Germany…). Each region gets its own listening port. Clients connect to the nearest region port — traffic stays local.
New connections are distributed evenly across all healthy servers in a region. No server gets overwhelmed while others idle. Configurable per-server client cap.
Every server is pinged on a configurable interval. Unhealthy servers are immediately taken out of rotation. They are automatically reinstated when they recover.
Only currently connected clients are counted. Disconnected or timed-out clients vanish from the registry instantly — no stale data, ever.
Flows that go silent for longer than the configurable idle window are automatically released, freeing server capacity without manual intervention.
A clean menu lets you start/stop the service, inspect clients by region, watch live connection counts, and check server health — all from the terminal.
100% Python standard library. No pip installs. No virtual environments required. If Python 3.8+ is installed, this runs.
Add or remove servers by editing a single server_list.txt. No YAML hell, no JSON nightmares,
no reload daemons — just save and restart.
The load balancer operates as a UDP proxy. It opens one listening port per region and maintains a persistent UDP socket to every upstream OpenVPN server. Client packets arrive, get assigned to a server via round-robin, and are forwarded. Return packets from the server are matched back to the originating client using an in-memory flow table.
VPN Client A ──┐ VPN Client B ──┤ UDP :1194 ┌─── REGION: USA ─────────────────────────────────┐ VPN Client C ──┼──────────────────────────► ClientProtocol (listener) │ │ │ assign_server() [round-robin] │ │ ▼ │ │ LoadBalancer + FlowTable │ │ │ │ │ ┌─────────────┬┴─────────────┐ │ │ ▼ ▼ ▼ │ │ ServerProto[0] ServerProto[1] ServerProto[2] │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ usa-node-1 usa-node-2 usa-node-3 │ └──────────────────────────────────────────────────────────────────────┘ VPN Client D ──┐ VPN Client E ──┼──────────────────────────► REGION: UK (port :1195) │ → uk-node-1, uk-node-2, uk-node-3 ··· and so on per region ···
UDP is stateless, so the load balancer maintains its own flow table:
a dictionary keyed by (client_ip, client_port, server_ip, server_port) that records
the last packet timestamp. When the upstream server replies, the balancer looks up the flow,
finds the original client, and sends the response back. This is the core of bidirectional UDP proxying.
Once a client is assigned to a server, that assignment is pinned in the ClientRegistry for the lifetime of the connection. OpenVPN sessions are long-lived — bouncing between servers mid-session would destroy the tunnel. Stickiness ensures continuity.
Every HEALTHCHECK_INTERVAL seconds, an async ICMP ping is sent to each server.
If a server goes dark, it is marked unhealthy and excluded from new assignments.
Existing flows to that server are preserved until they idle out — no forced disconnects.
| File | Role | Key Components |
|---|---|---|
| main.py | Entry point | Starts CLI or headless service; handles --headless and --log-level flags |
| cli.py | CLI menu | 5-option interactive menu; runs the service in a background thread |
| interactive.py | Display layer | Live client tables, region detail views, auto-refresh watch mode |
| loadbalancing.py | Orchestrator | LoadBalancingService: sets up regions, runs health check loops |
| logic.py | Balancing logic | LoadBalancer: round-robin, flow table, idle timeout, health flags |
| connecting.py | Network layer | ClientProtocol, ServerProtocol, create_region_services() |
| openvpn.py | Config & constants | Parses server_list.txt, assigns region ports, ICMP health checks |
| clients.py | Client registry | Thread-safe store of live clients only — no disconnected entries |
| count.py | Stats display | ServerCounter: per-server and per-region client counts |
| server_list.txt | Configuration | Plain text: name,region,ip,port,protocol — one server per line |
Pull the project to the machine that will act as your load balancer node.
git clone https://github.com/kencypher56/cypher-openvpn-loadbalancer.git cd cypher-openvpn-loadbalancer
Add your OpenVPN servers. One line per server. Protocol defaults to udp if omitted.
# Format: name, region, ip, port, protocol usa-node-1,USA,203.0.113.10,1194,udp usa-node-2,USA,203.0.113.11,1194,udp uk-node-1,UK,198.51.100.5,1194,udp uk-node-2,UK,198.51.100.6,1194,udp sg-node-1,SINGAPORE,192.0.2.20,1194,udp
Launch the CLI menu. No root required unless binding to ports below 1024.
python3 main.py
Skip the menu entirely. Logs go straight to stdout. Ideal for production deployments.
python3 main.py --headless python3 main.py --headless --log-level DEBUG
In your client .ovpn config, set remote to the load balancer's IP and the
region port.
# In your client .ovpn file: remote YOUR_LB_IP 1194 # → USA region remote YOUR_LB_IP 1195 # → UK region remote YOUR_LB_IP 1196 # → Singapore region proto udp
All core tunables live at the top of openvpn.py. Edit and restart to apply.
# openvpn.py — core tunables MAX_CLIENTS_PER_SERVER = 100 # Hard cap per upstream server IDLE_TIMEOUT_SECONDS = 300 # 5 min silence = client released HEALTHCHECK_INTERVAL = 15 # Ping servers every 15 seconds HEALTHCHECK_TIMEOUT = 3 # Ping timeout in seconds
# name , region , ip , port , protocol(optional) my-server,USA,10.0.0.1,1194,udp # Omit protocol → defaults to udp my-server-2,USA,10.0.0.2,1194 # Multiple regions → multiple listening ports assigned automatically
Every other load balancer wants your credit card. HAProxy is powerful but not built for VPN UDP flows. AWS NLB costs money. Dedicated appliances cost more money. I built this because the community deserves a free, understandable tool that does exactly one job and does it well. No telemetry. No license keys. No "enterprise tier." Just code. Forever.
— Kencypher
Run it anywhere, including commercially, at no cost. It is not open source: the code is
readable here, but not licensed for reuse or redistribution. See LICENSE.txt.
~600 lines of plain Python. Read every line. Understand everything. No black boxes.
The client cap, idle timeout and health-check timing are constants at the top of
openvpn.py. Change them and restart.
Uses asyncio.create_task(), f-strings, and walrus-compatible patterns. Any modern
Linux/macOS/Windows install qualifies.
Entirely standard library: asyncio, socket, threading,
logging, subprocess, time, os, argparse.
Nothing to pip install.
Health checks use system ping. On most Linux servers this works without root. If ICMP is
blocked, health checks will report all servers unhealthy — adapt check_server_health() in
openvpn.py to use a TCP probe instead.
Your upstream servers must be configured with proto udp. TCP mode is not supported in this
version.