PlayMesh/Docs
HomeGitHubnpm

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

server.ts
import { PlayMesh } from '@playmesh/server';

const mesh = new PlayMesh({
  port: Number(process.env.PORT ?? 3000),
  redis: process.env.REDIS_URL ?? undefined,
  socket: {
    cors: {
      origin: process.env.CLIENT_ORIGIN ?? '*',
      methods: ['GET', 'POST'],
    },
  },
});
.env
PORT=4000
REDIS_URL=redis://localhost:6379
CLIENT_ORIGIN=https://game.example.com
JWT_SECRET=your-secret-here

Docker

Dockerfile

Dockerfile
FROM node:22-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
RUN npm run build
ENV NODE_ENV=production PORT=4000
EXPOSE 4000
CMD ["node", "dist/server.js"]

Docker Compose (with Redis)

docker-compose.yml
version: '3.9'
services:
  redis:
    image: redis:7-alpine
    volumes: [redis-data:/data]

  server:
    build: .
    environment:
      PORT: 4000
      REDIS_URL: redis://redis:6379
      CLIENT_ORIGIN: http://localhost:3000
    ports: ["4000:4000"]
    depends_on: [redis]

volumes:
  redis-data:

Kubernetes (multi-node)

Scale horizontally by running multiple pods. Each pod connects to the same Redis. Socket.IO's Redis adapter broadcasts across pods.
deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: playmesh-server
spec:
  replicas: 3
  selector:
    matchLabels: { app: playmesh-server }
  template:
    metadata:
      labels: { app: playmesh-server }
    spec:
      containers:
        - name: server
          image: your-registry/playmesh-server:latest
          ports: [{ containerPort: 4000 }]
          env:
            - name: REDIS_URL
              valueFrom:
                secretKeyRef:
                  name: playmesh-secrets
                  key: redis-url
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.

Attaching to an existing HTTP server

import express from 'express';
import { createServer } from 'node:http';
import { PlayMesh } from '@playmesh/server';

const app = express();
const httpServer = createServer(app);

app.get('/health', (_req, res) => res.json({ ok: true }));
app.get('/metrics', (_req, res) => res.json(mesh.metrics()));

const mesh = new PlayMesh({ server: httpServer, port: 4000 });
await mesh.start();

Graceful shutdown

process.on('SIGTERM', async () => {
  await mesh.shutdown();
  process.exit(0);
});

process.on('SIGINT', async () => {
  await mesh.shutdown();
  process.exit(0);
});