Skip to content

Troubleshooting Guide

Solutions for common rVPN problems.


Symptoms: The client hangs at Connecting to wss://... and eventually times out.

Check:

  1. Server port is accessible:
Terminal window
# From a different machine
nc -zv your-server.com 443
curl -I https://your-server.com/api/v1/ws
  1. Firewall allows port 443:
Terminal window
sudo ufw status # UFW
sudo iptables -L -n | grep 443 # iptables
  1. TLS certificate is valid:
Terminal window
openssl s_client -connect your-server.com:443 -servername your-server.com </dev/null 2>/dev/null | openssl x509 -noout -dates
  1. WebSocket path is correct. Clients connect to {websocket_path} (e.g., /api/v1/ws). If your server uses a different path, update client.toml.

Symptoms: Client shows TLS handshake failed or certificate verify failed.

Causes and solutions:

  1. Let’s Encrypt certificate not renewed:
Terminal window
sudo certbot certificates
sudo systemctl reload rvpn-server
  1. Wrong hostname in server_address:
# The hostname must match the certificate
server_address = "wss://your-server.com/api/v1/ws" # Certificate must be for your-server.com
  1. SNI mismatch:
# If connecting through a CDN or by IP
server_address = "wss://10.0.0.1/api/v1/ws"
sni_hostname = "your-server.com" # Certificate hostname
  1. iOS: Cert verification issue (older builds): Ensure you have rebuilt the Rust library after updating. rVPN’s TLS stack uses BoringSSL with a bundled Mozilla CA root store and does not consult the iOS keychain, so certificate rotations or trust changes at the OS level don’t apply until the app is rebuilt.

Symptoms: Client fails instantly with Connection refused.

Check:

Terminal window
# Is the server running?
sudo systemctl status rvpn-server
# Is it listening on the right port?
sudo ss -tlnp | grep 443
# Can you connect locally? (uses whatever bind_address is set to)
curl -I https://127.0.0.1:443/api/v1/ws --insecure

Symptoms: Connection starts but fails during encryption setup.

Causes:

  1. Prekey bundle mismatch: Clients and servers must use the same prekey bundle. If the server rotated keys and the client has an old bundle, this can fail.
Terminal window
# On server: regenerate prekey bundle
rvpn-server prekey-bundle
# Distribute new prekey-bundle.json to clients
  1. Identity key changed: If the server identity key was regenerated, all clients need the new prekey bundle.

”Too many connections” or rate limit exceeded

Section titled “”Too many connections” or rate limit exceeded”

Symptoms: Connection refused or Rate limited after connecting successfully for a while.

Check server rate limits:

[server.rate_limit]
max_connections_per_ip = 500 # default
max_handshakes_per_minute = 2000 # default

Each client connection consumes one slot. Defaults are generous — if you have a very large fleet on a single egress IP, raise these; if you’re being probed and want tighter limits, drop them.


Client connects but has no internet access

Section titled “Client connects but has no internet access”

Check 1: IP forwarding on server:

Terminal window
sysctl net.ipv4.ip_forward
# Must return: net.ipv4.ip_forward = 1

If not enabled:

Terminal window
sudo sysctl -w net.ipv4.ip_forward=1
echo "net.ipv4.ip_forward = 1" | sudo tee -a /etc/sysctl.conf

Check 2: NAT rules on server:

Terminal window
sudo iptables -t nat -L POSTROUTING -v
sudo iptables -L FORWARD -v

You should see MASQUERADE rules and FORWARD ACCEPT rules.

If missing:

Terminal window
sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
sudo iptables -A FORWARD -i tun0 -o eth0 -j ACCEPT
sudo iptables -A FORWARD -i eth0 -o tun0 -m state --state RELATED,ESTABLISHED -j ACCEPT

Check 3: Server security group/firewall allows outbound: The server must be able to initiate outbound connections to any IP on any port for NAT to work.

Check 4: NAT enabled in server.toml:

[server.network]
nat_enabled = true

Check 5: Client routes:

Terminal window
# On client, check routing table
ip route show # Linux
route -n get 0.0.0.0 # macOS
# Default route should point to tunnel interface

Symptoms: You can ping external IPs but get no response. Server logs show frames being relayed but no “Relay completed”.

