feat(server): built-in Node.js cluster mode — all services share one process, impossible to scale independently
M.Zandi
Strapi version:
5.x (latest)Node.js version:
20.xPackage manager:
yarn 4.12.0Problem:
server/index.ts
mounts the admin panel, content API, upload handler,cron jobs, and webhook runner all on one Koa app in one OS process.
There is no built-in mechanism to run multiple worker processes
(Node.js cluster mode). This means:
- You can only scale the entire Strapi process — you cannot scale
just the Content API under traffic while leaving the admin panel
on a single instance.
- Running multiple instances externally (via pm2 or Kubernetes replicas)
causes cron jobs to fire on every instance simultaneously, producing
race conditions and duplicate side-effects (duplicate emails, duplicate
webhook calls). See also: #15634.
- The cron service (cron.ts) usesnode-schedulewith no leader-
election or instance awareness — every instance runs every cron task.
What the code does today:
In
packages/core/core/src/services/server/index.ts
, createServer()
builds one Koa app with both
createAdminAPI(strapi)
andcreateContentAPI(strapi)
on the same process and same port.In
packages/core/core/src/services/cron.ts
, createCronService()
schedules jobs unconditionally with no check for whether this process
is the designated primary.
Proposed solution (monolith fix, no microservices required):
Add opt-in built-in cluster support:
- A server.cluster.enabledconfig flag (default: false).
- When enabled, Strapi's entry point forks N worker processes using
Node.js
cluster
module (default: os.cpus().length
).- A STRAPI_INSTANCE_TYPEenvironment variable (primary/worker)
lets services guard singleton behaviour:
- Cron jobs only run on the primary process.
- Webhook runner only runs on the primary process.
- Database migrations only run on the primary process.
- Workers handle all HTTP traffic (load-balanced by the OS).
Related issues:
- #3976 (cluster mode question, closed 2019, no code change made)
- #15634 (cron race condition, open/stale, no PR merged)
I plan to submit a PR implementing this.
reproduce:
- Deploy two Strapi v5 instances pointing to the same database
(e.g. two pm2 instances or two Kubernetes pods).
- Configure a cron task in config/cron-tasks.tsthat sends an email
or writes a unique log entry.
- Wait for the cron schedule to fire.
- Observe: the cron task executes on BOTH instances simultaneously,
producing duplicate emails or duplicate log entries.
For the scaling limitation:
- Monitor CPU usage on a single Strapi instance under load.
- Attempt to scale only the content API (e.g. by running a second
instance that only serves
/api/*
routes).- Observe: impossible without running a full second Strapi process,
which doubles the cron, webhook, and admin overhead unnecessarily.
#Expected behavior
When
server.cluster.enabled: true
is set in config:- Strapi should fork N worker processes automatically.
- Only the primary process should run cron jobs and webhook delivery.
- All workers should handle HTTP requests, load-balanced by the OS.
- Database migrations should run once on the primary before workers start.
- The behaviour should be identical to the current single-process mode
when
cluster.enabled
is false (default), so there is zero breakingchange for existing deployments.