# Running on a server

> Ports, public URLs and certificates for rooms hosted on your own machines.

A room is an ordinary Node process, so it runs anywhere Node runs: your own
computer, a rented server, or a container. Start it directly, under a service
manager such as systemd, or in Docker. If the process exits or crashes, its
rooms end and disappear from the directory shortly after.

Running on a server mostly comes down to three things: letting players reach
the room through your firewall, telling players the right address, and
encryption, which the room handles for you.

## Ports

Players connect to a room over WebTransport, a modern browser protocol that
runs over UDP. So the server's firewall must allow incoming UDP traffic on the
room's port.

- Without `listen`, a room binds `0.0.0.0:4433`. If that port is taken, it
  tries the next one, up to 4532, and fails with `listen_failed` after that.
  One firewall rule, **UDP 4433–4532**, covers up to 100 rooms.
- An explicit `listen` port is used exactly. Port `0` picks a random free
  port. `room.url` reports the port in use.

## Public URL

Players need an address to connect to. Usually the room works it out by
itself: when `listen` is an unspecified address (`0.0.0.0` or `::`) and
`publicUrl` is omitted, the room advertises `https://<ip>:<port>/room`, using the address
disko observes for the host. It is re-read on every lease renewal.

Set `publicUrl` when the host is behind NAT or port forwarding, or has a
domain name:

```js title="arena.js"
import { readFileSync } from "node:fs";

import { Room } from "@disko-game/room";

const { ROOM_ID, ROOM_TOKEN, ROOM_PASSWORD } = process.env;

if (!ROOM_ID || !ROOM_TOKEN) {
  throw new Error("Set ROOM_ID and ROOM_TOKEN from the dashboard");
}

await Room.launch({
  id: ROOM_ID,
  token: ROOM_TOKEN,
  name: "EU arena",
  game: "gabriel/arena@1.4.2",
  // A fixed port and the address players connect to.
  listen: "0.0.0.0:4433",
  publicUrl: "https://arena.example.com:4433/room",
  // Optional: your own certificate, as PEM strings. Without it the room uses
  // a self-signed certificate that it rotates automatically.
  certificate: {
    certificatePem: readFileSync("/etc/arena/cert.pem", "utf8"),
    privateKeyPem: readFileSync("/etc/arena/key.pem", "utf8"),
  },
  password: ROOM_PASSWORD || null,
  logging: { format: "json" },
});
```

## Certificates

WebTransport connections are always encrypted, which needs a certificate. You
don't have to get one: without `certificate`, the room generates a self-signed ECDSA P-256
certificate valid for 13 days, as browsers require for WebTransport
certificate hashes, and rotates it every 6 days. Both hashes are advertised
during the overlap, so connecting players always get a valid one. No domain
or CA certificate is needed.

With `certificate`, the room uses your chain as PEM strings. Renew it by
restarting the room.

## Secrets

The hosting token and the room password are options, not files: read them
from your own secret store or environment. The token is never logged and is
sent only to disko. Rotating the token in the Creator Portal disconnects the
running host from the directory until it restarts with the new token.
