Skip to content

Running on multiple servers

By default Warlock keeps state in the process: memory cache, local disk, in-process socket rooms, in-process counters. That is right for one server. With two or more, each piece needs a shared backend. In production Warlock warns once at boot for the ones it can detect.

ConcernSingle-server defaultMulti-server setupSilence the warning
Cache, repository cache, locksMemory driverredis or pg cache driver — see Cachecache.silenceSingleServerWarning
FilesLocal diskS3 or R2 — see Storage and Upload to S3storage.silenceSingleServerWarning
Global rate limitIn-processhttp.rateLimit.redisn/a
SocketsIn-process roomssocket.adapter + sticky sessionssocket.silenceSingleServerWarning
Scheduled jobsRuns on every serveronOneServer — see Overlap preventionn/a
Idempotency keysCache-backedShared cache (above)n/a

http.rateLimit passes @fastify/rate-limit options through:

import Redis from "ioredis";
export default {
rateLimit: {
max: 100,
duration: 60_000,
redis: new Redis(process.env.REDIS_URL),
},
};

enabled: false turns the global limiter off. The per-route middleware.rateLimit() stays in-process, so its counters are per server.

import { createAdapter } from "@socket.io/redis-adapter";
export default {
adapter: () => createAdapter(pubClient, subClient),
};

Enable sticky sessions on the load balancer so long-polling requests from one client reach the same server.

middleware.idempotency() reserves the key before the handler runs; a concurrent duplicate gets 409 + Retry-After. That only holds across servers when the cache is shared.

The repository cache is cleared after the model event and again after the transaction commits. With a memory cache, other servers keep their own copies — use redis or pg.