Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/nestrilabs/nestri/llms.txt

Use this file to discover all available pages before exploring further.

The Nestri relay is a lightweight Go service that acts as a libp2p node. It handles WebRTC signaling between browser clients and game runner containers, performs NAT hole-punching, and serves the HTTP/WebSocket/WebTransport endpoint that peers connect to. Media traffic flows directly between peers once a WebRTC connection is established — the relay is not in the media path after the initial handshake.

Docker image

The official nightly image is published to the GitHub Container Registry:
ghcr.io/nestrilabs/nestri/relay:nightly
The image is built from containerfiles/relay.Containerfile using a two-stage Go build on Alpine Linux.

Quick start

docker run -d \
  --name nestri-relay \
  -p 8088:8088/udp \
  -e VERBOSE=true \
  --restart unless-stopped \
  ghcr.io/nestrilabs/nestri/relay:nightly
The relay listens on port 8088 for all protocols: HTTP, WebSocket, QUIC, and WebTransport simultaneously. No extra ports are needed unless you configure a separate UDP range for WebRTC (see WEBRTC_UDP_START / WEBRTC_UDP_END below).

Environment variables

All configuration is passed via environment variables. Every variable maps to a command-line flag with the same name in lower camel case.
env.REGEN_IDENTITY
boolean
default:"false"
Regenerate the relay’s libp2p identity on startup. When false, the existing identity.key in PERSIST_DIR is reused. Set to true only if you need a fresh peer identity — this invalidates any existing peer connections that know the old identity.
env.VERBOSE
boolean
default:"false"
Enable verbose (debug-level) logging to stdout. Implied when DEBUG=true.
env.DEBUG
boolean
default:"false"
Enable debug mode. Implies VERBOSE=true. Produces more detailed internal relay diagnostics.
env.ENDPOINT_PORT
number
default:"8088"
The TCP port the relay’s HTTP/WebSocket/WebTransport endpoint listens on. Expose this port in your Docker run command and firewall rules.
env.WEBRTC_UDP_START
number
default:"0"
Start of the UDP port range allocated for individual WebRTC peer connections. Set to 0 to disable port-range mode and use the UDP mux port instead (recommended). Ignored when WEBRTC_UDP_MUX is non-zero.
env.WEBRTC_UDP_END
number
default:"0"
End of the UDP port range for WebRTC. Must be set together with WEBRTC_UDP_START. Leave at 0 when using mux mode.
env.STUN_SERVER
string
default:"stun.l.google.com:19302"
The STUN server used for WebRTC ICE candidate gathering. You can replace this with a private STUN/TURN server if your network blocks Google’s servers.
env.WEBRTC_UDP_MUX
number
default:"8088"
The UDP port used as a single WebRTC mux. All WebRTC traffic is multiplexed through this single port, which avoids needing a large UDP port range. Defaults to the same value as ENDPOINT_PORT.
env.WEBRTC_NAT_IPS
string
default:""
A comma-separated list of public IP addresses to advertise in WebRTC ICE candidates. Required when the relay is behind NAT and AUTO_ADD_LOCAL_IP cannot detect the correct public IP. Example: 1.2.3.4.
env.AUTO_ADD_LOCAL_IP
boolean
default:"true"
Automatically detect and add the machine’s non-loopback, non-private IP as a WebRTC NAT candidate. The relay source code in packages/relay/internal/common/flags.go iterates interface addresses and picks the first non-loopback, non-private, non-unspecified address. Set to false and use WEBRTC_NAT_IPS when you need explicit control.
env.PERSIST_DIR
string
default:"./persist-data"
Directory where the relay stores its libp2p identity (identity.key) and peer store (peerstore.json). Mount a volume at this path to survive container restarts without losing the relay’s peer identity.
env.METRICS
boolean
default:"false"
Enable the Prometheus-compatible metrics endpoint.
env.METRICS_PORT
number
default:"3030"
Port for the metrics HTTP endpoint. Only active when METRICS=true.

Identity persistence

On each graceful shutdown, the relay saves its peer store to $PERSIST_DIR/peerstore.json. The libp2p identity key is written to $PERSIST_DIR/identity.key. Mount a named volume to preserve these files:
docker run -d \
  --name nestri-relay \
  -p 8088:8088/udp \
  -v nestri-relay-data:/relay/persist-data \
  -e PERSIST_DIR=/relay/persist-data \
  --restart unless-stopped \
  ghcr.io/nestrilabs/nestri/relay:nightly
If you lose the identity.key file, the relay will generate a new libp2p peer identity on the next start with REGEN_IDENTITY=true. Any runners or clients that stored the old peer ID will need to reconnect.

TLS proxy setup

The relay itself does not handle TLS termination. Run it behind a reverse proxy for HTTPS/WSS. The examples below are drawn directly from the reference compose files in containerfiles/.
The Caddyfile proxies WebSocket upgrade requests and all other HTTP traffic to the relay container on port 8088. Replace relay.example.com and you@example.com with your domain and email.
Caddyfile
relay.example.com {
    @ws {
        header Connection Upgrade
        header Upgrade websocket
    }
    tls you@example.com
    reverse_proxy @ws relay:8088
    reverse_proxy relay:8088
}
docker-compose.relay.caddy.yml
services:
  caddy:
    image: caddy:latest
    container_name: caddy
    ports:
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - ./cert:/etc/caddy/certs
    depends_on:
      - relay
    networks:
      - relay_network
    restart: unless-stopped

  relay:
    image: ghcr.io/nestrilabs/nestri/relay:nightly
    container_name: relay
    environment:
      - VERBOSE=true
      # Uncomment when the relay is behind NAT:
      # - AUTO_ADD_LOCAL_IP=false
      # - WEBRTC_NAT_IPS=1.2.3.4
    ports:
      - "8088:8088/udp"
    networks:
      - relay_network
    restart: unless-stopped

networks:
  relay_network:
    driver: bridge

Port requirements

PortProtocolPurpose
8088UDPWebRTC media and mux (required, must be reachable from the internet)
8088TCPHTTP / WebSocket / WebTransport endpoint (proxied via TLS terminator)
443TCPHTTPS/WSS (handled by Caddy or Traefik in front of the relay)
If clients are behind strict firewalls that block UDP, WebTransport over HTTPS provides a fallback. Ensure port 443/tcp is reachable and your TLS proxy passes the connection through to the relay’s HTTP endpoint.

NAT traversal

When the relay container is behind a router performing NAT, WebRTC ICE candidates advertised by the relay may not be reachable from the internet. There are two ways to fix this:
  1. Automatic detection (AUTO_ADD_LOCAL_IP=true, the default): The relay calls net.InterfaceAddrs() and picks the first non-loopback, non-private, non-unspecified address. This works when the container shares the host network or the host has a public IP on an interface.
  2. Explicit IP (WEBRTC_NAT_IPS=<public-ip>): Set this to your machine’s public IPv4 address. Disable AUTO_ADD_LOCAL_IP when using this option (AUTO_ADD_LOCAL_IP=false).

Build docs developers (and LLMs) love