The answer is not a preference, it is a break-even calculation. Here is how to run the numbers for your own workload.
The serverless versus always-on argument is usually conducted as a matter of taste. One side says you should never pay for idle machines. The other says you should never let a cloud provider bill you per request. Both positions are stated as principles, and principles are hard to check.
The question has an answer, and the answer is arithmetic. Two pricing models produce two cost curves. Those curves cross somewhere. Your job is to work out where, and which side of that crossing point your workload sits on.
This post sets out the method. It deliberately quotes no prices, because provider rates change and any number written here would be wrong before you read it. Everything is expressed in symbols. You supply the current published rates from your own provider's pricing page.
Almost every hosting option is one of two shapes underneath the marketing.
Pay per hour, always-on. You reserve a machine. It has a fixed amount of CPU and memory. You pay for every hour it exists, whether it handles a million requests or none. A virtual machine, a container running continuously, a managed instance: all the same shape. Your cost is a flat line. It does not care about traffic.
Pay per request and duration, serverless. You upload code. The provider runs it when something calls it and charges you twice: a small fee for each invocation, meaning each individual time your function is triggered, and a fee for compute duration, meaning how long the function ran multiplied by how much memory you allocated to it. When nothing calls it, you pay nothing. Your cost is a line that rises from near zero in proportion to traffic.
That is the whole structure. A flat line and a sloped line. Two lines on the same axes cross exactly once. Everything else in this post is a refinement of where.
The instinct is to say serverless is for small things and always-on is for big things. That is not quite right. The deciding factor is duty cycle: the proportion of time your machine would actually be doing work.
Consider two services that handle the same number of requests per month.
The first is an internal reporting tool, hammered for twenty minutes each morning and silent the rest of the day. On an always-on instance you buy 720 hours a month and use roughly 10 of them. Idle time is almost all of what you bought.
The second is a public API with steady traffic through the day. The instance sits at 70 percent CPU more or less permanently. Every hour you pay for is an hour of work. There is very little waste to eliminate.
Same volume, opposite conclusions. Serverless wins the first because it charges you nothing for the silence. Always-on wins the second because a reserved machine at high utilisation is one of the cheapest ways to buy compute, and serverless charges a premium for not having to think about capacity.
The general rule follows from this:
Serverless wins on spiky, unpredictable, or low duty cycle workloads. Always-on wins once utilisation is high and steady, because you are paying for a machine you are actually using.
Define your variables. Every one of these is something you can measure or look up.
| Symbol | Meaning | Where it comes from |
|---|---|---|
R | Requests per month | Your analytics or load balancer logs |
D | Average compute time per request, in seconds | Your latency metrics, server side only |
M | Memory allocated per function, in GB | Your configuration choice |
Pi | Price per invocation | Provider's current pricing page |
Pd | Price per GB-second of duration | Provider's current pricing page |
H | Hourly rate of the equivalent instance | Provider's current pricing page |
Serverless cost per month is the invocation charge plus the duration charge:
Cost_serverless = (R * Pi) + (R * D * M * Pd)Always-on cost per month is the hourly rate times the hours in a month. Call it 730 hours as a working average:
Cost_alwayson = H * 730The break-even is where those are equal. Solve for R:
R_breakeven = (H * 730) / (Pi + (D * M * Pd))That is the whole calculation. Below R_breakeven requests per month, serverless is cheaper. Above it, the reserved machine is cheaper. Plug in your provider's current rates, your own measured D, and your chosen M.
Two things fall out of the formula that are worth noticing.
D and M matter more than most people expect. They multiply together in the denominator. A function that takes 800ms at 1GB costs eight times a function that takes 100ms at 1GB, for the same number of requests. Halving your function's runtime moves the break-even point roughly twice as far out. Optimising the code changes the economics, not just the latency.
The invocation fee dominates for very short functions. If D * M * Pd is tiny compared to Pi, you are essentially paying per request regardless of what the request does. For sub-50ms functions this is often the case, and it means micro-optimising further buys you nothing.
These numbers are invented for illustration. They are not any provider's real rates. Do not use them for a decision. Look up your own.
Hypothetical rates. Say invocations cost
Pi = 0.0000002per call, duration costsPd = 0.000017per GB-second, and a small always-on instance costsH = 0.02per hour. Suppose your function takesD = 0.2seconds atM = 0.5GB.
Cost of one request under serverless:
per_request = 0.0000002 + (0.2 * 0.5 * 0.000017)
= 0.0000002 + 0.0000017
= 0.0000019Cost of the always-on box for a month:
0.02 * 730 = 14.60Break-even:
R_breakeven = 14.60 / 0.0000019 ≈ 7,700,000 requests per monthRoughly 7.7 million requests a month, or about 3 requests per second sustained. Under that, the hypothetical serverless option is cheaper. Over it, the box is.
The number itself is meaningless because the inputs are invented. The shape of the result is the point: the break-even landed at a traffic level where the instance would be genuinely busy. The crossing point tends to sit near the utilisation level at which the reserved machine stops being mostly idle.
The formula above is the honest starting point, and it is incomplete. Both models carry costs that never appear in the invocation-versus-hourly comparison.
On the serverless side:
On the always-on side:
On both sides:
Framing this as a binary choice is the actual mistake. Most systems do not have one traffic shape. They have several, and different parts want different answers.
The pattern I would default to:
This is not a compromise. It is applying each pricing model to the traffic shape it is good at. Sizing one always-on fleet to absorb both your steady load and your rare bursts means paying for burst capacity around the clock. Moving the bursts to serverless lets you size the core for the steady state, which is the thing you can predict.
One number is missing from every calculation above, and at small scale it dominates all of them.
An engineer's hour costs more than a small instance's month. If choosing the theoretically cheaper option costs a week of migration and debugging, you have spent more on the decision than it saves in a year. The saving has to clear that bar before it is a saving at all.
This cuts both ways. Serverless removes patching and capacity planning, which is real time returned. It also introduces cold start debugging, connection pooling, local development friction, and a harder time reproducing production behaviour. Always-on gives you a boring machine that behaves the way your laptop does, and asks for maintenance in return.
Count the hours. They are the largest line item on most small systems, and the one nobody puts in the spreadsheet.
At low traffic, this entire analysis is theatre. Both options cost less than lunch. The difference between them is noise against your salary bill, and the only variable that matters is how fast you can ship. Pick whichever gets the thing in front of users soonest, and spend the saved deliberation on the product.
The calculation starts to matter at scale, and it announces itself. When compute is a visible fraction of your infrastructure bill, when a spike costs real money, when someone asks why the invoice moved: that is the point to sit down with the formula, look up your provider's current rates, measure your actual D, and find out where your break-even sits.
Then check which side of it you are on, and act on the answer rather than the principle.
Run the numbers before the argument. The numbers are usually shorter.
Coming soon.