Concepts
UDP is connectionless, so there is no persistent “connection” to forward. Instead, rustunnel tracks sessions — one per unique remote source address (IP:port) — and forwards datagrams in both directions for as long as the session is active.
- Public port — allocated from a dedicated UDP port range on the server (separate from the TCP tunnel pool).
- Session — identified by the remote client’s source
IP:port. Each unique source gets its own yamux stream to the client and its own locally bound UDP socket. - Idle timeout — sessions are reaped after 60 seconds of inactivity.
Unlike HTTP/TCP tunnels, there is no long-lived connection to track. Sessions are created on first datagram and expire on inactivity.
Connection Flow
1
Client registers a UDP tunnel
The rustunnel client sends
RegisterTunnel with protocol = Udp. The server allocates a port from udp_port_range and replies with TunnelRegistered { public_port }.2
Remote client sends a datagram
A remote UDP client sends a datagram to the allocated public port. The server’s UDP edge looks up a session keyed by the datagram’s source address.
3
Server creates a session on first datagram
If no session exists, the server creates one, buffers the datagram (up to 64 packets), and sends
NewConnection { conn_id, protocol: Udp } to the client over the control-plane WebSocket.4
Client opens a yamux stream
The client opens a new yamux stream for the
conn_id, binds a local UDP socket, and connect()s it to the local service address (e.g., 127.0.0.1:27015).5
Bidirectional datagram forwarding
The server forwards the buffered and subsequent datagrams into the yamux stream. The client writes them to the local service and copies replies back through the stream. The server delivers replies to the original source address.
Transport and Framing
UDP datagrams ride over the same yamux-over-WebSocket tunnel used by HTTP and TCP. Because yamux is a reliable byte stream and UDP is message-oriented, rustunnel preserves datagram boundaries with a simple length-prefixed frame:- No encoding overhead beyond the 4-byte header.
- Maximum payload is bounded by UDP itself (65,507 bytes after the IP/UDP header).
- Framing overhead is not counted toward
bytes_proxied— only raw payload bytes are metered.
Sessions
A UDP session holds:
A reaper task runs every 10 seconds and closes any session idle for more than 60 seconds. Because UDP has no FIN, this is how rustunnel decides when a session has ended.
Server Configuration
UDP tunnels require a dedicated port range inserver.toml. It must not overlap with tcp_port_range, and the ports must be reachable through the host firewall.
CLI Usage
Expose a local UDP service:host:port is forwarded to your local service, and replies are returned to the original sender.
Config File
In~/.rustunnel/config.yml:
rustunnel start.
Limitations and Notes
Troubleshooting
Related pages
P2P tunnels
Connect two clients directly with NAT hole punching over QUIC, with automatic relay fallback.
Load balancing and health checks
Run multiple backends behind one subdomain or TCP port with automatic failover.
Client guide
Every command, flag, and config option for the rustunnel CLI client.
Self-hosting
Run your own rustunnel server and configure port ranges, TLS, and firewall rules.

