Mansarovar, JaipurPartner With Us
WhatsApp
Get a free quote

Node.js

Node.js in Production: 10 Mistakes That Slow Down or Crash Your App

Node.js apps that worked perfectly in testing often struggle after launch. These are the production mistakes behind slow APIs, memory leaks and midnight outages - and how to fix them.

Avinash SharmaFounder & CEO, MetaFox Technologies 7 min readPublished Updated
Cover: Node.js in Production: 10 Mistakes That Slow Down or Crash Your App

A Node.js application can look flawless in development and testing, then start misbehaving within weeks of launch: APIs that take several seconds to respond during busy hours, memory that climbs until the process restarts, or a crash at midnight with no clue why.

These problems usually come from a predictable set of production mistakes. Here are the ten we see most often when businesses ask us to stabilise a Node.js application, explained in plain English with practical fixes.

1. Blocking the event loop

Node.js runs your JavaScript on a single main thread with an event loop. While it waits for databases, files or other APIs, it serves other users. But if one request runs a long, CPU-heavy task - generating a large PDF, resizing images, parsing a huge Excel file, heavy encryption done synchronously - every other user waits.

Fix:

  • Move heavy work to background job queues (BullMQ with Redis is a popular choice) and notify the user when it is done.
  • Use worker threads for CPU-intensive tasks that must stay in Node.js.
  • Avoid synchronous functions (those ending in "Sync") in request handlers.
  • Process large files as streams instead of loading them fully into memory.

2. Slow database queries hiding behind "Node.js is slow"

Most "slow Node.js" complaints are really slow databases. A listing page that ran in milliseconds with a hundred test records can take seconds with a few hundred thousand real ones.

Fix:

  • Add indexes on columns used in filters, joins and sorting.
  • Use the database's query analyser (EXPLAIN in PostgreSQL and MySQL) to find slow queries.
  • Avoid the N+1 query problem, where code fetches a list and then runs one extra query per item.
  • Paginate lists; never return thousands of rows to a screen.
  • Use a connection pool sized to your database server.

3. No caching

Recalculating the same dashboard totals, product listings or settings on every request wastes resources.

Fix: Cache frequently read, rarely changed data in Redis or in memory with sensible expiry times. Use HTTP caching headers and a CDN for static assets and public API responses where appropriate. Be deliberate about invalidation - when a product's price changes, its cached entry must be cleared.

4. Running a single process on a multi-core server

A single Node.js process uses one CPU core for JavaScript. Deploy it once on a four-core server and most of the server sits idle while users wait.

Fix: Use PM2 cluster mode, the Node.js cluster module or multiple containers to run one process per core, behind Nginx or a load balancer. This also means one crashing process does not take the whole app down.

| Setup | Uses all cores | Survives one process crash | Zero-downtime reloads |
|---|---|---|---|
| Single "node app.js" | No | No | No |
| PM2 fork mode, one instance | No | Restarts it | No |
| PM2 cluster mode | Yes | Yes | Yes, with graceful reload |
| Multiple containers behind a load balancer | Yes | Yes | Yes, with rolling deploys |

5. Storing sessions and state inside the process

When you run multiple processes or servers, anything stored in a single process's memory - user sessions, rate-limit counters, uploaded files, WebSocket connection lists - is invisible to the others. Users get logged out randomly, or real-time messages reach only some people.

Fix: Keep shared state in Redis or the database, store uploads in object storage or a shared folder, and use an adapter (such as the Socket.IO Redis adapter) for real-time features across processes.

6. Unhandled errors and promise rejections

An unhandled exception or rejected promise can crash the Node.js process. If there is no process manager, the app simply stays down until someone notices.

Fix:

  • Use consistent error handling in every route, with a central error middleware.
  • Always handle promise rejections; use async/await with try/catch where needed.
  • Run under PM2, systemd or a container orchestrator that restarts crashed processes.
  • Treat every crash as a bug to fix, not something the restart hides.

7. Memory leaks

Memory that climbs steadily until the process restarts is a classic leak. Common causes are caches without size limits, event listeners added on every request, large objects held in global variables and timers that are never cleared.

Fix: Monitor memory per process over days, not minutes. Use bounded caches (LRU), remove listeners when done, and take heap snapshots to find what is growing. PM2's memory-based restart is a safety net, not a solution.

8. Calling external APIs inside the user's request

Sending an SMS, pushing data to a CRM, calling a shipping aggregator or generating an invoice email during a request makes your app as slow - and as unreliable - as the slowest third party.

