Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
// pm2 deployment for the central backend.
//
// npm run build && pm2 start ecosystem.config.cjs (or `npm run start` → pm2-runtime, in Docker)
//
// The HTTP/WS server is stateless and clusters cleanly. Schema/catalog/reconcile jobs live in a
// separate singleton worker so no server process owns background state.
//
// Runs on Kubernetes → default to 'max' (use every core in the pod). Caveat: pm2 'max' reads
// os.cpus().length, which under a cgroup CPU *limit* still reports the NODE's cores, not the pod's
// limit → it can over-subscribe. If the pod has a CPU limit, pin BACKEND_INSTANCES from the
// downward API (resources.limits.cpu) instead of relying on 'max'.
const instances = process.env.BACKEND_INSTANCES
? (process.env.BACKEND_INSTANCES === 'max' ? 'max' : Number(process.env.BACKEND_INSTANCES))
: 'max'
module.exports = {
apps: [
{
name: 'backend-worker',
script: 'dist/worker.js',
exec_mode: 'fork',
instances: 1,
wait_ready: true,
listen_timeout: 120000,
kill_timeout: 6000,
env: { NODE_ENV: 'production' },
},
{
name: 'backend',
script: 'dist/server.js',
exec_mode: 'cluster',
instances,
// Shutdown drains websockets (1012) before exit — give it room: 4s close grace + Redis quit.
kill_timeout: 10000,
// Each cluster worker has its own V8 heap, so the container's memory limit must cover
// instances × heap (plus ~50% for buffers / native). Without an explicit ceiling V8 sizes the old
// space from the HOST's RAM, and a leak on one worker surfaces as the OOM-killer taking the whole pod
// instead of pm2 recycling one process. BACKEND_HEAP_MB / BACKEND_MAX_MEMORY tune both together.
node_args: `--max-old-space-size=${process.env.BACKEND_HEAP_MB || 1024}`,
max_memory_restart: process.env.BACKEND_MAX_MEMORY || '1536M',
env: { NODE_ENV: 'production' },
},
],
}
442335385This file is pinned to the selected release. Download the original to open it in your design tool.