Inside the Node.js Execution Model
As you might know, Node.js is single-threaded. This means that all incoming requests hitting a Node.js application are processed by a single thread within a single background process.
When you set up a web server like Caddy or Nginx in front of Node.js, any static file rendering is completely taken care of by that web server. Those static requests never even touch your Node.js application process.
Instead, the web server only forwards the requests meant for dynamic processing down to Node.js. Because Node.js behaves like many other modern programming languages, it loads your entire application code into physical memory in bytecode form upon booting up. When a user request arrives, Node.js processes it instantly straight out of that active RAM—meaning no disk I/O operations happen until a database involvement occurs.
Now you might say: We already know these things, what is new here?
Yes, but did you focus closely on how node_modules actually works? Have you looked at how Node.js differs from PHP when we compare npm modules to native PHP extensions?
If you want to learn how PHP and its extensions work together in shared memory, https://imprakash.dev/blog/php-architecture
The Hidden Memory Cost: How Modules Work in Physical RAM
Let's move ahead. In Node.js, third-party modules belong directly to your specific application codebase, not to the global V8 engine or a centralized interpreter process.
Suppose you have a single Node.js version installed on your VPS machine, and you deploy 10 different domains—meaning 10 completely separate web applications. Now, suppose you use a common set of modules in each of those applications, like express, bcrypt, and a few others.
Because of Node's isolated architecture, that module code will be loaded 10 separate times into physical memory, bundled alongside each independent application script.
Let's look at the math to see how this impacts your VPS:
- Suppose those 10 common modules take up 100 MB of space total (\(10 \text{ modules} \times 10 \text{ MB} = 100 \text{ MB}\)).
- Because you deployed 10 domains and every single one of them imports those exact same modules, your physical memory footprint instantly balloons to 1,000 MB (\(100 \text{ MB} \times 10 \text{ instances} = 1,000 \text{ MB}\)).
This means that 900 MB of your physical RAM is being used to hold almost completely duplicated module data.
The Reality Verdict
A lot of people say Node.js is better than PHP in every single aspect, but that simply isn't true. No single language is universally better. Yes, they maximize performance in certain areas, but they still lag behind in others.
While PHP leverages centralized OS-level shared memory to read its extensions only once across hundreds of sites, Node.js isolates each app into its own independent memory heap. Node.js gives you blistering performance for a single application, but when scaling multiple sites on a single budget VPS, its architecture forces you to pay a heavy price in duplicate RAM overhead.