Frequently Asked Questions
Performance & Scaling
How many CCU can a Colyseus server handle?
The maximum number of concurrent users (CCU) a Colyseus server can handle depends on several factors:
- CPU: How intensive your game loop and message handlers are. Complex physics or AI calculations will limit CCU.
- RAM: Each connection and room state consumes memory. A typical connection uses ~2-5KB, but this grows with your state size. Monitor memory usage to avoid OOM crashes.
- Bandwidth: The frequency and size of state patches sent to clients. Use State View to reduce per-client traffic.
- File descriptors: Linux servers default to ~1024 open connections. This can be increased via
ulimit -n(e.g.,ulimit -n 65535), but ensure your hardware can handle the load.
As a baseline, a small cloud server (1 vCPU, 1GB RAM) can typically handle 1,000-2,000 concurrent connections for simple games. For complex games with large state, expect fewer. Always load test your specific use case.
How can I improve performance?
- Reduce state size: Only include data that needs to be synchronized. Use messages for transient data.
- Optimize patch rate: The default
patchRateis 50ms. Increase it if your game doesn’t need frequent updates. - Use State View: Limit what each client sees using State View to reduce bandwidth per client.
- Profile your room code: Avoid heavy computations.
Does Colyseus help me with client-prediction?
Not yet—client-prediction is planned for a future release (see Roadmap). For now, see our Phaser tutorial for a reference implementation.
Connection Issues
I’m getting "Error: seat reservation expired", what does it mean?
This error means that the client was not able to effectively connect with the server’s room in time.
This error may appear in a few scenarios:
- Server is under heavy load and by the time it can respond to a WebSocket connection request, the “seat reservation” code has already expired. You may increase the seat reservation time limit to remedy this.
- You have mixed Colyseus package versions in your
package.json. (between0.14.xand0.15.x, for example) Make sure to only use packages under0.15.x(if you are using version 0.15), OR0.14.x(if you are using version 0.14) - the exact patch version does not matter, but major and minor matters. - When using multiple Node.js processes, your scalability configuration may not be correct. Make sure to follow scalability instructions.
How do I handle reconnection?
When a client disconnects (e.g., closed browser tab), you can allow them to reconnect to the same room:
- Store the
room.reconnectionTokenon the client after joining - Use
client.reconnect(reconnectionToken)to rejoin - On the server, set
allowReconnectioninonLeave:
import { CloseCode } from "colyseus";
// ...
async onLeave(client, code) {
if (code !== CloseCode.CONSENTED) {
// Allow reconnection for 30 seconds
await this.allowReconnection(client, 30);
}
}State Synchronization
Why is my room state not synchronizing with the client?
If you are using TypeScript to target ES2022/ESNext, you need to provide "useDefineForClassFields": false on your tsconfig to make sure @type() decorators are defining property accessors properly.
{
"compilerOptions": {
"useDefineForClassFields": false,
"experimentalDecorators": true
}
}See colyseus/colyseus#510 for more details.
How can I sync data only to a specific client?
Support for per-client state visibility was introduced in version 0.16. You can use the StateView class to control which parts of the state are visible to each client. See State View for more details.
When should I use state vs messages?
| Use State when… | Use Messages when… |
|---|---|
| Data needs to persist | Event is one-time |
| All/new clients need the value | Only current clients care |
| Data changes frequently | Triggering an action/effect |
| Example: positions, scores | Example: chat, events |
TypeScript Errors
I’m getting this error: Class constructor Room cannot be invoked without 'new'", what should I do?
Make sure you have es2015 or higher in your tsconfig.json:
{
"compilerOptions": {
"target": "es2015"
}
}TypeScript decorators aren’t working
Ensure these settings in your tsconfig.json:
{
"compilerOptions": {
"experimentalDecorators": true,
"useDefineForClassFields": false
}
}Unity SDK
Unity WebSocket connection drops during debugging
If you set a breakpoint in your application while the WebSocket connection is open, the connection will be closed automatically after 3 seconds due to inactivity. To prevent this, use pingInterval: 0 during development:
import { defineServer } from "colyseus";
import { WebSocketTransport } from "@colyseus/ws-transport";
const server = defineServer({
transport: new WebSocketTransport({
pingInterval: 0, // Disable for debugging
}),
});Remember to set pingInterval back to a positive value (default: 3000) in production.
How do I test multiple Unity clients locally?
Starting with Unity 6000.1.0b1, use Multiplayer Play Mode. For older versions, use ParrelSync.
Rooms & Architecture
When should I create separate room types?
Create separate room types when:
- Different game modes have different logic (e.g.,
BattleRoom,LobbyRoom) - Different state structures are needed
- Different max players or configurations
Reuse room types when:
- The same game can have multiple instances (most common case)
- Only configuration differs (pass options on join)
Can rooms communicate with each other?
Yes, using Presence. Rooms can publish/subscribe to channels to coordinate across instances:
// In Room A
this.presence.publish("game-events", { type: "tournament-start" });
// In Room B
this.presence.subscribe("game-events", (message) => {
console.log("Received:", message);
});How do I persist room data?
Rooms are ephemeral by default. To persist data:
- Use a database (Postgres, MongoDB, Redis) to save game state
- Load state in
onCreate(), save inonDispose() - See Database for integration guides