PacketRelayDocs

Deploy PacketRelay#

PacketRelay can run from a Rust checkout or from the repository Docker image. Production deployments need a TLS-terminating proxy or hosting edge because the relay itself listens with plain HTTP and WebSocket protocols.

The source repository and image build context are private. Obtain an authorised checkout from the maintainer before following the source or Docker steps below; there is no public image distribution documented here.

Before you start#

  • Install Rust 1.83 or newer for a source build, or install Docker.
  • Choose one externally reachable port.
  • Decide which proxy or platform will provide HTTPS and wss://.
  • Use a single replica unless the calling application deliberately partitions clients across independent relays.

Run from source#

  1. Build the locked dependency graph.

    cargo build --locked --release
  2. Start the relay on port 8080.

    cargo run --locked -- --port 8080
  3. Keep the process behind a service manager for a long-running installation.

The PORT environment variable overrides --port when it contains a valid TCP port. Hosted platforms commonly inject PORT automatically.

Run in Docker#

  1. Build the image from the repository root.

    docker build -t packet-relay .
  2. Start one container.

    docker run --rm -p 8080:8080 packet-relay
  3. Pass operational limits after the image name when defaults do not fit.

    docker run --rm -p 8080:8080 packet-relay --max-connections 512 --max-spectators 200

The image listens on 8080 by default and runs as an unprivileged user. Preserve the repository build context because the image build includes protocol test fixtures.

Every supported option and its accepted range is listed in Configuration.

Run on a hosted platform#

The relay reads PORT from the environment, so a platform that injects a port needs no --port. Point the platform health check at /ready: it returns 200 ready normally and 503 draining once shutdown has begun, so an in-progress deploy is taken out of rotation before its sockets close.

The relay shuts down gracefully on SIGTERM and on Ctrl+C. It stops accepting, closes every live WebSocket with code 1001, and exits within --shutdown-drain seconds — 10 by default.

Warning: several platforms send SIGKILL immediately after SIGTERM unless a drain window is configured. On Railway, RAILWAY_DEPLOYMENT_DRAINING_SECONDS defaults to 0. Set the platform's drain window comfortably above --shutdown-drain or every deploy cuts live connections instead of closing them cleanly.

Supply the /metrics bearer token as a platform secret named METRICS_TOKEN rather than as --metrics-token, so it does not appear in process listings. While no token is set the endpoint answers 404, which is the safe default.

Do not enable instance sleeping or scale-to-zero. A sleeping relay drops live sockets, and the sessions behind them do not survive.

Put TLS at the edge#

Publish the container through a proxy or platform endpoint with a valid certificate. Client URLs then use wss:// with that deployed edge hostname. Do not expose a plain ws:// endpoint across an untrusted network.

WebSocket route selection does not depend on the URL path, but several paths are reserved. /health, /healthz, /ready, /readyz, and /metrics serve HTTP; /ws/host, /ws/device, /api, /auth, and /push are reserved and answer 404. Send WebSocket clients to any remaining path, such as /. The full list is in Protocol.

Verify#

Check liveness and readiness before connecting application clients:

curl -fsS http://127.0.0.1:8080/health
curl -fsS http://127.0.0.1:8080/ready

Both commands return a successful response. Through the public edge, repeat the checks over HTTPS and then complete First bridge.

If it does not work#

Symptom Check
The process listens on an unexpected port Inspect PORT; it takes precedence over --port.
The container exits during startup Run it without detached mode and read the argument-validation message.
Health works but WebSocket upgrade fails Confirm the proxy forwards the Upgrade and Connection headers, and that the client is not using a reserved path.
Clients see a dropped socket on every deploy instead of a 1001 close Confirm the platform's drain window exceeds --shutdown-drain.
/ready returns 503 draining and stays there The process received a shutdown signal; confirm the platform is not restarting it in a loop.
One client reaches a different session population Confirm only one relay replica is serving the hostname.