Deployment
Running PlayMesh in production - Redis, multi-node, Docker, and environment configuration.
Single-node vs. multi-node
Single-node
- No Redis required
- In-memory state & presence
- No background queues
- Great for development
Multi-node (Redis)
- Horizontal scaling
- Distributed state & presence
- BullMQ queues available
- Required for production at scale
Environment configuration
Redis namespaces
Multiple deployments (or environments) can share one Redis by giving each a namespace:
Every Redis key PlayMesh writes is prefixed with the namespace - presence, unique-user claims, synced state and its revision counters, the shared signing keypair, the BullMQ queue prefix (
<namespace>:bull - queue names are unchanged), and Socket.IO adapter channels (<namespace>:socket.io). Deployments with different namespaces never interact. Namespaces must be non-empty and use only letters, digits, hyphens, underscores, colons or periods.ℹ
The default namespace is
playmesh, which produces exactly the pre-0.6 keys - upgrading without setting namespace reads all your existing Redis data. Switching an existing deployment to a new namespace abandons the old keys (nothing is migrated): drain BullMQ queues first and clean up the old keys manually.Serialization
JSON is the default wire encoding.
serialization: 'msgpack' switches Socket.IO to socket.io-msgpack-parser - binary frames, usually noticeably smaller for game traffic. Server and every client must use the same encoding; a mismatched client cannot connect, so switch both at once. Keep payloads JSON-compatible either way (signing requires it).Admin tools
Build admin panels on top of these instead of tracking rooms and players yourself. The commands reach a user's sessions on any node.
ℹ
Instance members in
inspect()are cluster-wide; session details (roles, addresses) are only known to the node that holds the connection. Instances created dynamically on another node only appear in that node's inspect().Crash recovery
When a node crashes, its sessions stay in shared presence as ghosts until the presence sweeper reaps them (once their heartbeat is 60 seconds old). Every node sweeps periodically, and a restarted node also sweeps on startup. Each ghost is claimed atomically, so its leave is broadcast - and
onMemberLeft runs - exactly once across the cluster. Put per-player cleanup there instead of a manual cleanup step on startup:Upgrades
⚠
@playmesh/server and @playmesh/client must run matching releases. The wire protocol is versioned in the handshake, and a mismatch is rejected on both sides with code VERSION_MISMATCH - deploy server and client upgrades together.0.6 to 0.7
0.7.0 moves to wire protocol v3, so 0.6 clients cannot connect to 0.7 servers (and vice versa). Custom
StateStore implementations must add patch(), ServerError.code is always present, and connection-setup failures now reject connect()with the server's real message and code CONNECTION_FAILED. If you enable msgpack, switch server and clients in the same deploy. The full guide, including a table mapping common workarounds to the new APIs, is in MIGRATION.md.Docker
Dockerfile
Docker Compose (with Redis)
Kubernetes (multi-node)
Scale horizontally by running multiple pods. Each pod connects to the same Redis. Socket.IO's Redis adapter broadcasts across pods.
ℹ
WebSocket connections require sticky sessions at the load balancer. Configure your ingress with cookie-based or IP-based session affinity.
Cloud platforms
Fly.io
Native multi-region support. Use Fly Redis. Enable sticky sessions in fly.toml.
Railway
Add a Redis service. Set REDIS_URL from Railway's connection string.
AWS ECS
Use ElastiCache for Redis. Configure ALB with duration-based sticky cookies.
AWS EKS
Same as the Kubernetes setup. Use ElastiCache for Redis.
DigitalOcean
App Platform + Managed Redis. Enable session affinity in app settings.
Heroku
Add Redis Cloud. REDIS_URL is set automatically.