Fix:

  • Move non-essential external calls to a queue and process them in the background.
  • Set timeouts on every outgoing request; never wait indefinitely.
  • Add retries with back-off for temporary failures.
  • Make webhook handlers, such as Razorpay payment webhooks, respond quickly and process the work asynchronously, while handling duplicate deliveries safely.

Our API integration and automation team designs integrations this way by default.

9. Poor logging and no monitoring

"The app was slow yesterday evening" is impossible to investigate if nothing was recorded.

Fix:

  • Use structured logging (JSON logs with a library like Pino) including a request ID, route, status code and response time.
  • Track key metrics: response times, error rates, event loop lag, memory and CPU per process, database query times.
  • Set up uptime monitoring and alerts to email or WhatsApp-connected channels so you hear about problems before customers do.
  • Never log passwords, OTPs or full personal data.

10. Outdated Node.js and dependencies

Running a Node.js version that has reached end of life means no more security fixes, and old dependencies often carry known vulnerabilities and performance issues.

Fix: Run an LTS release of Node.js, plan upgrades before end-of-life dates, audit dependencies on every build and update them in small, regular batches with tests. This is part of any sound website maintenance routine.

A production readiness checklist

| Area | Ready when... |
|---|---|
| Event loop | Heavy work runs in queues or worker threads |
| Database | Slow queries reviewed, indexes added, pagination everywhere |
| Caching | Hot data cached with clear invalidation |
| Processes | Cluster mode or multiple instances, auto-restart enabled |
| Shared state | Sessions, rate limits and sockets backed by Redis or DB |
| Errors | Central handling, no unhandled rejections |
| External calls | Timeouts, retries, background processing |
| Observability | Structured logs, metrics, uptime alerts |
| Versions | Node.js LTS, dependencies audited |
| Deployments | Zero-downtime reloads and a tested rollback plan |
| Backups | Automated database backups with tested restores |

Prepare for your busiest days

Many Indian businesses see traffic spikes around predictable events - festive sales around Diwali, end-of-season offers, admission seasons, exam results or a campaign going viral on Instagram. These are exactly the days you cannot afford an outage.

  • Load test before the event with a tool such as k6, Artillery or autocannon, using realistic user journeys rather than a single URL.
  • Watch the database first - it is usually the first thing to struggle under load.
  • Scale ahead of time: add processes or server capacity before the spike, not during it.
  • Freeze risky deployments in the days before a big event.
  • Have a rollback plan and someone on call who knows the system.
  • Review afterwards what slowed down and fix it before the next event.

Do you need to rebuild?

Rarely. Most struggling Node.js applications improve significantly with targeted fixes: adding indexes, moving slow work to queues, enabling cluster mode, introducing caching and adding monitoring. A full rewrite should be a last resort, considered only after profiling shows the architecture itself is the limit.

Key takeaways

  • Never block the event loop with CPU-heavy work; use queues and worker threads.
  • Database indexes, pagination and caching solve most "slow API" complaints.
  • Run multiple processes and keep shared state in Redis or the database.
  • Handle errors properly and monitor memory, latency and error rates.
  • Put timeouts and background processing around every external API.
  • Stay on a supported LTS version with regular dependency updates.

Is your Node.js app slowing down or crashing under real traffic? Book a free consultation and our team will review your setup and share a clear stabilisation plan. Learn more about our Node JS development and cloud and DevOps services.

Written by

Avinash Sharma

Founder & CEO, MetaFox Technologies, MetaFox Technologies

Avinash Sharma is the founder of MetaFox Technologies, a Jaipur-based company that builds websites, CRM, custom software and digital marketing systems for small and medium businesses. He writes about practical ways businesses can win more customers online.

FAQ

Frequently asked questions

Common causes are slow database queries without indexes, calls to external APIs inside the request, or a single CPU-heavy operation blocking the event loop. Profiling and query analysis usually reveal the culprit quickly.

A Node.js process runs JavaScript on one main thread. To use multiple cores, run several processes using PM2 cluster mode, the built-in cluster module or multiple containers behind a load balancer.

Typical causes include caches that grow without limits, event listeners that are added but never removed, global variables holding large data and long-lived closures. Monitoring memory over time and taking heap snapshots helps identify them.

Most business applications do not. A well-configured VPS with PM2 or Docker, Nginx, monitoring and backups serves many products reliably. Kubernetes makes sense for larger, multi-service systems with a team to operate it.

Call +91 98877 61886Chat on WhatsApp