Root cause: SOCKS5 response sent before tunnel was ready.

Fix: This was a bug in older versions. Rebuild and redeploy:

Terminal window
cd rvpn-ios && ./build_rust.sh

Check server logs:

INFO rvpn_server: Listening on 0.0.0.0:443
INFO rvpn_server: WebSocket path: /api/v1/ws
INFO rvpn_server: TUN mode enabled

If TUN mode is not enabled, clients in TUN mode will fail to connect properly.


Check:

Terminal window
sudo ip addr show tun0
sudo ip link show tun0

If interface does not exist:

  1. Verify enabled = true in [server.tun]
  2. Check server logs for errors during interface creation
  3. Try a different interface name in case of naming conflict

Check:

Terminal window
# On server
ping 10.200.0.1 # From server to itself via tun0
# On client
ping 10.200.0.1 # Client should be able to reach server's TUN IP

If client cannot reach 10.200.0.1, the tunnel is not established properly.


Check 1: Proxy is running:

Terminal window
curl --socks5 127.0.0.1:1080 https://api.ipify.org

If this returns your VPN server IP, the proxy is working.

Check 2: System proxy settings: Ensure your system or app is configured to use 127.0.0.1:1080 as SOCKS5 proxy.

Check 3: Browser proxy settings: Chrome and Edge use system proxy settings. Firefox has its own proxy settings.

Check 4: App-specific issues: Some apps do not support SOCKS5 (only HTTP proxy). Use a SOCKS5-to-HTTP proxy adapter or switch to TUN mode.


Symptoms: DNS leak test shows your ISP DNS, not VPN DNS.

Fix: Enable the DNS proxy:

[dns_proxy]
enabled = true
listen_address = "127.0.0.1:53"

Configure your system DNS to 127.0.0.1. See DNS Leak Prevention for detailed setup.

Chrome-specific issue: Chrome uses its own secure DNS resolver by default, which bypasses the system DNS and the VPN tunnel.

  • On desktop/Android: Go to Settings → Privacy and security → Security → Use secure DNS and turn it off.
  • Clear Chrome’s DNS cache: visit chrome://net-internals/#dns and click Clear host cache.
  • On iOS: Force-close Chrome or clear browsing data to flush its cache.

Connection-per-flow limits (multiplex trade-off)

Section titled “Connection-per-flow limits (multiplex trade-off)”

multiplex defaults to false — the standard, recommended mode — because one-WebSocket-per-flow blends in with the traffic pattern of normal browsing and evades multiplex-shape DPI classifiers. The trade-off is that a busy browser can open dozens of concurrent WebSockets and, on shared egress IPs, hit the server’s max_connections_per_ip limit.

Two ways to handle it:

  1. Ask your server administrator to raise the defaults:
    [server.rate_limit]
    max_connections_per_ip = 1000
    max_handshakes_per_minute = 5000
  2. Enable multiplex on the client to share a single tunnel across all flows (lower latency, but a more distinctive traffic pattern):
    [socks5]
    multiplex = true

Possible causes:

  1. High latency: The VPN server is geographically distant
  2. Server overload: Too many connections to one server
  3. Bandwidth limit: Server’s upstream is saturated
  4. MTU issues: Packet fragmentation on high-latency links

Solutions:

  1. Lower MTU in client.toml:
[tun]
mtu = 1280
  1. Try a different server closer to your location
  2. Check server load: uptime, htop
  3. Disable IPv6 if not needed:
[network]
ipv6_enabled = false
prefer_ipv4 = true

Check:

  1. Server address includes wss:// (not https://)
  2. Identity key is generated (Settings -> Identity)
  3. Prekey bundle is imported
  4. Server is running and accessible

Rebuild Rust library:

Terminal window
cd rvpn-ios && ./build_rust.sh

Possible causes:

  1. Network instability (Wi-Fi to cellular handoff)
  2. iOS suspending the app in background
  3. VPN profile being revoked

Solutions:

  1. Enable “Always-on VPN” in iOS Settings -> VPN
  2. Check for iOS updates
  3. Rebuild and reinstall the app

Cause: The server’s DHCP pool is exhausted.

Fix: Increase the DHCP range on the server:

[server.network]
dhcp_range = "10.200.0.0/22" # /22 gives 1022 IPs instead of 254

