How to Create a Backend Resource Utilization Monitor in NodeJs?

By Atit Purani

March 13, 2026

Why is your Node.js server slowing down under load?

Learn to track CPU usage, heap memory, and event loop lag in real time using Node.js built-in modules, no third-party packages needed.

Why Should You Care About Server Health?

Imagine you’re running a restaurant. Everything looks fine from the outside, the lights are on, & customers are walking in.

But in the kitchen, the stove is overheating, the chef is exhausted, & the storage room is almost full. If nobody checks, the whole kitchen breaks down during the dinner rush.

Your backend server works the same way. A backend server is the engine running behind every app or website.

It handles requests, crunches data, and sends responses back to users.

But if that server is using too much CPU (processing power) or RAM (memory), it starts to slow down or crash entirely.

This is where a Backend Resource Utilization Monitor comes in.

It watches your server’s health in real time, just like a dashboard in a car shows you speed, fuel, and engine temperature.

What Is Backend Resource Utilization?

Every computer, including a server, has two main resources it uses to do work:

  1. CPU (Central Processing Unit): Consider it as your brain. The more tasks it handles at once, the more stressed it gets. If CPU usage hits 100%, things grind to a halt.
  2. RAM / Memory: It is kind of your desk space. The more apps running, the more desk space gets filled. When the desk is full, you can’t put anything new on it.

“Resource utilization” simply means: how much of these resources is your server currently using?

Monitoring this tells you:

  • Is my server under too much load?
  • Is there a memory leak slowly eating up my RAM?
  • Should I upgrade my server capacity?
  • Is something broken and consuming resources silently?

Why Build Your Own Node.js Performance Monitor?

Why-Build-Your-Own-Nodejs-Performance-Monitor

There are many commercial APM (Application Performance Monitoring) tools out there, like Datadog, New Relic, & Dynatrace. They are powerful, but:

  • They cost money (often hundreds of dollars per month).
  • They add complexity to your project.
  • They may be overkill for small or medium projects.
  • They require external services and accounts.

Building your own Node.js resource utilization monitor using the built-in OS module & process object gives you full control, zero cost, & a deep understanding of what’s happening inside your server.

How Do We Build Backends That Stay Healthy?

We engineer Node.js backends designed for performance, observability, and long-term reliability.

  • Every backend we ship is built with CPU efficiency & memory management in mind, using async patterns, optimized event loops, & lean architecture from day one.
  • We integrate /health endpoints, resource tracking, & alerting into every production backend, the same patterns you just learned in this guide, applied at scale.
  • Our code reviews specifically catch common Node.js pitfalls: Unclosed connections, unbounded caches, and detached event listeners, before they ever reach production.
  • We connect your backend metrics to dashboards your whole team can understand, not just developers.

Want a Scalable Backend for Your NodeJs Solution?

Talk With Our NodeJs Experts Now!

Here’s the

Complete GitHub Code

Step-By-Step Process to Build a Backend Resource Utilization Monitor in NodeJs

Step-By-Step-Process-to-Build-a-Backend-Resource-Utilization-Monitor-in-NodeJs

Here are the complete steps with code to build a backend resource utilization monitor in NodeJs.

Step 1: Set Up Your Project

Open your terminal and create a new project folder:

        
                mkdir node-resource-monitor
                cd node-resource-monitor
                npm init -y

        
        

This creates a basic Node.js project. Now create your main file:

        
                touch monitor.js
        
        

Step 2: Monitor CPU Usage with process.cpuUsage()

Node.js gives us process.cpuUsage(), a built-in function that returns how many microseconds of CPU time your process has consumed.

To get a meaningful percentage, we measure two snapshots and calculate the difference.

Add this to your monitor.js file:

        
                // monitor.js
                function getCpuUsagePercent() {
                const start = process.cpuUsage();
                
                // Wait 100ms and measure again
                return new Promise((resolve) => {
                        setTimeout(() => {
                        const end = process.cpuUsage(start);
                
                        // Convert microseconds to percentage over 100ms
                        const totalMicros = 100 * 1000; // 100ms = 100,000 microseconds
                        const cpuPercent = ((end.user + end.system) / totalMicros) * 100;
                
                        resolve(Math.min(cpuPercent, 100).toFixed(2));
                        }, 100);
                });
                }

        
        

