Skip to main content
This page documents UDP tunnels in rustunnel, our Rust-based UDP forwarding implementation. A UDP tunnel exposes a local UDP service — a game server, DNS resolver, QUIC endpoint, VoIP service — on a public port on the rustunnel server, forwarding datagrams bidirectionally between remote clients and the local service over the same multiplexed control connection that powers HTTP and TCP tunnels. For TCP-style direct connections see P2P tunnels; for HTTP load-balanced groups see Load Balancing & Health Checks.

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.
Subsequent datagrams from the same remote source reuse the existing session and yamux stream. Datagrams from a new source create a new session.

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.
UDP tunnels reuse the client’s existing WebSocket connection to the server. No new sockets are opened from client to server for UDP.

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 in server.toml. It must not overlap with tcp_port_range, and the ports must be reachable through the host firewall.
Open the range in the host firewall:
udp_port_range and tcp_port_range must not overlap. Setting udp_port_range = [0, 0] disables UDP tunnel registration entirely — any client attempting to register a UDP tunnel will get an error.

CLI Usage

Expose a local UDP service:
On success the client prints the allocated public UDP endpoint:
Any datagram sent to that public host:port is forwarded to your local service, and replies are returned to the original sender.

Config File

In ~/.rustunnel/config.yml:
Start all configured tunnels with rustunnel start.

Limitations and Notes


Troubleshooting


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.