How Far a €5.50 VPS Actually Scales: What the Numbers Say
Cloud

How Far a €5.50 VPS Actually Scales: What the Numbers Say

Nobody's benchmark is your app's benchmark. Instead of quoting a number nobody ran, here's the actual mechanism that caps a 4 vCPU / 8 GB box, how caching changes the arithmetic by orders of magnitude, and exactly how to load-test your own workload.

Published September 7, 202611 min readUpdated Sep 6, 2026

Written by · Full-Stack Agentic AI Software Engineer — AI Agents, Automation & Revenue Systems for GTM/RevOps teams

In brief

How much traffic can a 4 vCPU / 8 GB VPS actually handle, and how do you find that number for your own app instead of trusting someone else's benchmark?

There's no single answer, because the ceiling is set by mechanism, not marketing — worker count and concurrency, memory per request, and how much of the response path ever reaches the origin at all. Caching doesn't make the box faster, it makes most requests never arrive, which is why hit ratio changes the arithmetic by orders of magnitude rather than by a percentage. The only honest number is the one you get from load-testing your own workload, and this post shows exactly how.

  • Throughput on a fixed box is capped by whichever resource runs out first — CPU, memory, or open connections — and which one that is depends entirely on the workload
  • A Node.js process is single-threaded per instance; concurrency comes from the event loop plus how many worker processes or containers you actually run
  • Memory sets a hard ceiling long before CPU does on most small apps — the OOM killer doesn't negotiate
  • Caching doesn't speed up a request, it prevents most requests from reaching the origin at all, which is why hit ratio matters more than raw server horsepower
  • The right way to find your own number is a load test against a staging copy, reading p95/p99 latency and error rate, not extrapolating from someone else's published figure

Evidence notes

Contabo Cloud VPS 4 specifications

4 vCPU, 8 GB RAM, 100 GB SSD, €5.50/month for the first 24 months — verified on Contabo's own pricing page, September 2026

Next.js self-hosting cache architecture

ISR and the Next.js server cache share one cache on local disk by default; multiple instances behind a load balancer need a custom cacheHandler with cacheMaxMemorySize set to 0, or each instance serves stale data independently

Cloudflare free-tier cache behavior

Free zones cannot set an edge cache TTL below 2 hours via cache rules, and the maximum cacheable object size is 512 MB — checked September 2026

Continue with purpose

Every "how far does a cheap VPS scale" post eventually cites a requests-per-second number, and the number is almost always someone else's, run against an app that isn't yours, on a day nobody documented the conditions for. That number is worthless to you. What's actually useful is understanding the mechanism that sets the ceiling — because once you know what's actually running out, you can reason about your own app instead of importing a stranger's benchmark and hoping it transfers.

So this isn't a load-test writeup. Nobody ran a benchmark for this post, and it says so plainly rather than dressing up a made-up figure as data. It's a mechanism post: what actually caps throughput on a 4 vCPU / 8 GB box, why caching changes the math by orders of magnitude rather than a percentage point, and — because that's the part every benchmark-free post owes the reader — exactly how to go measure your own number. the team at XenGrowth approaches load testing from the operator's side, which complements the engineering view here.

What actually runs out first on a fixed-size box?

Not CPU, usually. That's the counterintuitive part. A Contabo Cloud VPS 4 — 4 vCPU, 8 GB RAM, 100 GB SSD, €5.50/month for the first 24 months, per Contabo's own current pricing — sounds like it should be CPU-bound, and for compute-heavy work it is. But most SaaS request handling isn't compute-heavy. It's I/O-bound: waiting on a database query, waiting on an external API, waiting on disk. During that wait, CPU sits idle while memory stays fully committed to holding the request's state open. On a small box, memory runs out long before CPU does.

Resource

What actually consumes it

What happens when it runs out

CPU (4 vCPU)

Request handling, rendering, compression, encryption

Requests queue and latency climbs; the process itself usually survives

Memory (8 GB)

Every open connection, every in-flight request's buffers, the runtime itself, and any in-process cache

The kernel's OOM killer terminates a process — often not the one that caused the spike

Open file descriptors / connections

Concurrent database connections, open sockets, worker pool size

New connections get refused or time out, existing ones keep working

Disk I/O

Database writes, log writes, local ISR cache reads/writes

Latency spikes across everything sharing that disk, not just the slow query

That memory-first failure mode is worth taking seriously rather than treating as an edge case, because it doesn't announce itself. There's no warning banner before the kernel kills something — the box looks fine, then a process is dead mid-request, and it's often not the process that actually caused the spike. The full mechanism behind that, and how to read the aftermath, is in what happens when your VPS runs out of RAM.

What determines how many requests you can actually handle at once?