Step 3: Monitor RAM and Heap Memory with process.memoryUsage()

Node.js gives us process.memoryUsage(), which returns detailed memory stats. The most important ones are:

  • heapUsed: Memory actively used by your JavaScript objects.
  • heapTotal: Total heap memory allocated by the V8 engine.
  • rss (Resident Set Size): Total memory occupied by the process.
        
                function getMemoryUsage() {
                const mem = process.memoryUsage();
                
                return {
                        heapUsedMB: (mem.heapUsed / 1024 / 1024).toFixed(2),
                        heapTotalMB: (mem.heapTotal / 1024 / 1024).toFixed(2),
                        rssMB: (mem.rss / 1024 / 1024).toFixed(2),
                        externalMB: (mem.external / 1024 / 1024).toFixed(2),
                };
                }
        
        

Step 4: Read Total System Memory with the OS Module

The OS module gives us system-level stats, total RAM, and free RAM available on the machine:

        
                const os = require('os');
 
                function getSystemMemory() {
                const totalMem = os.totalmem();
                const freeMem = os.freemem();
                const usedMem = totalMem - freeMem;
                
                return {
                        totalMemGB: (totalMem / 1024 / 1024 / 1024).toFixed(2),
                        freeMemGB: (freeMem / 1024 / 1024 / 1024).toFixed(2),
                        usedMemGB: (usedMem / 1024 / 1024 / 1024).toFixed(2),
                        memUsagePercent: ((usedMem / totalMem) * 100).toFixed(2),
                };
                }
        
        

Step 5: Detect Event Loop Lag

This is a Node.js-specific metric. The event loop is the heart of Node.js; it’s how Node handles multiple tasks without using multiple threads.

If the event loop is “lagging”, it means your server is too busy to respond quickly. We measure lag by scheduling a tiny timer and checking how late it fires:

        
                function measureEventLoopLag() {
                return new Promise((resolve) => {
                        const start = Date.now();
                
                        // This should fire almost immediately (after 0ms)
                        setImmediate(() => {
                        const lag = Date.now() - start;
                        resolve(lag); // lag in milliseconds
                        });
                });
                }
        
        

Step 6: Build the /health API Endpoint

Now let’s combine everything into a real HTTP endpoint. This is a standard pattern in production servers, a /health route that returns the server’s current status as JSON:

        
                const http = require('http');
                const os = require('os');
                
                // --- Helper functions (paste your functions from Steps 2-5 here) ---
                
                async function getHealthReport() {
                const [cpuPercent, eventLoopLag] = await Promise.all([
                        getCpuUsagePercent(),
                        measureEventLoopLag(),
                ]);
                
                return {
                        timestamp: new Date().toISOString(),
                        status: 'ok',
                        cpu: {
                        usagePercent: parseFloat(cpuPercent),
                        },
                        memory: getMemoryUsage(),
                        system: getSystemMemory(),
                        eventLoop: {
                        lagMs: eventLoopLag,
                        healthy: eventLoopLag < 50,
                        },
                        uptime: {
                        processSeconds: Math.floor(process.uptime()),
                        systemSeconds: Math.floor(os.uptime()),
                        },
                };
                }
                
                const server = http.createServer(async (req, res) => {
                if (req.url === '/health') {
                        const report = await getHealthReport();
                        res.writeHead(200, { 'Content-Type': 'application/json' });
                        res.end(JSON.stringify(report, null, 2));
                } else {
                        res.writeHead(404);
                        res.end('Not found');
                }
                });
                
                server.listen(3000, () => {
                console.log('Resource monitor running at http://localhost:3000/health');
                });

        
        

Step 7: Add Real-Time Console Logging

