A QUIC reverse proxy for Hytale servers. Routes connections by domain (SNI) and supports optional TLS termination for protocol inspection and manipulation.
Since Hytale lacks SRV record support (source), this proxy enables multiple servers on a single IP and port.
flowchart LR
A[Player A] -->|play.example.com| P[QUIC Relay :5520] --> S1[play.example.com]
B[Player B] -->|lobby.example.com| P --> S2[127.0.0.1:5521]
C[Player C] -->|minigames.example.com| P --> S3[minigames.dev:67344]
curl -sSL https://raw.githubusercontent.com/HyBuildNet/quic-relay/master/dist/install.sh | sudo bash
sudo systemctl enable --now quic-relayConfigure via /etc/quic-relay/config.json. Reload with systemctl reload quic-relay.
docker run -p 5520:5520/udp ghcr.io/hybuildnet/quic-relay:latestpodman run -p 5520:5520/udp ghcr.io/hybuildnet/quic-relay:latestOr mount your config:
docker run -p 5520:5520/udp -v /path/to/config.json:/data/config.json ghcr.io/hybuildnet/quic-relay:latestNote: If your backend servers are on an internal network, ensure the container has network access (e.g.,
--network hostor connect to the appropriate Docker network).
Handlers form a chain. Each handler processes the connection and either passes it to the next handler (Continue), handles it (Handled), or drops it (Drop).
Custom handlers can be implemented quite easily, but the project needs to be recompiled.
Routes connections based on the domain (SNI) players connect to.
{
"listen": ":5520",
"handlers": [
{
"type": "sni-router",
"config": {
"routes": {
"play.example.com": "10.0.0.1:5520",
"lobby.example.com": "10.0.0.2:5521",
"minigames.example.com": "myserver.internal.dev:8000"
}
}
},
{
"type": "forwarder"
}
]
}Note: The
forwarderhandler contains the actual forwarding logic. This separation allows replacing it with a terminating proxy for protocol inspection/manipulation.
Routes all connections to a single backend.
{
"listen": ":5520",
"handlers": [
{
"type": "ratelimit-global",
"config": {
"max_parallel_connections": 10000
}
},
{
"type": "simple-router",
"config": {
"backend": "10.0.0.1:5527"
}
},
{
"type": "forwarder"
}
]
}Limits the total number of concurrent connections. New connections are dropped when the limit is reached.
Forwards packets to the backend. Must be the last handler in the chain.
Logs the SNI of each connection. Useful for debugging.
{
"listen": ":5520",
"handlers": [
{
"type": "logsni"
},
{
"type": "sni-router",
"config": {
"routes": {
"play.example.com": "10.0.0.1:5521"
}
}
},
{
"type": "forwarder"
}
]
}Both sni-router and simple-router support round-robin load balancing. Use an array of backends instead of a single address:
{
"type": "sni-router",
"config": {
"routes": {
"play.example.com": ["10.0.0.1:5520", "10.0.0.2:5520", "[2001:db8::1]:5520"]
}
}
}For simple-router, use backends (array) instead of backend (string).
The terminator handler terminates QUIC TLS and bridges to backend servers for protocol inspection.
Note: Requires the HytaleCustomCert plugin on backend servers or another workflow to extract the /tmp certs at every runtime.
See docs/tls-termination.md for configuration.
{
"listen": ":5520",
"session_timeout": 600,
"handlers": [...]
}session_timeout- Idle session timeout in seconds (default:7200= 2 hours). Sessions without traffic are cleaned up after this duration. Can be changed via hot-reload (SIGHUP).
Fallback when not set in config:
QUIC_RELAY_LISTEN- Listen address (default::5520)QUIC_RELAY_BACKEND- Backend address forsimple-router
make buildProduces bin/proxy.
MIT License. See LICENSE for details.
This project is neither related to nor affiliated with HYPIXEL STUDIOS CANADA INC. or any other Trademark owner of Hytale.