Concurrency, not raw CPU count, is the number that actually matters, and concurrency is set by how many workers you run and how each one handles simultaneous requests. Node.js is single-threaded per process — one process doesn't parallelize CPU-bound work across multiple cores on its own. What it does well is I/O concurrency: while one request waits on a database call, the event loop is free to start handling the next one, which is exactly the shape of most web request handling. The XenGrowth resource library approaches this from the the operations side of this side.

  • One Node.js process uses the event loop to interleave I/O-bound requests efficiently, but a single process still only uses one CPU core for actual computation

  • Running multiple worker processes — one per vCPU, roughly — is how you actually use all 4 vCPU on the box, whether through a process manager or multiple containers behind the reverse proxy

  • Each additional worker also multiplies your memory footprint, because each is a separate runtime with its own heap — this is the direct tradeoff against the memory ceiling above

  • A database connection pool sized too small serializes requests behind it regardless of how many app workers are running; sized too large, it can itself exhaust the database server's own connection limit

This is why "how many requests per second can a 4 vCPU box handle" doesn't have a universal answer. An app that's mostly waiting on a fast, well-indexed database query can run many concurrent requests per worker, because most of that time isn't consuming CPU. An app doing real computation per request — image processing, complex serialization, cryptographic work — hits the CPU ceiling with far fewer concurrent requests, because there's no idle waiting to interleave around.

Why does caching change the arithmetic instead of just the speed?

Caching doesn't make a request faster in the way people usually picture it — it prevents most requests from reaching the origin server at all. That's a structural difference, not a tuning improvement, and it's why hit ratio moves the ceiling by orders of magnitude instead of by a percentage. A box that can comfortably serve 50 uncached requests per second can serve many times that in effective traffic if 95% of requests are answered at the edge and never touch the VPS.

Next.js's own architecture makes this concrete: ISR and the server-side render cache share one cache, on local disk by default, per the framework's own self-hosting documentation. That's fine for a single instance with persistent disk — cache hits skip rendering entirely and just serve the stored HTML. It stops being fine the moment you run multiple instances of the app behind a load balancer without a shared cache handler, because each instance then holds its own independent, partially-stale copy, and a request that would have been a hit on one instance is a cold miss on another.

Cache layer

What it prevents from happening

What breaks it

Cloudflare edge cache (free tier)

Request from reaching your origin server at all

TTL below the free tier's 2-hour floor, or a response marked uncacheable

Next.js ISR / server cache

Server-side render work for a request that already has a stored result

Multiple app instances without a shared cache handler, each holding a divergent local copy

Browser cache

Request from leaving the user's machine in the first place

Missing or overly short cache-control headers on static assets

One free-tier detail is easy to trip on: Cloudflare's free zones can't set an edge cache TTL below two hours through cache rules, per Cloudflare's own documentation. For content that changes often, that floor is either a feature — it forces you to think about what's actually safe to cache for two hours — or a real constraint that pushes you toward a paid plan's finer TTL control. Either way, it's worth knowing before you design a caching strategy around an assumption the free tier doesn't support. XenGrowth on governed AI marketing workflows approaches this from the AI agents and marketing automation side.

So how do you actually find your own ceiling?

By load-testing your own app against a staging copy of your own infrastructure — not by reading someone else's number and assuming it transfers. This is the part most "scaling" posts skip, because a real number requires running a real test, and a fabricated one is faster to write. Here's what that test actually needs.

  1. Run the test against a staging environment sized identically to production, not against production itself and not against a laptop

  2. Pick a load-testing tool that can ramp concurrency gradually rather than slamming full load instantly — k6, Apache Bench, wrk, or Locust all work; k6 and Locust let you script realistic multi-step user flows instead of hammering one endpoint

  3. Test your actual critical paths, not just the homepage — the endpoint that hits the database and the one that's served entirely from cache will report wildly different ceilings

  4. Watch p95 and p99 latency, not the average — the average hides exactly the tail-latency degradation that means real users are having a bad time while your dashboard still looks fine

  5. Watch error rate and memory usage on the box in parallel with the load test, not just requests-per-second, because the failure mode is often memory exhaustion or connection-pool saturation, not a clean rate limit

  6. Increase load in steps and note where p95 starts climbing sharply rather than gradually — that inflection point, not the eventual failure point, is your real usable ceiling

A benchmark run against someone else's app tells you what their app can do. p95 latency and error rate from your own load test, against your own staging box, are the only numbers that describe your ceiling.

p95 specifically deserves a plain definition, because it gets thrown around without one constantly: it's the latency figure below which 95% of requests complete — meaning 1 in 20 requests is slower than that number, sometimes much slower. That's the number that matters for user experience, because averages get dragged down by the fast, cached majority while the slow tail is exactly what a frustrated user actually experiences.

Does vertical scaling ever actually solve this, or just delay it?

Moving up Contabo's own lineup — from the Cloud VPS 4 at 4 vCPU / 8 GB to the Cloud VPS 6 at 6 vCPU / 12 GB, or further — buys more headroom against every resource in the table above, but it doesn't change which one runs out first. A memory-bound app that's hitting the OOM killer on 8 GB will generally stop hitting it on 12 GB, at least for a while, but if the underlying cause is a memory leak or an unbounded cache rather than genuinely higher legitimate load, a bigger box just delays the same failure to a later, possibly less convenient moment.