In addition to the HTTP endpoint, it’s useful to log stats to the console at regular intervals, great for development and debugging:

        
                async function logStats() {
                const [cpu, lag] = await Promise.all([
                        getCpuUsagePercent(),
                        measureEventLoopLag(),
                ]);
                
                const mem = getMemoryUsage();
                const sys = getSystemMemory();
                
                console.log('\n====== Resource Monitor ======');
                console.log(`[${new Date().toISOString()}]`);
                console.log(`CPU Usage  	: ${cpu}%`);
                console.log(`Heap Used  	: ${mem.heapUsedMB} MB / ${mem.heapTotalMB} MB`);
                console.log(`Process RSS	: ${mem.rssMB} MB`);
                console.log(`System Memory  : ${sys.usedMemGB} GB / ${sys.totalMemGB} GB (${sys.memUsagePercent}%)`);
                console.log(`Event Loop Lag : ${lag}ms ${lag > 50 ? '⚠ HIGH' : '✓ OK'}`);
                console.log('==============================');
                }
                
                // Log every 5 seconds
                setInterval(logStats, 5000);
                logStats(); // Run immediately on start
        
        

The Complete Code for monitor.js File

Here is the complete, ready-to-run file. Copy this into your monitor.js:

        
                / monitor.js — Backend Resource Utilization Monitor in Node.js
                // No third-party packages needed!
                
                const http = require('http');
                const os = require('os');
                
                // ── CPU Usage ──────────────────────────────────────────────────
                function getCpuUsagePercent() {
                const start = process.cpuUsage();
                return new Promise((resolve) => {
                        setTimeout(() => {
                        const end = process.cpuUsage(start);
                        const totalMicros = 100 * 1000;
                        const cpuPercent = ((end.user + end.system) / totalMicros) * 100;
                        resolve(Math.min(cpuPercent, 100).toFixed(2));
                        }, 100);
                });
                }
                
                // ── Heap Memory ────────────────────────────────────────────────
                function getMemoryUsage() {
                const mem = process.memoryUsage();
                return {
                        heapUsedMB: (mem.heapUsed / 1024 / 1024).toFixed(2),
                        heapTotalMB: (mem.heapTotal / 1024 / 1024).toFixed(2),
                        rssMB: (mem.rss / 1024 / 1024).toFixed(2),
                        externalMB: (mem.external / 1024 / 1024).toFixed(2),
                };
                }
                
                // ── System Memory ──────────────────────────────────────────────
                function getSystemMemory() {
                const totalMem = os.totalmem();
                const freeMem = os.freemem();
                const usedMem = totalMem - freeMem;
                return {
                        totalMemGB: (totalMem / 1024 / 1024 / 1024).toFixed(2),
                        freeMemGB: (freeMem / 1024 / 1024 / 1024).toFixed(2),
                        usedMemGB: (usedMem / 1024 / 1024 / 1024).toFixed(2),
                        memUsagePercent: ((usedMem / totalMem) * 100).toFixed(2),
                };
                }
                
                // ── Event Loop Lag ─────────────────────────────────────────────
                function measureEventLoopLag() {
                return new Promise((resolve) => {
                        const start = Date.now();
                        setImmediate(() => {
                        resolve(Date.now() - start);
                        });
                });
                }
                
                // ── Health Report ──────────────────────────────────────────────
                async function getHealthReport() {
                const [cpuPercent, eventLoopLag] = await Promise.all([
                        getCpuUsagePercent(),
                        measureEventLoopLag(),
                ]);
                return {
                        timestamp: new Date().toISOString(),
                        status: 'ok',
                        cpu: { usagePercent: parseFloat(cpuPercent) },
                        memory: getMemoryUsage(),
                        system: getSystemMemory(),
                        eventLoop: { lagMs: eventLoopLag, healthy: eventLoopLag < 50 },
                        uptime: {
                        processSeconds: Math.floor(process.uptime()),
                        systemSeconds: Math.floor(os.uptime()),
                        },
                };
                }
                
                // ── HTTP Server ────────────────────────────────────────────────
                const server = http.createServer(async (req, res) => {
                if (req.url === '/health') {
                        const report = await getHealthReport();
                        res.writeHead(200, { 'Content-Type': 'application/json' });
                        res.end(JSON.stringify(report, null, 2));
                } else {
                        res.writeHead(404);
                        res.end('Not found');
                }
                });
                
                server.listen(3000, () => {
                console.log('Monitor running → http://localhost:3000/health');
                });
                
                // ── Console Logging ────────────────────────────────────────────
                async function logStats() {
                const [cpu, lag] = await Promise.all([
                        getCpuUsagePercent(),
                        measureEventLoopLag(),
                ]);
                const mem = getMemoryUsage();
                const sys = getSystemMemory();
                console.log('\n====== Resource Monitor ======');
                console.log(`[${new Date().toISOString()}]`);
                console.log(`CPU Usage  	: ${cpu}%`);
                console.log(`Heap Used  	: ${mem.heapUsedMB} MB / ${mem.heapTotalMB} MB`);
                console.log(`Process RSS	: ${mem.rssMB} MB`);
                console.log(`System Memory  : ${sys.usedMemGB} GB used of ${sys.totalMemGB} GB (${sys.memUsagePercent}%)`);
                console.log(`Event Loop Lag : ${lag}ms ${lag > 50 ? 'WARNING - HIGH' : 'OK'}`);
                console.log('==============================');
                }
                
                setInterval(logStats, 5000);
                logStats();

        
        

