Every few months a platform announcement reopens the same argument, and the team ends up choosing a deployment model based on whatever shipped that week. In November 2024, AWS made SnapStart generally available for Python and .NET Lambda functions—extending the cold-start mitigation that had launched for Java back in 2022—and the message in that release was almost comedic: the feature exists precisely because the single biggest knock against serverless, the cold start, is real enough that cloud vendors keep engineering around it. That is the right lens for this whole debate. The question is never "is serverless better than containers?" It is "which failure mode can my workload tolerate, and which can it not?" As Softechinfra's CTO, I make this call across our web development projects several times a quarter, and the decision has almost nothing to do with the latest launch. This guide gives you the workload-by-workload framework we actually use—one that will still be correct long after today's announcements are forgotten.
First, Define the Terms Honestly
The words have drifted, so let me pin them down. By serverless I mean functions-as-a-service: AWS Lambda, Google Cloud Functions, Azure Functions, Cloudflare Workers. You ship code, the platform runs it on demand, scales it to zero when idle, and bills per request and per millisecond of execution. You never see a server.
By containers I mean a packaged application image you run on infrastructure you provision—whether that is a managed orchestrator like AWS ECS or Google Cloud Run, a full Kubernetes cluster, or a humble VM running Docker. You define the runtime, you decide how many instances stay warm, and you pay for capacity whether or not a request arrives.
The honest framing is that these are not two products competing for the same job. They are two points on a spectrum of how much operational responsibility you hand to a vendor. Cloud Run and Fargate sit deliberately in the middle—containers you do not have to orchestrate. The decision is rarely "all serverless" or "all containers"; mature systems run both and put each workload where it belongs.
The Five Axes That Actually Decide It
Forget the benchmarks for a moment. Five practical factors decide this for real teams, and you should score your workload on each before you look at a single pricing page.
1. Traffic shape and cost
This is where the money is won or lost. Serverless bills you for what you use, so a workload that sits idle most of the day and spikes occasionally is dramatically cheaper on functions—you pay nothing for the quiet hours. A container running 24/7 to serve that same spiky traffic is paying rent on empty rooms.
The crossover flips when traffic is steady and high. Once a function is invoked frequently enough that you would keep containers warm anyway, per-invocation billing starts to lose to per-hour capacity. There is a well-known break-even region where a constantly busy Lambda costs more than the equivalent Fargate task. The trap is assuming serverless is "cheaper" as a blanket rule—it is cheaper for the right traffic shape and more expensive for the wrong one.
2. Cold starts and latency
A cold start is the delay when the platform spins up a new execution environment for a function that has no warm instance ready. For a background job, nobody notices. For a synchronous, user-facing API where p99 latency is a product feature, a few hundred milliseconds of cold start on top of your own logic can be the difference between a snappy app and a sluggish one. This is exactly the pain SnapStart and provisioned-concurrency options exist to paper over—and the fact that you may need those mitigations is itself a signal that your latency-critical path might be happier on always-warm containers.
3. Execution duration and resource shape
Functions have hard ceilings—Lambda caps a single invocation at 15 minutes, and memory and CPU are bounded. Long-running jobs, big batch processing, WebSocket connections held open for hours, anything that wants a GPU or large memory: these belong in containers. If your workload bumps against the timeout or wants unusual hardware, the platform is telling you which side of the line you are on.
4. Vendor lock-in and portability
Containers are the most portable artifact in modern infrastructure—an image that runs on ECS today runs on Cloud Run or a Kubernetes cluster tomorrow with minimal change. Serverless functions, especially once you lean on the surrounding event sources, IAM model, and managed triggers, are far stickier. That is not automatically bad; deep integration buys you velocity. But you should choose lock-in deliberately, not back into it. We treat portability as a first-class requirement on the same checklist we use for choosing a startup tech stack.
5. Team skills and operational load
The model your team can actually operate at 2 a.m. beats the model that benchmarks better. Serverless removes a huge class of operational work—no patching, no capacity planning, no node management. Kubernetes gives you total control and demands a platform engineer's attention to match. A three-person team without a dedicated ops hire should weight this axis heavily; "we can run it" is a real constraint, not a soft one.
| Factor | Favors Serverless | Favors Containers |
|---|---|---|
| Traffic shape | Spiky, idle-heavy, event-driven | Steady, high, predictable |
| Latency tolerance | Async/background OK | Tight, user-facing p99 |
| Run duration | Short bursts under the timeout | Long-running or unusual hardware |
| Portability need | Vendor integration is welcome | Multi-cloud or exit optionality |
| Team capacity | No dedicated ops | Platform engineering on staff |
A Decision Matrix by Workload Type
Stop deciding at the application level. Decide at the workload level. Here is how the common pieces of a typical product map out.
- Public REST/GraphQL API with steady traffic — containers (Cloud Run or ECS Fargate). Predictable latency, no cold-start tax on every request.
- Webhooks and third-party event handlers — serverless. Bursty, idle most of the time, perfect fit for scale-to-zero.
- Scheduled/cron jobs and report generation — serverless, unless the job runs longer than the function timeout.
- Image/video/file processing pipelines — serverless for short transforms; containers for heavy or long encodes.
- Real-time, WebSocket, or stateful connections — containers. Functions are the wrong shape for held-open connections.
- Internal admin tools and low-traffic dashboards — serverless. Why pay to keep a box warm for a handful of daily users?
- ML inference — depends on model size and latency budget; large or GPU-bound models lean container.
On ChipmakerHub, the B2B semiconductor platform we built, this split is exactly how the system is laid out. The main browsing and search surface runs on always-warm containers because users expect instant page loads and the traffic is steady through the working day; the supplier-data ingestion, enrichment, and notification jobs run as serverless functions because they fire irregularly and would waste money sitting idle on reserved capacity. Neither model "won." Each workload went where its traffic shape and latency profile pointed. This is the same hybrid instinct we describe in our guide to when to split a monolith into microservices—architecture should follow the workload, not a slide deck.
The Hidden Costs Nobody Quotes You
Both models carry costs that never appear on the pricing calculator. Naming them up front prevents the nastiest surprises.
Serverless surprises. Egress and downstream charges—your function is cheap, but the database connections, API calls, and data transfer it triggers may not be. Connection pooling against a relational database is genuinely awkward from functions, since each concurrent invocation can open its own connection and exhaust the database; you often need a proxy in front. Local development and debugging are harder than running a container on your laptop. And observability across hundreds of short-lived invocations demands tooling you would not need for a long-lived process.
Container surprises. The cluster you have to run. Kubernetes in particular is a product your team now operates—upgrades, node pools, networking, security patches. Right-sizing is a perpetual chore: over-provision and you burn money on idle capacity, under-provision and you fall over under load. Managed options like Cloud Run and Fargate buy most of this pain away, which is exactly why they are the sensible default for teams that want containers without a platform team.
How to Actually Make the Call
When a team brings me this decision, we run the same short process every time. It takes an afternoon, not a sprint.
- List your workloads, not your app. Break the system into discrete workloads—each API surface, each job, each pipeline—and treat them independently.
- Score each on the five axes. Traffic shape, latency tolerance, run duration, portability need, team capacity. A 30-minute exercise per workload.
- Estimate cost at realistic load, not peak. Model expected traffic and idle time, then compare per-invocation against keep-warm capacity for that specific shape.
- Default to managed. Prefer Cloud Run, Fargate, or functions over self-managed clusters unless a hard requirement forces orchestration.
- Keep an exit lane. Whichever side you choose, keep business logic decoupled from platform glue so a future migration is a config change, not a rewrite.
- Revisit at scale milestones. The right answer at 1,000 requests a day differs from the right answer at 10 million. Re-score when traffic crosses an order of magnitude.
This is the same discipline that runs through our backend choices in picking a backend language: the decision is contextual, the framework is durable, and "it depends" is a feature of good engineering, not a dodge.
The Durable Takeaway
Platform launches will keep arriving—new cold-start tricks, new pricing tiers, new managed runtimes—and each one will tempt a team to relitigate the whole decision. Resist it. The framework does not move: score the workload, not the platform; default to managed; choose lock-in deliberately; keep an exit lane; revisit at scale milestones. A team that runs that process will route each workload to the right home in any pricing era, and will spend its energy on the product instead of the deployment debate. The announcement that triggered this post will be a footnote in a year. The decision matrix will still be true.
Need Help Choosing a Deployment Architecture?
We design and run hybrid serverless-and-container infrastructure for startups and product teams—right-sized for your traffic, your budget, and the team you actually have.
Talk to Our Engineering Team →
