Servers and room placement
irtio decides which server each of your rooms runs on. A project starts on one server and gets another when its rooms need one. There is nothing to configure and no route to call.
A room is not tied to a server for its life. When the server holding a room stops answering, irtio places that room on another one, and it wakes there from its last save.
When a project gets another server
irtio adds a server when a join needs a room and every server the project already has is at its room budget. The budget is per server and per size class:
| Size class | Awake rooms per server |
|---|---|
| Small | 20 |
| Medium | 8 |
| Large | 3 |
A room counts against the budget while it is awake. Asleep rooms cost nothing, so a project with thousands of rooms and twenty awake ones stays on one server.
Two things bound it:
| Condition | What happens |
|---|---|
| The project is on the free plan | The project stays on one server. The free tier’s awake-room limit refuses room N+1 by name. |
| No server in the project’s region has room | The join answers E_PLACEMENT, naming the region. Add capacity to that region. |
Adding a server does not disturb the rooms already running, and nothing restarts.
What a player sees when a room moves
A room moves for one reason: the server holding it stopped answering. Two things reach the client.
E_ROOM_MOVED (close 4292). Routine. The room now runs somewhere else, and the client SDK
reconnects on its own and lands on the new server. Your game does not have to handle this.
E_ROOM_UNSAVABLE (close 4293). Final. The server could reach neither irtio nor storage, so it
stopped the room rather than keep taking input it could not save. The SDK does not reconnect, and room.on('error') fires with E_ROOM_UNSAVABLE. Offer your own “rejoin” button if you want one.
Before a room is stopped for that reason, its players are warned:
// client
room.on('notice', (n) => {
if (n.phase === 'warning') banner(`Connection trouble. Progress may be lost.`);
if (n.phase === 'recovered') banner('');
if (n.phase === 'closing') banner('This room is ending. Recent progress was not saved.');
}); The warning reaches every client in a room that runs your room code. A relay room does not send it in this release, so a project that has deployed no room file gets the close code with no warning ahead of it. A room with code can take the announcement over:
// irtio/room.ts
onPlatformNotice(state, notice) {
state.banner = notice.phase === 'recovered' ? '' : 'Connection trouble';
return false; // irtio sends nothing; your UI says it
} Return nothing to accept the built-in notice as well. Throwing does not stop it: a handler that threw has said nothing, and the players still get told.
What a room loses when a server fails
Everything since the room’s last write. A room writes a snapshot when it hibernates. While it is awake its latest state is in memory, and an awake room can go hours without writing anything, so a server that fails without warning loses the ticks since that snapshot. This is true whenever a server fails and is not new.
For a durable point of your own, call room.save() at the moments your
game cannot afford to lose. A save writes the same bytes hibernation writes, without interrupting the
room.
How long a move takes
Two numbers bound what a player can see, and both are worth knowing before you design around them.
| Bound | Value | What it covers |
|---|---|---|
| Server heartbeat | 15 seconds | How long a failed server can keep serving players who are already connected before it is told to stop. |
| Edge cache | 30 seconds | How long a new join can still be sent to the old server. |
Neither loses state. Play on a superseded server is discarded rather than merged, so the room that comes back on the new server is the room as of its last save.
The room bus across servers
room.bus.send reaches any room in your project, whichever server it is on. Same server, nothing
changes. On a different one, the message is held in the sending room’s own outbox, forwarded by
irtio, and delivered. The promise resolves the same way it always does: it succeeds, or it rejects
with E_BUS_NO_SUCH_ROOM or E_BUS_MAILBOX_FULL.
A message can arrive twice, when a retry fires while the target is still loading. Handle a repeated send as you would any other duplicate. See the room bus.
room.bus.publish reaches subscribers on the sending room’s own server only. See what placement does not do.
Limits per server
Several limits are enforced per server rather than per project, so a project on three servers has three servers’ worth of each:
- New connections per IP per minute, and the
HELLOrate limit. - The free tier’s awake-rooms limit. A free project stays on one server, so this is exact for it.
- The room budget in the table above.
Client ids stay unique within a room however many servers a project runs on.
Relay projects
A project with no room code spreads the same way, and per room: its second room goes to a server with memory when the first room’s server has none. It costs less because there is no room code to run, not because it is placed differently.
What placement does not do
- Move an awake room live. A room moves by hibernating and waking, so a move ends the current session and the clients reconnect.
- Let you choose a server. You cannot ask for a room to be placed on a particular server, or ask for two rooms to be placed together.
- Carry
publishacross servers.room.bus.publishreaches subscribers on the sending room’s own server. Usesendto reach a room on any server. - Move a room whose server can reach storage but not its players. Those rooms stay where they are and are unreachable until an operator intervenes. irtio does not move a room because clients cannot dial it.