The right server for an app depends on the app’s shape, not on brand loyalty. A static frontend needs no running process at all, just files served fast. An API needs a process that stays up and answers requests, sized by concurrent connections rather than raw traffic. A background worker needs CPU in bursts and often runs on less memory than people assume. A database needs memory more than anything else, because most of its job is keeping data in RAM instead of reading it off disk. Size each component by what it is actually doing, not by one number applied to the whole project.
“What server do I need for my app” usually gets answered with a generic instance recommendation that ignores what the app is actually doing under the hood. A static frontend, an API and a database are not the same workload, and sizing them the same way wastes money on one and starves another.
Table of contents
- Static frontend: no server process at all
- API or backend service: sized by concurrency, not traffic
- Background worker: CPU in bursts
- Database: RAM before CPU
- The honest vCPU and RAM sizing rule
- Multiple app shapes, one project
- Signs you sized it wrong
- How this fits the rest of the stack
- FAQ
Static frontend: no server process at all
A built frontend, a React, Vue or Astro export, is just files once the build finishes. There is no vCPU or RAM sizing question in the way there is for a running process, because nothing is running continuously. The real cost variable is bandwidth, not compute, and the sizing question that actually matters is build time and asset size, not server capacity.
Where people still get this wrong is server-side rendering: a Next.js app configured to render on every request is not a static frontend anymore; it is a Node process, and needs vCPU and RAM sizing like any other API. Confirm which mode your framework is actually running in before assuming the static-hosting sizing rules apply. A build that outputs pre-rendered HTML files needs none of this; a build that spins up a server to render per request needs all of it.
API or backend service: sized by concurrency, not traffic
A Node, Python or Go API holds connections open and does work per request, so it is sized by how many requests it handles at once and how long each one takes, not by total daily traffic. A low-traffic API is often fine on a fraction of a vCPU; the real bottleneck is frequently memory from loaded dependencies, not raw compute.
A worked example: an API handling ten requests per second, each taking 150 milliseconds, has roughly one and a half requests in flight at any moment on average, which a small instance handles easily. The same ten requests per second at two seconds each, waiting on a slow downstream call, means twenty requests in flight simultaneously, which is a concurrency problem no amount of raw CPU fixes on its own. Know which shape your API actually has before sizing it.
Background worker: CPU in bursts
A queue consumer, cron job or scheduled task is often idle and spikes when it actually runs. Sizing for the idle baseline wastes money; sizing for the burst is what matters, and that burst is usually about memory if the job loads a large dataset or file into memory at once, more than it is about sustained CPU.
The failure case to watch for is a worker sized for its typical job hitting an unusually large one, a bigger-than-normal CSV import, a video file instead of an image, and getting killed for exceeding memory rather than failing gracefully. Processing in smaller chunks avoids the problem entirely and is usually cheaper than renting enough memory to hold the worst case a job might ever see.
A queue also decouples the worker’s sizing from the API’s. A spike in incoming jobs queues up rather than overwhelming a process in real time, which means a worker can be sized for typical throughput rather than for the rare moment everyone submits a job at once, as long as the queue itself has somewhere to hold the backlog.
Database: RAM before CPU
A database’s speed comes largely from how much of its working set fits in memory rather than getting read off disk on every query. A small dataset with modest concurrency is comfortable with a moderate amount of RAM; a few million rows with real joins wants more memory before it wants more compute. Connection limits matter as much as either, since a database can run out of available connections long before it runs out of CPU.
A practical check: if query latency climbs steadily as the dataset grows even though the queries themselves have not changed, that is usually the working set outgrowing available memory, not a CPU shortage. Adding compute to a memory-bound database changes very little; adding memory, or adding the right index so less of the table needs to be scanned per query, is what actually helps.
A second practical check catches the opposite mistake: a database that is fast on a small local copy but slow in production usually has a different data volume, not a different server. Testing against a dataset close to production size, even a sampled subset, tells you far more about future sizing than testing against a mostly-empty development database ever will.
The honest vCPU and RAM sizing rule
Start smaller than instinct says. Watch actual usage after launch, not a worst-case guess made before a single real request arrives. Move up when a real metric says so, sustained CPU above roughly 70 to 80 percent, or memory pressure hitting swap, rather than preemptively renting headroom nobody uses.
This runs against the instinct to size for Black Friday on day one. Most projects never see that spike, and the ones that do are usually better served by scaling up in response to a real metric than by permanently paying for capacity that sits idle 360 days a year. Keep an eye on the metric that actually predicts trouble for your workload, CPU for an API, memory for a database or worker, rather than a generic dashboard number that does not map to how the app fails.
The one exception worth planning for in advance, rather than reacting to, is a known launch date with an expected traffic bump, a product announcement or a marketing push. That is a case for temporarily sizing up ahead of the event based on an actual estimate, then sizing back down once the real traffic pattern is known, not a case for permanently overprovisioning from day one.
Multiple app shapes, one project
A real project is usually a static frontend, an API, and a database at minimum, each with a different shape and a different sizing decision. Treating the whole project as one server to size is where most overspending happens: the frontend needs none of the resources the database does, and lumping them together sizes for the wrong workload.
Sizing each piece independently also isolates failures. A traffic spike hitting the frontend does not starve the database of memory if they are on separate plans; a runaway background job does not take the API down with it. That isolation is worth more than the minor convenience of managing one combined size, especially once a project has more than a single moving part.
It also makes the monthly bill legible. A combined server’s bill answers “what did the whole project cost” but not “which piece is actually expensive,” which matters the moment a project needs to cut costs or justify an upgrade. Separate plans turn that into a line-item question instead of a guess.
Signs you sized it wrong
- Restart loops or out-of-memory kills mean the process is undersized on RAM
- Sustained high CPU with slow responses under normal load means an undersized API or worker
- A static frontend that “feels slow” is almost always a build or asset problem, not a server sizing problem
- A database that gets slower as the dataset grows is a memory question before it is a CPU question
- A worker that times out on some jobs but not others usually means the largest jobs exceed available memory
Check these against actual metrics rather than a hunch. A dashboard showing memory climbing steadily toward a limit right before every restart is a far more reliable signal than “the app feels slow today,” and it is the difference between fixing the real problem and guessing at a bigger plan that may not address it. A bigger plan bought without reading the actual metric first sometimes fixes nothing, because the real bottleneck was never the resource that got increased.
How this fits the rest of the stack
Once you know the app’s shape, sizing is a matter of picking from a short ladder rather than guessing at a server size. A Python or Node API service starts on the $4 Dev plan at 0.3 vCPU and 624MB, moves to Basic or BasicMini as concurrency grows, and a managed database sits beside it on its own plan with its own memory. Autoscaling moves a service between a floor and ceiling plan automatically once you have a real CPU number to react to, rather than a guess made before launch. Add up a specific combination on the RunxBuild hosting calculator.
Useful related references:
- Application Server vs Web Server: The Line People Keep Blurring
- Application Hosting vs Web Hosting: The Decision Tree, The Cost Difference, and The Traps
- 8GB or 16GB RAM: How Much Does Your Server Actually Need?
- Services on RunxBuild
FAQ
What server do I need for my app?
It depends on what the app does, not just how much traffic it gets. A static frontend needs no running process. An API needs a process sized by concurrent connections. A worker needs CPU in bursts. A database needs memory before anything else. Size each component separately.
What is the difference between an app server and a web server?
A web server typically serves files and proxies requests, often sitting in front of an application. An app server runs your application code, handling logic, database queries and business rules. Modern platforms blur the line by running your process directly and handling the proxy layer for you.
How much RAM does a small API need?
Often less than people assume. A low-traffic API in Node or Python frequently runs comfortably in 512MB to 1GB, with memory dominated by loaded dependencies rather than request volume. Watch actual memory usage after launch rather than sizing for a worst case that may never happen.
Where should I host an app’s backend?
Anywhere that can run your language’s process from a repository, expose environment variables, and sit a database nearby on a private network. Whether that is a VPS you manage or a platform that builds and deploys for you depends on how much of the operational work you want to own.
How do I know if I sized my server wrong?
Restart loops or out-of-memory kills mean too little RAM. Sustained high CPU with slow responses under normal load means too little compute for an API or worker. A static frontend that feels slow is almost always a build or asset problem, not a server sizing problem.