A blazing fast, cross-platform, transparent TCP & UDP proxy written in Rust. No bloat, no config headaches — just simple port forwarding that works out of the box.
Ships with rproxy-edit, a built-in terminal UI for managing proxy configurations visually, and hot reload support — update your config and apply changes without dropping a single connection.
cargo install rproxyThis installs both rproxy (the proxy) and rproxy-edit (the config editor).
Forward local port 8080 to a remote service:
rproxy -b 0.0.0.0:8080 -r 10.0.0.5:3000 -p TCPForward UDP traffic (e.g. DNS, VPN tunnels):
rproxy -b 0.0.0.0:1194 -r vpn-server.example.com:1194 -p UDPRun many proxies at once from a single JSON config:
rproxy -c proxies.jsonWhere proxies.json looks like:
{
"settings": {
"max_connections": 2048,
"max_client_tunnels": 1024,
"keepalive_idle": 60,
"keepalive_interval": 30
},
"proxies": [
{
"bind": "0.0.0.0:8080",
"remote": "10.0.0.5:3000",
"protocol": "TCP"
},
{
"bind": "0.0.0.0:1194",
"remote": "vpn-server.example.com:1194",
"protocol": "UDP"
}
]
}The settings block is optional — omit it to use defaults. Plain JSON arrays (the old format) are still supported for backward compatibility.
Tired of editing raw JSON? Open any config in the built-in terminal editor:
rproxy-edit proxies.json┌─────────────────────────────────────────────────┐
│ rproxy config editor — proxies.json │
├────┬──────────────────┬──────────────────┬───────┤
│ # │ Bind │ Remote │ Proto │
├────┼──────────────────┼──────────────────┼───────┤
│> 1 │ 0.0.0.0:8080 │ 10.0.0.5:3000 │ TCP │
│ 2 │ 0.0.0.0:1194 │ vpn-server.ex... │ UDP │
├────┴──────────────────┴──────────────────┴───────┤
│ [NORMAL] j/k:nav i:edit a:add d:del s:settings│
└─────────────────────────────────────────────────┘
Keybindings (Helix/Vim-style):
| Key | Action |
|---|---|
j / k |
Navigate up/down |
i / Enter |
Edit selected entry |
a |
Add new proxy entry |
d |
Delete selected entry |
s |
Edit settings (connections, keepalive, etc.) |
Tab / Shift+Tab |
Cycle fields in edit mode |
Space |
Toggle protocol (TCP/UDP) |
:w |
Save |
:q |
Quit |
:wq |
Save and quit |
? |
Show help |
rproxy — A platform neutral asynchronous UDP/TCP proxy
Options:
-r, --remote <host>:<port> Remote endpoint to forward traffic to
-b, --bind <ip>:<port> Local address to listen on
-p, --protocol TCP|UDP Protocol (UDP by default)
-c, --config <file.json> JSON config for multiple proxies
-s, --signal <reload> Send signal to running rproxy (reload config)
-d, --debug Enable debug logging
-l, --logger_settings <file> Logger config file (YAML)
--max-connections N Max concurrent TCP connections (default: 1024)
--max-client-tunnels N Max concurrent UDP client tunnels (default: 1024)
--keepalive-idle N TCP keepalive idle time in seconds (default: 60)
--keepalive-interval N TCP keepalive probe interval in seconds (default: 30)
CLI flags override values from the config file's settings block.
Forward traffic from a public-facing port to a service running on localhost:
rproxy -b 0.0.0.0:443 -r 127.0.0.1:3000 -p TCPRelay VPN tunnel traffic through an intermediary server. rproxy handles UDP natively with per-client session tracking:
rproxy -b 0.0.0.0:1194 -r vpn-backend.internal:1194 -p UDPRun a single rproxy instance as a lightweight gateway that fans out to multiple backend services:
[
{ "bind": "0.0.0.0:80", "remote": "web-server:8080", "protocol": "TCP" },
{ "bind": "0.0.0.0:443", "remote": "web-server:8443", "protocol": "TCP" },
{ "bind": "0.0.0.0:5432", "remote": "db.internal:5432", "protocol": "TCP" },
{ "bind": "0.0.0.0:53", "remote": "dns.internal:53", "protocol": "UDP" }
]rproxy -c gateway.jsonForward SSH access to machines behind a firewall without configuring SSH ProxyJump:
rproxy -b 0.0.0.0:2222 -r 192.168.1.100:22 -p TCPThen connect with: ssh -p 2222 user@proxy-host
Relay UDP game traffic to reduce latency or provide a stable entry point:
rproxy -b 0.0.0.0:27015 -r game-server.region.example.com:27015 -p UDPUse rproxy-edit to maintain separate configs for different environments, then hot-reload without downtime:
rproxy -c configs/production.json # start production proxies
rproxy-edit configs/production.json # edit config in TUI (press 's' for settings)
rproxy -s reload # apply changes — zero downtimeThe editor validates addresses and protocols before saving, so you catch mistakes before they hit production.
rproxy supports nginx-style hot reload — update your config file and apply changes without restarting the process or dropping existing connections.
- Start rproxy with a config file:
rproxy -c proxies.json- Edit the config (add, remove, or change proxies) using
rproxy-editor any text editor:
rproxy-edit proxies.json- Apply changes without restarting:
rproxy -s reloadrproxy compares the old and new config and makes the minimum necessary changes:
| Change | What happens |
|---|---|
| Proxy unchanged (same bind, remote, protocol) | No interruption — zero downtime |
| New proxy added | Started immediately |
| Proxy removed | Stops accepting new connections, existing connections drain gracefully |
| Proxy changed (same bind, different remote) | Old proxy drains, new proxy starts |
Existing TCP connections through unchanged proxies are never dropped. UDP tunnels through removed proxies receive a clean terminate signal and drain naturally.
When started with -c, rproxy writes its PID to /tmp/rproxy.pid. Running rproxy -s reload reads the PID file and sends SIGHUP to the process, which triggers the config diff and reload cycle.
Config files use a JSON object with an optional settings block and a proxies array. Plain JSON arrays (without the wrapper object) are also supported for backward compatibility.
| Field | Type | Description |
|---|---|---|
bind |
string |
Local ip:port to listen on |
remote |
string |
Remote host:port to forward to (supports hostnames with auto DNS refresh) |
protocol |
string |
"TCP" or "UDP" |
All settings are optional and have sensible defaults:
| Field | Type | Default | Description |
|---|---|---|---|
max_connections |
integer |
1024 |
Max concurrent TCP connections |
max_client_tunnels |
integer |
1024 |
Max concurrent UDP client tunnels |
keepalive_idle |
integer |
60 |
Seconds of idle before first TCP keepalive probe |
keepalive_interval |
integer |
30 |
Seconds between TCP keepalive probes |
rproxy automatically re-resolves DNS hostnames every 30 seconds, so you can point at dynamic endpoints without restarts.
Most proxies resolve a hostname to an IP once at startup and hold onto it forever. If the IP behind that hostname changes, traffic breaks and you have to restart the proxy. rproxy takes a different approach: it re-resolves DNS every 30 seconds and seamlessly switches to the new IP with zero downtime.
This matters more than you might think:
Cloud providers frequently reassign public IPs when instances are stopped, restarted, or auto-scaled. Your VPN relay points at vpn.example.com, and after a routine maintenance window the underlying IP changes from 52.14.88.10 to 52.14.91.33. A traditional proxy keeps sending packets to the old IP — connections time out and users call you at 3 AM. rproxy picks up the new IP on the next 30-second cycle automatically.
{ "bind": "0.0.0.0:1194", "remote": "vpn.example.com:1194", "protocol": "UDP" }In Kubernetes, a Service or Ingress hostname can resolve to different pod IPs after a rolling deployment. If you're proxying traffic into the cluster through an external rproxy, the backend IP may shift every time a deployment rolls out. Dynamic resolution means deployments never cause proxy downtime.
{ "bind": "0.0.0.0:8080", "remote": "my-app.k8s.internal:8080", "protocol": "TCP" }Services like Route 53, Cloudflare DNS, or Consul use DNS records for health-based failover — when a primary server goes down, the DNS record updates to point at a standby. rproxy follows the DNS change within 30 seconds, so failover works end-to-end without manual intervention.
{ "bind": "0.0.0.0:5432", "remote": "db-primary.service.consul:5432", "protocol": "TCP" }If you're running rproxy on a home server or edge device, the remote endpoint might be behind a dynamic DNS provider (e.g. myhouse.duckdns.org). The ISP rotates your IP periodically. rproxy handles this transparently — no cron jobs, no restart scripts.
rproxy -b 0.0.0.0:2222 -r myhouse.duckdns.org:22 -p TCPEvery 30 seconds, rproxy re-resolves the hostname for each proxy. When a new IP is detected, it logs the change and routes all new connections to the updated address — existing connections continue on the old IP until they naturally close. No connections are dropped, no traffic is interrupted.
MIT