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. Placement is automatic.
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 |
|---|---|
| Standard | 20 |
| Large | 8 |
| Extra 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.
Additional servers depend on your plan and regional capacity:
| 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. Email support@irt.io about capacity in 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 this notice, 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.
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
These intervals affect recovery time:
| 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. |
Play on a superseded server is discarded. The replacement room resumes from its last save, so changes made after that save can be lost.
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. Use send for delivery across servers.
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.
Recovery constraints
Moving a room restores its saved state and reconnects clients. Placement is automatic; applications cannot select a server or require rooms to share one.
A server that can reach storage but cannot accept player connections remains assigned to its rooms. Those rooms require operator intervention; client connection failures alone do not trigger a move.