Skip to content

NetherNet Transport

MEMOxiiii edited this page Sep 17, 2026 · 1 revision

NetherNet Transport

NetherNet is Bedrock's official WebRTC-based transport, the same mechanism Bedrock Dedicated Server exposes when transport=nethernet is set in server.properties. It's Portal's default and official player-facing transport (network.transport: "nethernet") — RakNet ("raknet") is still available as a straight swap for anyone who needs it, see Configuration.

This page covers what actually changes under the hood, and — more importantly — what you need to get right to run it on anything beyond localhost. If something isn't connecting, check Troubleshooting after reading the relevant section here.

Two channels, not one

Unlike RakNet (a single UDP socket), NetherNet splits a connection into two independent channels:

  1. Signaling — a plain HTTP(S) endpoint, served on network.address over TCP. This is how a client and Portal exchange WebRTC connection details (SDP offer/answer) to set up the real connection. It's what responds to a client's GET /v1/join probe.
  2. Media — the actual WebRTC data channel (ICE → DTLS → SCTP) once signaling completes, over UDP, on the port(s) configured by network.nethernet.udp_ports.

Signaling succeeding (you'll see nethernet: GET /v1/join / POST /v1/join/... in the debug log) does not mean the connection is up — the media channel has to establish separately afterward, and that's where firewall/NAT problems show up, as accepted connection never following the signaling lines, or start ICE: context deadline exceeded in errorlog-level output.

How a client finds the signaling endpoint

When a player manually adds a server by IP and port (or types one to join), a Bedrock client that supports NetherNet tries, in order, stopping at the first that responds: https://host:port, https://host (443), http://host:port, http://host (80). An explicit port (which is the normal case — Portal's default network.address is :19132) collapses this to just the https/http variants on that port. If none respond, the client falls back to RakNet on the same address — which only works if a RakNet listener is actually there too; Portal's own player listener is one or the other, not both simultaneously (see Configuration's network.transport).

Configuration reference

All of this lives under network.nethernet in config.json — see Configuration for the full file.

Key Purpose Default
tls.cert_file / tls.key_file PEM cert/key to serve signaling over HTTPS. Empty serves plain HTTP — clients still find it (see above), but HTTPS is recommended for anything reachable from the public internet "" / ""
ice_servers STUN/TURN servers offered for WebRTC NAT traversal, as [{"urls": [...], "username": "", "password": ""}] [] (none)
udp_ports UDP port, or "min-max" range, for the media channel "19133"

udp_ports — the one everyone gets bitten by

A single port (the default, "19133") is shared by every player connection through one UDP mux, and is the easiest to forward through a firewall or router. A range spreads connections across more ports but can run out under heavy load; leaving it empty lets the OS pick an ephemeral port per connection, which almost no firewall allows unsolicited inbound traffic to by default — don't do this anywhere but a fully open network.

This port must be unique on the host it runs on. It cannot be shared with:

  • RakNet's own port, if anything on the same machine uses RakNet (different protocol, but Portal's own default of 19133 was deliberately chosen distinct from RakNet's usual 19132).
  • Portal's own udp_ports value, if a NetherNet-transport backend server also happens to run on the same physical host as Portal itself (a very common setup for local development/testing). Two independent NetherNet listeners both trying to bind the same UDP port fails outright — see Troubleshooting for the exact error and fix.

If you're running Portal plus one or more NetherNet-transport backend servers all on one machine, give each of them (Portal included) its own distinct udp_ports value.

ice_servers — required for anything beyond the same LAN, often needed even on it

WebRTC needs a way for two peers to find a working network path. On the exact same machine (e.g. testing a client and Portal both on localhost), this works with no configuration because ICE can use direct "host" candidates. The moment a real second device is involved — even one on the same Wi-Fi network — that's no longer guaranteed, and without any STUN/TURN servers configured, ICE negotiation can simply time out (start ICE: context deadline exceeded in the log) even though signaling worked perfectly.

A public STUN server is free and enough to fix same-network and most NAT cases:

"ice_servers": [
  { "urls": ["stun:stun.l.google.com:19302"] }
]

For players behind restrictive/symmetric NATs or across the open internet where a STUN-assisted direct path still can't be found, you need a TURN server (relay) as well — STUN alone can't help there.

Firewall / port forwarding

Two things need to be reachable from wherever your players actually are:

  1. TCP on network.address's port (default 19132) — signaling.
  2. UDP on network.nethernet.udp_ports (default 19133) — media.

On Windows, the TCP side is often allowed automatically (or via the first-run "Allow this app" prompt), but the UDP media port commonly needs an explicit inbound rule:

New-NetFirewallRule -DisplayName "Portal NetherNet Media" -Direction Inbound -Protocol UDP -LocalPort 19133 -Action Allow

(Adjust the port to whatever network.nethernet.udp_ports is actually set to, and run this as Administrator.) If players outside your local network need to reach the proxy, both ports also need forwarding on your router, same as they would for RakNet.

Server identity and the TOFU prompt

Portal generates a temporary P-384 ECDSA key to identify itself to connecting clients every time it starts, logging:

WARN generating a new private key for this listener. a TOFU (Trust on First Use) prompt may be surfaced to players on first join

This is expected and harmless — it just means returning players may occasionally see a "trust this server" prompt again after a proxy restart, since the identity looks different each time. There is currently no config option to persist this key across restarts; if that matters for your deployment, open an issue.

A caveat worth knowing

The "client checks NetherNet before falling back to RakNet" behavior described above depends on the connecting player's Bedrock client actually supporting it. This is a relatively new client-side capability — most current clients do, but if a specific player genuinely can't connect while everyone else can, an outdated client is worth ruling out before assuming a server-side problem. See Troubleshooting for how to tell the two apart from the log.

See also

  • Backend Transports — the same NetherNet/RakNet choice, applied per backend server instead of the player listener
  • Troubleshooting — exact error messages and fixes for everything on this page

Clone this wiki locally