Back to News & Insights
DevOps September 21, 2026 · 6 min read

How much traffic can a $6 server handle? I measured it: 9,737 requests a second

A real app on the cheapest droplet money buys: 1,117,976 requests with zero errors, 9,737/s static and 18,557/s once nginx cached the dynamic route. The limits I hit were a config default and my own arithmetic, not the hardware.

How much traffic can a $6 server handle? I measured it: 9,737 requests a second

Every few weeks someone asks a version of the same question: is a cheap server enough for my project? The answers are always adjectives. "It depends." "Probably fine for a small site." "You'll want to scale up before launch." Nobody produces a number, so everybody over-provisions, because nobody wants to be the person whose launch fell over.

So I rented the cheapest thing DigitalOcean sells that anyone would actually run a site on — one vCPU, 1GB of memory, $6 a month, which works out at 0.893 cents an hour — put a real application on it, and pushed it until something broke. Then I measured what broke, which turned out to be the interesting part.

The short version: it served 9,737 requests a second, and the first thing to give out was not the hardware.

Most benchmarks of this shape are useless for two reasons, and both were easy to avoid.

The first is that they serve a hello-world page. I put four workloads on the box instead: a 6.7KB catalogue page served as a static file by nginx, a JSON API endpoint, a SQLite query against a 5,000-row table returning twenty rows, and a server-rendered HTML page built from that same query. Node 22 behind nginx, which is what a small production app actually looks like.

The second is that they run the load generator on a laptop, so what gets measured is the laptop's network. I rented a second, much larger machine — four vCPUs, in the same region — purely to generate load, on the principle that the thing doing the measuring should never be the thing that runs out first. At no point did the generator break a sweat.

Every number below is the mean of three fifteen-second runs, and I kept the raw output rather than a summary, for reasons that become clear later.

| workload | 100 connections | 400 connections | p50 at 100 | errors | |---|---|---|---|---| | static page (nginx) | 9,737/s ±491 | 9,773/s ±405 | 9.2 ms | 0 | | JSON endpoint (Node) | 1,291/s ±28 | 672/s ±67 | 67 ms | 0 | | SQLite query | 1,049/s ±4 | 556/s ±119 | 82 ms | 0 | | server-rendered HTML | 1,099/s ±39 | 521/s ±109 | 79 ms | 0 |

Across the whole verification run the box served 1,117,976 requests and returned zero non-2xx responses. Not a handful of errors at the top end. None.

Translating that into the units people actually plan with: the static page at 9,737 a second is 35 million requests an hour. The rendered page, the slowest thing I tested, still does 3.9 million an hour. If a visitor views eight pages in a session, the dynamic path supports something like 490,000 visitors an hour at full tilt.

I sampled the server itself once a second throughout, which is the part most benchmarks skip, and the answer is clear: it is CPU, and only CPU.

At saturation the single core was 57% in user space and 21% in the kernel, with 2% idle. Memory was never remotely a factor — the lowest free memory I recorded was 596MB of 961MB, with nginx holding 10MB and Node 83MB. The 1GB that sounds so meagre on the pricing page sat two thirds empty while the CPU was pinned.

One number deserves calling out, because shared-CPU instances have a reputation: CPU steal averaged 0.58% across the loaded period. Steal is the time your virtual core is ready to run and the hypervisor gives it to somebody else, and on a noisy host it can be brutal. I saw a single one-second spike of 23% and essentially nothing otherwise. The cheapest shared instance on the platform was not meaningfully robbed of the CPU it was sold.

A static file goes nine times faster than a rendered page. That gap is the whole story of why small servers feel slow: your machine is fine, it is just doing work per request that it could be doing once per ten seconds.

So I added a single directive in front of the rendered route — proxycache with a ten-second lifetime — and ran it again.

| rendered HTML | before | after | change | |---|---|---|---| | 100 connections | 1,099/s | 18,662/s | 17x | | 400 connections | 521/s | 18,557/s | 34x |

The cached dynamic page is now faster than the static file, which looks absurd until you notice the rendered response is 566 bytes against the catalogue page's 6.7KB. Less to push down the wire.

The honest caveat: a ten-second cache means somebody can see ten-second-old content. For a catalogue, a marketing page, a leaderboard, a blog, that is free performance. For a dashboard showing someone their own balance it is wrong, and no amount of throughput makes it right.

Want to discuss this further?

Book a free strategy call with our team to see how these insights apply to your specific business goals.

Book a consultation