Or disconnect unused clients.


Symptoms: The app shows “Connected” but no traffic passes, or the connection fails immediately after updating from the App Store or rebuilding from Xcode.

Cause: macOS persists VPN profiles in System Settings independently of the app. After an update, the stored profile’s tunnel extension reference becomes stale because the extension’s code signature has changed. The app tries to start the old extension, which no longer exists.

Fix:

  1. Open System Settings > VPN (or System Settings > General > VPN & Filter on newer macOS versions).
  2. Delete the rVPN entry.
  3. Re-open the rVPN app and reconnect. The app will create a fresh profile automatically.

This is resolved in version 1.2.4 and later, which automatically detects and replaces stale profiles on launch.


”Failed to start VPN” or connection fails silently

Section titled “”Failed to start VPN” or connection fails silently”

Check:

  1. Open the rVPN app and go to Settings (Cmd+,). Verify the profile shows green checkmarks for both Identity Key and Prekey Bundle.
  2. Server address must start with wss:// and have no trailing whitespace.
  3. The server must be running and accessible on port 443.

If the profile is missing keys:

  1. Generate a new identity key in the profile editor.
  2. Import the prekey bundle from your server administrator.

If keys are present but it still fails:

  1. Delete the VPN profile from System Settings > VPN.
  2. Delete the rVPN app.
  3. Reinstall from the App Store.
  4. Reconfigure the profile and reconnect.

The macOS app runs a local DNS proxy for split-tunnel DNS resolution. If DNS fails:

  1. Check that the server address is reachable from your network.
  2. Try disabling split tunnel in the profile editor to test full-tunnel mode.
  3. Check server logs for DNS proxy errors.

Rust-level logs (bypasses macOS log redaction):

Terminal window
cat ~/Library/Group\ Containers/group.org.rvpn.client/rvpn_tunnel_rust.log

System-level logs (may be redacted on macOS 12+):

Terminal window
log show --predicate 'subsystem == "org.rvpn.tunnel"' --last 5m --level debug

Console.app can also be used: filter by subsystem org.rvpn.tunnel.


Check:

[split_tunnel]
enabled = true # Must be true for any bypass to work
builtin_bypass_countries = ["CN"]

Verify rules are loaded: The client logs should show bypass rules on startup:

INFO rvpn_client: Split tunnel enabled, X networks bypassed

Check routing table:

Terminal window
ip route show # Linux
route -n get 0.0.0.0 # macOS

Ensure bypassed networks are not in the VPN routing table.


Causes:

  1. Streaming services may use GPS/locale signals, not just IP
  2. Account payment currency and history affect content
  3. CDN IPs may not match country bypass data

Solutions:

  1. Clear browser/app cookies and cache
  2. Use a browser extension to spoof timezone and locale
  3. Full tunnel mode may be needed (bypass nothing)

Check latency to server:

Terminal window
ping your-server.com

If latency is high even to the server, the issue is geographic distance, not the VPN.

Optimise:

  1. Use a server closer to your location
  2. Lower MTU if on satellite or high-latency link:
[tun]
mtu = 1280

Check:

  1. Server bandwidth: iperf3 test to server
  2. Client hardware: encryption is CPU-intensive on older devices
  3. Network congestion

Optimise:

[performance]
worker_threads = 4
crypto_worker_count = 4
recv_buffer_size = 262144
send_buffer_size = 262144

For detailed client logs:

Terminal window
RUST_LOG=debug rvpn -c ~/.config/rvpn/client.toml

For server logs:

Terminal window
RUST_LOG=debug sudo rvpn-server -c /etc/rvpn/server.toml
Terminal window
# systemd journal
sudo journalctl -u rvpn-server -f
# kernel logs (for TUN interface issues)
dmesg | grep tun
Terminal window
# Trace path to server
traceroute your-server.com
# Trace path from server to target
# (on server) sudo tcpdump -i tun0 -n
Log messageMeaning
Listening on 0.0.0.0:443Server started successfully
WebSocket path: /api/v1/wsWebSocket endpoint configured
TUN mode enabledServer TUN interface active
X3DH handshake completeEncryption established
NAT enabledServer will masquerade client traffic
Relay completedOne data relay session finished
Too many connectionsRate limit exceeded