Slow Node.js on a VPS: Memory Pressure or a Blocked Event Loop?
Use a repeatable investigation to distinguish a busy event loop from a memory leak or a slow dependency.
When a Node.js API slows down, adding memory may help one failure mode and leave another untouched. A growing heap, a large synchronous transformation, an overloaded database, and a slow upstream service can all appear as delayed HTTP responses. Build an evidence trail that connects the slow request to process behavior before changing a LayerOne VPS size. The most useful first question is what changed when latency increased. Note whether the issue began after a release, a dataset growth milestone, or a change in request mix; each suggests a different comparison to run.
Capture a comparable baseline
Choose representative routes and record request rate, response duration, error rate, process memory, and CPU consumption during the same time window. Include a heavier route such as report generation alongside ordinary reads. Keep request inputs and dataset size consistent between experiments. An idle process measurement tells you little about peak work. If you have no baseline, reproduce the symptom in staging with a bounded load and stop before resource exhaustion obscures the initial cause.
Recognize event loop contention
The Node.js event loop guide explains why long callbacks delay other clients. Large synchronous parsing, expensive regular expressions, and CPU intensive transformations deserve scrutiny. An asynchronous function declaration does not move ordinary JavaScript computation onto a separate processor. If a CSV export makes unrelated health requests slow at the same moment, investigate shared event loop work. Consider streaming, smaller batches, or an appropriate worker architecture after measuring the expensive section.
Interpret memory growth over time
Observe whether memory settles after traffic falls or grows with every repeated operation. A cache intentionally retaining recent objects behaves differently from a collection that never releases old entries. Measure resident memory as well as JavaScript heap metrics because buffers and runtime allocations matter to the VPS. Diagnostic snapshots can be disruptive and may contain sensitive application data; capture them in a controlled environment with enough headroom. Increasing a heap limit is not a substitute for understanding retained objects.
Trace slow dependencies separately
A request may spend almost all its time waiting for PostgreSQL or an external API while Node CPU stays modest. Measure those spans and inspect connection pool waiting time. A burst of promises can create more simultaneous downstream work than the database can handle. Bound concurrency and set timeouts with an application level recovery policy. For example, fetching ten independent reports in parallel may be reasonable; launching ten thousand database queries at once can merely relocate the queue.
Validate one change at a time
Repeat the same workload after each change and compare high percentile latency, errors, throughput, and memory stability. If a background worker removes a CPU spike from requests, include its resource use in the total budget. If memory remains the limiting resource after application fixes, compare current VPS memory options. Keep the lifecycle controls from the Node process management guide in place so a restart produces useful evidence and a controlled recovery.