That distinction matters because it changes what you should actually do in response to hitting a ceiling. If a load test shows memory climbing linearly with legitimate concurrent users, more RAM is a reasonable, honest fix. If memory climbs over time regardless of load and only a restart brings it back down, that's a leak, and a bigger box just buys a longer runway before the same crash — worth knowing before spending money on vertical scaling that a code fix would have solved for free. There is a longer treatment of AI search, GEO and discovery in XenGrowth on building one SEO and GEO content system.

What does this mean for sizing the box in the first place?

It means the sizing decision isn't "how many vCPU do I need" in isolation — it's a joint decision across vCPU, RAM, worker count, and expected cache hit ratio, and getting one of those wrong doesn't get rescued by the others being generous. A 4 vCPU / 8 GB box running a well-cached, I/O-bound app can comfortably outperform a larger box running an uncached, memory-hungry one. The full framework for making that call — which of vCPU or RAM to prioritize for a given workload shape — is in vCPU or RAM: how to size a VPS, and it's worth reading before you run the load test above, so you know what you're actually looking for in the results.

It's also worth remembering what's actually consuming resources underneath the app itself — the deploy layer, the reverse proxy, and the runtime it's all wrapped in aren't free, and sizing a box for the app alone while forgetting what runs alongside it is a common way to end up short. Docker concepts you need before Coolify covers exactly what's eating memory and CPU besides your application code.

There's no single answer to "how far does this scale," and anyone who gives you one without having run it against your workload is guessing with confidence. Run the test, read p95 instead of the average, and watch memory alongside requests-per-second — that's the whole method, and it works regardless of which box you're on. If you want a second set of eyes on your own load-testing setup or capacity plan, that's the kind of work I take on through services.

Further reading from XenGrowth

Where this work meets go-to-market

the XenGrowth practice writes for the teams who have to run load testing day to day.

What runs out first?

Four questions on where the ceiling actually is on a small box. It's rarely the thing people upgrade for.

1 / 4
On a small VPS running a Next.js app and a database, what's usually the first hard limit?

Apply this article

How to turn insights into execution

A practical sequence for teams turning concepts into production outcomes.

VPSPerformanceLoad TestingContaboCachingCapacity Planningcloud

Audit your current state

Map the bottlenecks and constraints connected to the article’s core problem.

Choose one bounded change

Test the most useful recommendation on one workflow before widening the scope.

Measure what changed

Keep the parts that improve the work, document what failed, and make the next decision from evidence.

Next step

Need help applying this in your stack?

I can translate these patterns into a concrete implementation plan for your team.

Discuss implementationBack to blog

Replies usually within 24 hours.

Next Steps

Continue reading

Why Next.js Feels Slow on a VPS — and the Six Things That Fix It

The app that felt instant on Vercel goes home to a cheap VPS and forgets how to load fast. The framework didn't change. Six specific things underneath it did, and none of them are next/image.

Navigate

vCPU or RAM? How to Size a VPS for Static Sites, Databases and Traffic Spikes

Four readers asked four versions of the same question, and each one has a different honest answer. Static files want RAM for page cache, Postgres wants RAM for shared_buffers, and "slow under traffic" is usually neither CPU nor RAM.

Navigate

Moving a SaaS Off Vercel to a €5.50 VPS: What the Numbers Actually Look Like

Nobody publishes their real Vercel invoice, so most migration posts trade in vibes instead of arithmetic. This one builds the comparison from Vercel's and Contabo's own published rates, states every assumption out loud, and shows exactly where the two lines cross.

Navigate

Should Your Database Live on the Same VPS as Your App?

The pitch for co-location is real: no network hop, no egress bill, one box to back up. So is the failure mode — one OOM event takes the app and the database down together, because they were never separate to begin with.

Navigate

One Big VPS or Several Small Ones? How Many Apps Fit on One Box

The question isn't how many containers fit in the RAM. It's what happens the day one of them takes the box down with it, and whether you'd rather patch one server or four.

Navigate

Best VPS Providers for Coolify: Contabo, Hetzner and the 2026 Price Shift

Every 'best VPS for Coolify' post still says Hetzner is the cheap option. That stopped being reliably true in June 2026 for two of its most popular tiers, and nobody's updated the recommendation yet.

Navigate

What Cloudflare's Cache Actually Does for Speed and Bandwidth

Cache HIT and MISS aren't a minor speed difference — they're two different request paths entirely, one of which never reaches your server. Here's the mechanism, the free tier's real limits, and exactly how to check your own hit ratio instead of trusting a number nobody measured.

Navigate

The Contabo + Coolify + Cloudflare Stack That Replaced My Vercel Bill

Three pieces, not thirty. Here's exactly what each one does, why that specific combination and not one of the dozen others I considered, and which parts of Vercel each one is actually standing in for.

Navigate

10 Mistakes That Break a Self-Hosted SaaS

None of these ten show up as a single dramatic outage. They show up as a disk that quietly fills, a rollback that turns out to be impossible, a backup nobody ever restored. Here's the mechanism behind each one, and the fix.

Navigate

The Backup Strategy Every Self-Hosted SaaS Needs (3-2-1, Applied)

3-2-1 is easy to nod along to and easy to get wrong in the specific way that only shows up on the day you need it. Here's what it actually means for one VPS running Postgres and Docker volumes, not the generic version you've already skimmed past twice.

Navigate