What Each Metric Means?

Here is a simple reference for interpreting your monitor output:

Metric Healthy Range Warning Sign
CPU Usage <70% Consistently above 80% scale up
Heap Used / Total <80% of heap total Growing steadily = memory leak
Process RSS Stable over time Rapidly growing = investigate
System Memory <85% used Above 90% = risk of OOM crash
Event Loop Lag <5ms Above 50ms = serious bottleneck

What You Can Add Next?

  • Alerting: Send an alert when a metric exceeds a threshold. You can integrate with Slack, email, or SMS APIs to notify your team automatically when CPU > 90% or event loop lag > 100ms.
  • Persistent Logging: Write stats to a log file using Node.js fs module, or store in a database like SQLite or MongoDB. This gives you historical data to spot trends over time.
  • Dashboard: Feed your /health endpoint into a free dashboard tool like Grafana (which can read JSON endpoints) to get beautiful real-time charts of your server stats.
  • PM2 Integration: If you’re running Node.js in production with PM2 (a popular process manager), you can expose these metrics through PM2’s built-in API for cluster-level monitoring.

Start Monitoring Your Node.js Server Today

Monitoring your backend server doesn’t have to be expensive or complex.

  • Node.js ships with everything you need to track CPU usage, monitor RAM and heap memory, and detect event loop lag, all with a few lines of clean JavaScript.
  • Whether you’re a developer running your first Node.js app or a tech lead building production infrastructure, a resource utilization monitor is one of the most practical tools you can add to your backend.

It gives you visibility, helps you catch problems before users notice, and makes debugging dramatically easier.

FAQs

  • No. All modules used in this tutorial (http, os, process) are built into Node.js. Just run node monitor.js, and you’re done.

  • Yes, the /health endpoint pattern is used by major companies and is compatible with load balancers, Kubernetes health checks, and uptime monitoring tools.
  • For large-scale production, you may eventually want to add a dedicated APM tool on top, but this monitor is a solid foundation.

  • A memory leak is when your app allocates memory but never releases it.
  • The symptom in your monitor is heapUsedMB growing slowly over hours without dropping.
  • If restarting the server “fixes” the slowness, you almost certainly have a memory leak.

Get in Touch

Got a project idea? Let's discuss it over a cup of coffee.

    Get in Touch

    Got a project idea? Let's discuss it over a cup of coffee.

      COLLABORATION

      Got a project? Let’s talk.

      We’re a team of creative tech-enthus who are always ready to help business to unlock their digital potential. Contact us for more information.