b8c.io All articles
Engineering Strategy

Serverless Looked Cheap Until the Bill Arrived

b8c.io
Serverless Looked Cheap Until the Bill Arrived

Photo: Hiren Tat, CC BY-SA 4.0, via Wikimedia Commons

There's a version of the serverless pitch that sounds almost too good. No servers to manage. Pay only for what you use. Scale to zero when nobody's home. For a lot of teams, especially smaller ones watching every dollar, that pitch lands hard.

Then three months in, someone opens the AWS or GCP console, stares at the billing dashboard, and does the math. The numbers don't add up the way the whitepaper said they would.

This isn't a hit piece on serverless. It's a genuinely useful architecture pattern for the right workloads. But "the right workloads" is doing a lot of heavy lifting in that sentence, and too many teams skip the part where they figure out whether their workload actually qualifies.

Let's walk through where the money actually goes.

The Cold Start Tax Nobody Mentions in the Sales Deck

Cold starts are the original serverless gotcha. When a function hasn't been invoked recently, the cloud provider has to spin up a new execution environment — pulling your container image, initializing your runtime, loading your dependencies. Depending on your language and framework, that can add anywhere from 200 milliseconds to several seconds of latency on top of your actual function execution.

For a background data processing job, that's annoying but survivable. For a user-facing API endpoint, it's a conversion killer.

The workaround most teams reach for is provisioned concurrency — essentially paying to keep a warm pool of function instances ready to go. On AWS Lambda, provisioned concurrency runs you around $0.015 per GB-hour for the reserved capacity, plus the standard invocation costs on top. Once you're running provisioned concurrency at meaningful scale, you're no longer paying for compute you use. You're paying for compute you might use. That's just a server with extra steps.

Here's a back-of-the-napkin scenario: a mid-traffic API with 10 provisioned concurrency units, each at 512MB, running 24/7 for a month. That's roughly $54/month in reserved capacity before a single request hits. Add invocation costs, and you're often looking at a price point that a modest EC2 instance or a couple of containers on ECS Fargate would beat without breaking a sweat.

Egress: The Line Item That Eats Teams Alive

Data transfer costs are the slow bleed of cloud infrastructure, and serverless architectures are particularly vulnerable to them. Every time your function pulls data from S3, talks to a managed database, calls an external API, or returns a response payload, bytes are moving — and the cloud provider is watching.

AWS charges $0.09 per GB for data transferred out to the internet from most regions. That sounds trivial until you're processing image uploads, streaming responses to mobile clients, or running a data pipeline that shuffles gigabytes between services.

The insidious part is that serverless makes it easy to build architectures where data crosses billing boundaries constantly. A Lambda function reads from RDS, writes to S3, triggers another Lambda via SNS, which writes to DynamoDB. Each hop is an opportunity for egress charges to accumulate. In a traditional VM or container setup, you'd probably colocate more of that logic, partly out of habit, partly because the architecture naturally discourages chatty cross-service communication.

Serverless inverts that instinct. The function-per-concern model encourages decomposition, which is architecturally elegant but financially punishing at scale.

Running an Actual Cost Audit

If you're already on serverless and wondering where your budget went, here's a practical audit framework:

Step 1: Map your invocation patterns. Pull 30 days of CloudWatch or equivalent metrics. Look at invocation count, duration, and error rate per function. Identify your top 10 functions by cost — they're almost always responsible for 80%+ of your bill.

Step 2: Separate compute cost from infrastructure cost. Compute is what you're paying for actual execution time. Infrastructure cost is everything else: provisioned concurrency, API Gateway, event source integrations, VPC NAT gateway fees if your functions live in a VPC. Teams routinely undercount the infrastructure layer because it doesn't show up under the Lambda line item.

Step 3: Quantify your egress. Use your cloud provider's cost explorer to isolate data transfer costs by service. If you're running a data-heavy workload and egress is more than 15% of your total serverless bill, that's a signal worth investigating.

Step 4: Model the alternative. Take your top three functions by cost and price out what equivalent throughput would cost on a container-based setup. Use something like ECS Fargate or a small Kubernetes cluster if you're already comfortable with that tooling. Be honest about operational overhead — containers aren't free to run either. But the comparison is often more favorable to containers than teams expect.

Vendor Lock-In Is a Cost Too

This one's harder to quantify but worth naming. Serverless functions are deeply coupled to their host cloud. Lambda's event source integrations, IAM permission model, and cold start behavior are AWS-specific. Moving to GCP Cloud Functions or Azure Functions isn't a lift-and-shift — it's a rewrite.

That coupling has a real cost when AWS raises prices (they do), when a competitor offers meaningfully better pricing for your workload (it happens), or when your team decides to go multi-cloud for resilience reasons. The exit cost from serverless is higher than most teams account for when they're committing to the architecture.

So When Does Serverless Actually Win?

To be fair: there are legitimate serverless use cases where the economics hold up.

Event-driven workloads with spiky, unpredictable traffic are a genuine sweet spot. If you're processing webhook payloads from a third-party service and volume is wildly variable, paying for idle capacity on a container cluster is genuinely wasteful. Serverless scales to zero in a way containers don't.

Low-frequency scheduled jobs are another good fit. A nightly data export that runs for 30 seconds doesn't need a dedicated server. Running it as a Lambda function is cheap and sensible.

Prototyping and internal tooling also make sense. When you're moving fast and operational simplicity matters more than cost optimization, serverless reduces the cognitive overhead of getting something deployed.

The mistake is assuming those advantages generalize. They don't. Serverless is a sharp tool for specific jobs, not a default architecture for everything you ship.

The Framework Before You Commit

Before your team goes all-in on serverless for a new service, run through these questions:

Serverless isn't a scam. But it's been oversold as a universal cost-reduction play, and a lot of teams are paying for that misconception one billing cycle at a time. Run the numbers before you commit — not after the invoice lands.

All Articles

Related Articles

You Probably Don't Need Kubernetes. You Just Think You Do.

You Probably Don't Need Kubernetes. You Just Think You Do.

All Signal, No Noise: Why Your Observability Stack Is Fooling You Into False Confidence

Flags That Never Die: The Hidden DevOps Tax You're Paying Every Single Sprint