The number is not a property of your deployment tool. It is your server's available memory divided by what one build takes at its peak, and every answer that skips that division is guessing on your behalf.
So do the division. An install-and-build of a Next.js, Nuxt, Remix or React Router project wants roughly 2 GB of RAM while it runs.
That number is worth being honest about: it comes from running these deploys rather than from a benchmark, and the observation behind it is sharper than the average. On servers with 2 GB of RAM or less, the build fails — not sometimes, and not only under load. Above that the figure is a starting point you should replace with your own, measured the way the next section describes. But the floor is not a rule of thumb.
Working it through with 2 GB per build and a machine that is already running the apps it is deploying:
| Server | Also running | Builds that fit | |---|---|---| | 2 GB | one small Node app | 0 — one build alone is already over the line | | 4 GB | one or two Node apps | 1 | | 8 GB | several apps, no database | 2–3 | | 8 GB | several apps + Postgres or MySQL | 2 | | 16 GB | several apps + a database | 3–4 |
These are the comfortable numbers, not the maximum. You can run five builds at once on the 8 GB row. Nothing refuses, and they will probably finish.
What changes first is not the build. Memory pressure rises across the whole machine, the applications already serving traffic get squeezed, and the visible symptom is 504s on sites nobody was deploying — while the deploys themselves get slower, because they are now competing for the same memory. The build failures come later, if at all. So the number you can reach and the number you want are different numbers, and only one of them is invisible to your visitors.
Framework matters too, and it moves the rows: a Next.js build is heavier than most, while an Astro build is light enough to change the 8 GB row to four. Two Node projects are not necessarily the same size, which is the other reason to measure rather than to trust a table.
What follows is how to replace these rows with your own numbers, and what actually happens on the machine when the arithmetic is wrong.
What you actually have free. The free column is not the answer; available is, because it counts the page cache the kernel will hand back under pressure.
What one build peaks at. Run a real build under GNU time — the full path matters, since the shell builtin does not have -v:
The figure is in kilobytes, and it is the largest single process, not the sum of everything running at that moment. A bundler that forks parallel workers uses more than this number reports, so treat it as a floor.
Whether the build survives a ceiling. This is the honest test, because it fails the same way the server will:
If the build completes inside 2 GB, two of them fit in a machine with 4 GB free. If it gets killed, you have your answer without taking a production site down to find it.
A four-core box does not run four builds. Bundling and type-checking are memory-hungry in a way that saturates RAM long before CPU, and the failure modes are asymmetric: a build starved of CPU finishes late, a build starved of memory dies — or worse, something else on the machine does.
The same logic explains why not every deploy costs the same. A Laravel deploy that runs composer install and php artisan migrate is cheap in memory terms. The same Laravel project with a Vite front end runs npm ci && npm run build too, and pays the Node price like everything else. Sizing by "number of projects" gets this wrong in both directions; sizing by peak RSS does not.
The mistake that turns a correct-looking calculation into a dead server is measuring against an idle machine. During a deploy, at minimum: The app being deployed is still running. It has to be — that is what zero-downtime means. On a Node project, briefly two copies are running. The new process comes up on a standby port and is soaked for 60 seconds before nginx is switched and the old worker is drained. For that window the server holds both. The zero-downtime sequence walks through why the soak is there. Every other app on the box is serving traffic, including the PHP-FPM pool, which sizes itself to concurrency rather than to your deploys. The database is holding its buffers, and it will not give them back because your build asked.
Headroom is what is left after all of that. Build concurrency is spent out of the remainder.
