vprox is a high-performance network proxy acting as a split tunnel VPN. The server accepts peering requests from clients, which then establish WireGuard tunnels that direct all traffic on the client's network interface through the server, with IP masquerading.
Both the client and server commands need root access. The server can have multiple public IP addresses attached, and on cloud providers, it automatically uses the instance metadata endpoint to discover its public IP addresses and start one proxy for each.
This property allows the server to be high-availability. In the event of a restart or network partition, the tunnels remain open. If the server's IP address is attached to a new host, clients will automatically re-establish connections. This means that IP addresses can be moved to different hosts in event of an outage.
On the Linux VPN server and client, install system requirements (iptables and wireguard).
# On Ubuntu
sudo apt install iptables wireguard
# On Fedora
sudo dnf install iptables wireguard-toolsThe client does not strictly need the WireGuard kernel module. When the kernel has no WireGuard support (for example, in Modal VM sandboxes), vprox connect falls back to the embedded userspace implementation (wireguard-go) on a TUN interface of the same name. That requires /dev/net/tun, and the startup log says which implementation is in use. The userspace interface serves the standard UAPI socket under /var/run/wireguard, so wg show works with it. The server always uses kernel WireGuard.
Also, you need to set some kernel settings with Sysctl. Enable IPv4 forwarding, and make sure that rp_filter is set to 2, or masqueraded packets may be filtered out. You can edit your OS configuration file to set this persistently, or set it once below.
# Applies until next reboot
sudo sysctl -w net.ipv4.ip_forward=1
sudo sysctl -w net.ipv4.conf.all.rp_filter=2For maximum throughput, especially on high-bandwidth servers with many peers, apply these additional sysctl settings. Create a file /etc/sysctl.d/99-vprox-performance.conf or apply them temporarily:
# UDP/Socket Buffer Sizes (WireGuard uses UDP)
# Increase max buffer sizes to 25MB for high-throughput scenarios
sudo sysctl -w net.core.rmem_max=26214400
sudo sysctl -w net.core.wmem_max=26214400
sudo sysctl -w net.core.rmem_default=1048576
sudo sysctl -w net.core.wmem_default=1048576
# Network device backlog (for high packet rates)
# Increase backlog to handle traffic bursts
sudo sysctl -w net.core.netdev_max_backlog=50000
sudo sysctl -w net.core.netdev_budget=600
# TCP tuning (for traffic inside the tunnel)
# Format: min default max
sudo sysctl -w net.ipv4.tcp_rmem="4096 1048576 26214400"
sudo sysctl -w net.ipv4.tcp_wmem="4096 1048576 26214400"
# Use BBR congestion control (better than cubic for most scenarios)
sudo sysctl -w net.ipv4.tcp_congestion_control=bbr
# Enable TCP Fast Open for reduced latency on reconnects
sudo sysctl -w net.ipv4.tcp_fastopen=3
# Connection tracking limits (critical for NAT with many peers)
# Increase max tracked connections to 1M
sudo sysctl -w net.netfilter.nf_conntrack_max=1048576
# Optional: Busy polling for lower latency (increases CPU usage)
# sudo sysctl -w net.core.busy_poll=50
# sudo sysctl -w net.core.busy_read=50To make these settings persistent across reboots, add them to /etc/sysctl.d/99-vprox-performance.conf without the sudo sysctl -w prefix:
# /etc/sysctl.d/99-vprox-performance.conf
net.core.rmem_max=26214400
net.core.wmem_max=26214400
net.core.rmem_default=1048576
net.core.wmem_default=1048576
net.core.netdev_max_backlog=50000
net.core.netdev_budget=600
net.ipv4.tcp_rmem=4096 1048576 26214400
net.ipv4.tcp_wmem=4096 1048576 26214400
net.ipv4.tcp_congestion_control=bbr
net.ipv4.tcp_fastopen=3
net.netfilter.nf_conntrack_max=1048576
Then apply with sudo sysctl --system.
To set up vprox, you'll need the private IPv4 address of the server connected to an Internet gateway (use the ip addr command), as well as a block of IPs to allocate to the WireGuard subnet between server and client. This has no particular meaning and can be arbitrarily chosen to not overlap with other subnets.
The control connection carries the client's bearer token and the server's WireGuard public key, so clients always verify the server. The server needs a hostname that resolves to its public IP and a certificate for that hostname issued by a publicly trusted CA (for example, Let's Encrypt):
vprox server --tls-cert <path> --tls-key <path>serves the PEM certificate chain (leaf first, then intermediates) and private key. Both files are reloaded whenever they change, so renewing them in place takes effect without restarting the server.vprox connect <ip> --server-name <hostname>(required, non-empty) dials the server by IPv4 address and verifies its certificate against<hostname>, using the system trust store.
The default authentication mode uses a shared password via the VPROX_PASSWORD environment variable:
# [Machine A: public IP 1.2.3.4, private IP 172.31.64.125]
VPROX_PASSWORD=my-password vprox server --ip 172.31.64.125 --wg-block 240.1.0.0/16 \
--tls-cert /etc/vprox/fullchain.pem --tls-key /etc/vprox/privkey.pem
# [Machine B: public IP 5.6.7.8]
VPROX_PASSWORD=my-password vprox connect 1.2.3.4 --server-name vpn.example.com --interface vprox0
curl ifconfig.me # => 5.6.7.8
curl --interface vprox0 ifconfig.me # => 1.2.3.4vprox supports OIDC token-based authentication, designed for use with Modal's OIDC integration. In this mode, the server verifies JWT identity tokens signed by Modal instead of using a shared password.
Server setup:
# Start the server in oidc-modal mode, restricting access to a specific Modal workspace
VPROX_AUTH_MODE=oidc-modal \
VPROX_OIDC_ISSUER=https://oidc.modal.com \
VPROX_OIDC_ALLOWED_WORKSPACE_IDS=ws-abc123 \
vprox server --ip 172.31.64.125 --wg-block 240.1.0.0/16 \
--tls-cert /etc/vprox/fullchain.pem --tls-key /etc/vprox/privkey.pemClient setup (inside a Modal container):
# Modal sets MODAL_IDENTITY_TOKEN automatically; pass it to vprox via VPROX_OIDC_TOKEN
VPROX_AUTH_MODE=oidc-modal \
VPROX_OIDC_TOKEN="$MODAL_IDENTITY_TOKEN" \
vprox connect 1.2.3.4 --server-name vpn.example.com --interface vprox0OIDC environment variables:
| Variable | Description | Default |
|---|---|---|
VPROX_AUTH_MODE |
Auth mode: password or oidc-modal |
password |
VPROX_OIDC_TOKEN |
OIDC identity token (JWT) for client authentication | — |
VPROX_OIDC_ISSUER |
OIDC issuer URL | https://oidc.modal.com |
VPROX_OIDC_AUDIENCE |
Expected aud claim (skip check if empty) |
(empty) |
VPROX_OIDC_ALLOWED_WORKSPACE_IDS |
Comma-separated list of allowed Modal workspace IDs. Set to * to explicitly allow all workspaces (testing only). |
(any) |
VPROX_OIDC_ALLOWED_ENVIRONMENT_IDS |
Comma-separated list of allowed environment_id claims. |
(any) |
VPROX_OIDC_ALLOWED_ENVIRONMENT_NAMES |
Comma-separated list of allowed environment_name claims. Requires VPROX_OIDC_ALLOWED_WORKSPACE_IDS. |
(any) |
VPROX_OIDC_ALLOWED_APP_IDS |
Comma-separated list of allowed app_id claims. |
(any) |
VPROX_OIDC_ALLOWED_APP_NAMES |
Comma-separated list of allowed app_name claims. Requires VPROX_OIDC_ALLOWED_WORKSPACE_IDS. |
(any) |
VPROX_OIDC_ALLOWED_FUNCTION_IDS |
Comma-separated list of allowed function_id claims. |
(any) |
VPROX_OIDC_ALLOWED_FUNCTION_NAMES |
Comma-separated list of allowed function_name claims. Requires VPROX_OIDC_ALLOWED_WORKSPACE_IDS. |
(any) |
Each allowlist is checked against the corresponding claim of the signature-verified token. An unset list leaves that claim unchecked; a set list must contain the token's value (a token missing the claim is rejected). All set lists must match, so they narrow access together — for example, to one app in two environments of a shared workspace:
VPROX_AUTH_MODE=oidc-modal \
VPROX_OIDC_ALLOWED_WORKSPACE_IDS=ws-abc123 \
VPROX_OIDC_ALLOWED_ENVIRONMENT_NAMES=staging,prod \
VPROX_OIDC_ALLOWED_APP_NAMES=my-gateway-app \
vprox server --ip 172.31.64.125 --wg-block 240.1.0.0/16 \
--tls-cert /etc/vprox/fullchain.pem --tls-key /etc/vprox/privkey.pemEnvironment, app and function names are only unique within a workspace, and any Modal user can mint a validly signed token for a name of their choosing in their own workspace. The server therefore refuses to start when a name allowlist is set without VPROX_OIDC_ALLOWED_WORKSPACE_IDS. Each of these variables also accepts * to explicitly allow all values (testing only). A variable that is set but contains no entries (for example " , ") is rejected at startup rather than treated as unset.
Note that Machine B must be able to send UDP packets to port 50227 on Machine A, and TCP to port 443.
All outbound network traffic seen by vprox0 will automatically be forwarded through the WireGuard tunnel. The VPN server masquerades the source IP address.
To build vprox, run the following command with Go 1.22+ installed:
CGO_ENABLED=0 go buildThis produces a static binary in ./vprox.
CI publishes every commit merged to main, once its checks pass, as a GitHub release tagged sha-<full commit SHA>. Each release contains static vprox-linux-amd64 and vprox-linux-arm64 binaries, built by CI from exactly that commit, plus a SHA256SUMS file. Releases are never marked latest. Pin an exact tag and verify the download before running it:
TAG=sha-<commit>
gh release download "$TAG" --repo prassoai/vprox --pattern 'vprox-linux-amd64' --pattern SHA256SUMS
sha256sum -c --ignore-missing SHA256SUMSOn cloud providers like AWS, you can attach secondary private IP addresses to an interface and associate each of them with a global IPv4 unicast address.
A vprox server listening on multiple IP addresses needs to provide --ip option once for every IP, and each IP requires its its own WireGuard VPN subnet with a non-overlapping address range. You can pass --wg-block-per-ip /22 to split the --wg-block into smaller blocks for each IP.
On AWS in particular, the --cloud aws option allows you to automatically discover the private IP addresses of the server by periodically querying the instance metadata endpoint.
- Works on Linux 5.15+ with WireGuard installed
- Supports forwarding IPv4 packets
- Works if the server has multiple IPs, specified with
--wg-block-per-ip - Automatic discovery of IPs using instance metadata endpoints (AWS)
- Only one vprox server may be running on a host
- Control traffic is encrypted with TLS, and clients verify the server certificate against a configured hostname
- Optimized for throughput with automatic MTU, MSS, GSO/GRO, and multi-queue configuration
- Connection tracking bypass (NOTRACK) for reduced CPU overhead on WireGuard UDP flows
- OIDC authentication for passwordless auth from Modal containers (
oidc-modal)
This library is created by the team behind Modal.