Serverless vs Containers: Which Architecture Should You Pick?

Two Ways to Run Code in Production — Both Valid, Neither Universal

When you are deciding how to deploy a new service, you will eventually hit this choice: serverless functions or containers. Both run your application code in production. Both scale. Both have managed cloud offerings that remove most of the infrastructure work. But they make fundamentally different trade-offs — and picking the wrong one for your use case creates problems that compound over time.

Serverless promises zero infrastructure management and automatic scaling to zero. Containers promise consistency, control, and predictable runtime behaviour. The marketing for both overstates the benefits and understates the costs.

This post gives you a direct, honest comparison so you can make the right call for what you are actually building.


🎯 Quick Answer (30-Second Read)

  • Pick serverless if: You have event-driven workloads, unpredictable or spiky traffic, and functions that complete in under a few minutes
  • Pick containers if: You need persistent connections, long-running processes, consistent cold-start latency, or full control over the runtime
  • Cost: Serverless is cheaper at low and spiky volume; containers are cheaper at sustained high throughput
  • Complexity: Serverless is simpler to start, containers are simpler to reason about at scale
  • Recommendation: Default to containers for APIs and long-running services; use serverless for event triggers, background jobs, and workloads with unpredictable traffic

What Serverless Actually Means

Serverless does not mean no servers. It means you do not manage servers. You deploy a function, define what triggers it (an HTTP request, a queue message, a cron schedule), and the cloud provider handles provisioning, scaling, and teardown automatically.

The execution model is fundamentally different from a traditional server:

Traditional Server / Container

Always running → Request arrives → Handler executes → Response returned

(server stays warm between requests)
Serverless Function

Request arrives → Container spun up → Handler executes → Container torn down

(cold start on first request or after idle period)

Every major cloud has a serverless compute offering: AWS Lambda, Google Cloud Functions, Cloudflare Workers, Vercel Functions, Netlify Functions. They differ in runtime environments, cold start behaviour, execution limits, and pricing models — but the core model is the same.

What Containers Actually Mean

A container packages your application and its dependencies into a portable, isolated unit that runs identically in any environment. On a server, in Kubernetes, in a managed container service (Cloud Run, ECS, Fly.io) — the container behaves the same way.

Unlike serverless functions, containers are long-running processes. They start once and handle many requests over their lifetime. They stay warm between requests. They can maintain in-memory state, hold open database connections, and run background threads.


Where Each One Wins

Serverless Wins: Spiky and Unpredictable Traffic

Serverless scales to zero when idle and scales to thousands of concurrent executions under load — automatically, without capacity planning. For workloads with unpredictable traffic patterns, this is a genuine advantage.

A webhook handler that processes 0 events per hour most of the day and 10,000 per hour during a business event does not need a container running 24/7 at enough capacity for peak load. A Lambda function scales to match the spike and costs nothing during idle periods.

Serverless Wins: Event-Driven Workloads

Serverless is the natural fit for event-driven architectures. S3 upload triggers an image processing function. A new row in DynamoDB triggers a notification. A queue message triggers a data transformation. Each trigger maps cleanly to a function invocation with no polling, no always-on process, no wasted compute.

Serverless Wins: Low-Volume APIs and Prototypes

At low request volume, serverless is meaningfully cheaper than running a container. A container on Cloud Run or ECS costs money even at minimal traffic. Lambda at 100,000 requests per month is effectively free. For internal tools, low-traffic APIs, and early-stage products validating demand, serverless removes the baseline infrastructure cost entirely.

Containers Win: Consistent Latency

Cold starts are serverless's most persistent problem. When a function has been idle, the first request triggers container initialisation — pulling the image, starting the runtime, loading your code. On AWS Lambda with a Node.js function this is typically 100–500ms. With a Java or .NET function it can be 1–5 seconds. For APIs where p99 latency matters, cold starts are unacceptable.

Provisioned concurrency (keeping function instances warm) mitigates cold starts but eliminates the cost advantage of serverless at scale. You are paying to keep containers warm — which is what a managed container service does by default.

Containers Win: Long-Running Processes

Serverless functions have hard execution time limits. AWS Lambda caps at 15 minutes. Cloudflare Workers cap at 30 seconds on most plans. For anything that needs to run longer — video processing, large data migrations, ML training jobs, PDF generation on large documents — containers are the right choice.

Containers Win: Stateful Connections

Database connection pooling requires persistent connections. A serverless function that opens a new database connection on every invocation will exhaust your connection limit under load — Lambda can spin up thousands of concurrent instances, each opening its own connection. Connection poolers (RDS Proxy, PgBouncer) mitigate this but add latency and cost.

Containers maintain a pool of connections per instance. A container-based API with ten replicas maintains ten connection pools — manageable and predictable.

Containers Win: Complex Runtime Requirements

Serverless functions run in constrained environments. Custom binaries, specific OS-level dependencies, large ML models, or anything requiring a non-standard runtime is either impossible or painful on serverless. Containers give you a full Linux environment — install whatever you need, run whatever binary you require.


The Right Approach vs The Wrong Approach

The right approach is using both and routing workloads to the architecture that fits them. A well-designed system is not entirely serverless or entirely containerised — it uses containers for the persistent, latency-sensitive, stateful core and serverless for the event-driven, spiky, stateless periphery.

Your user-facing API runs in containers — consistent latency, persistent database connections, full runtime control. Your image resizing, email sending, webhook processing, and scheduled data exports run as serverless functions — event-triggered, scale-to-zero, no idle cost.

This is the architecture most mature cloud-native systems converge on. The mistake is treating it as an either-or decision at the application level.

The wrong approach is going fully serverless for performance-sensitive APIs without accounting for cold starts. Developers who build REST APIs entirely on Lambda and deploy to production discover cold start latency when real users complain that the first request after idle periods is slow. Provisioned concurrency fixes it but the cost model is now equivalent to running a container — the simplicity benefit of serverless remains, but the cost benefit disappears.

The other wrong approach is running containers for everything, including workloads that are naturally event-driven and low-volume, because containers feel familiar. You end up paying for idle compute and writing polling loops for things that should be event triggers.


My Take

The reason the serverless vs containers debate never fully resolves is that it is really two separate questions disguised as one: what is the right execution model for this workload, and what is the right operational model for this team. Serverless wins the operational simplicity argument for small teams — no Dockerfiles, no Kubernetes manifests, no scaling configuration. Containers win the predictability argument for production systems where latency SLOs matter and runtime behaviour needs to be consistent. The best outcome is a team that treats these as complementary tools and routes workloads correctly — event-driven and spiky to serverless, persistent and latency-sensitive to containers. The worst outcome is a team that went fully serverless on a user-facing product, hit cold start problems at launch, added provisioned concurrency to fix it, and is now paying container prices for serverless complexity. Right now the most interesting development is platforms like Cloudflare Workers and Deno Deploy pushing the cold start problem toward zero with V8 isolates instead of full container spin-up — which genuinely changes the trade-off calculation for edge-deployed functions. Where this is heading: the distinction between serverless and containers will continue blurring as container startup times drop and serverless platforms remove execution limits. The execution model will matter less; the pricing model and operational model will matter more.


Comparison Table

Concern Serverless Containers
Cold start latency 100ms–5s (varies) None (always warm)
Scaling Automatic, to zero Manual or auto, minimum 1
Execution time limit 15 min (Lambda) None
Database connections Problematic at scale Predictable pooling
Cost at low volume Very low Fixed baseline
Cost at high sustained load Expensive Cheaper per request
Runtime flexibility Constrained Full Linux environment
Operational overhead Very low Low (managed) to high (K8s)
Best for Event-driven, spiky, async APIs, persistent services, ML

Real Developer Use Case

A developer building a SaaS document processing platform started with everything on Lambda — the REST API, the document parser, the notification sender, and the PDF generator. The REST API had cold start issues: p99 latency was 1.8 seconds because Lambda instances were spinning up on the first request after idle periods. The PDF generator on large documents was hitting Lambda's 15-minute limit.

The fix was architectural routing: the REST API moved to Cloud Run containers — always warm, consistent sub-100ms latency. The document parser and notification sender stayed on Lambda — genuinely event-driven, short-lived, scale-to-zero appropriate. The PDF generator moved to a container-based async job queue with no time limit.

Result: REST API p99 latency dropped from 1.8 seconds to 60ms. PDF generation on large documents no longer timed out. Lambda costs stayed low because only the right workloads ran on it. Total infrastructure cost dropped 35% compared to provisioned concurrency on everything.


Frequently Asked Questions

What is a cold start in serverless?

A cold start is the latency added when a serverless function spins up a new container instance to handle a request. It includes pulling the runtime, initialising the execution environment, and loading your code. Cold starts range from ~100ms for lightweight Node.js or Python functions to several seconds for JVM-based runtimes. They occur on the first request after a function has been idle or when scaling up to handle concurrent requests.

Is serverless cheaper than containers?

Depends on your traffic pattern. At low or spiky volume, serverless is significantly cheaper — you pay per invocation, not for idle time. At sustained high throughput, containers are cheaper — you are paying for reserved compute rather than per-request pricing with overhead. The crossover point varies by provider and workload but is typically around the volume where your serverless bill approaches the cost of a small container instance running continuously.

Can serverless handle a production API?

Yes, with caveats. Serverless APIs work well for workloads with low-to-moderate traffic, acceptable latency variance, and stateless request handling. For APIs with strict p99 latency requirements, heavy database connection usage, or sustained high throughput, containers are the more reliable choice. Provisioned concurrency removes cold starts on Lambda but at a cost that makes containers comparably priced.

What is the difference between AWS Lambda and Cloud Run?

AWS Lambda is a function-as-a-service platform — you deploy individual functions with execution time limits and per-invocation pricing. Google Cloud Run runs full containers on-demand — longer execution times, full runtime control, and per-request billing with scale-to-zero. Cloud Run sits between Lambda and a full container orchestration platform like Kubernetes — more flexibility than Lambda, less operational overhead than GKE.

Should I use Vercel Functions or a container for my Next.js API?

Vercel Functions (serverless) work well for most Next.js API routes — event-driven, stateless, short-lived handlers. Use containers (via a separate backend service) when you need persistent database connections without a connection pooler, long-running operations, or workloads that exceed Vercel's function execution limits. Many production Next.js apps use Vercel Functions for the frontend API layer and a separate containerised service for heavy backend workloads.


Conclusion

Serverless and containers are not competitors — they are tools with different trade-off profiles. Serverless wins on operational simplicity and cost at low, spiky volume. Containers win on latency consistency, runtime flexibility, and cost at sustained load.

The right architecture uses both. Route event-driven, short-lived, unpredictable workloads to serverless. Route persistent, latency-sensitive, stateful services to containers. Stop treating it as an either-or decision and start treating it as a workload routing problem.

Pick based on what the workload needs, not on what the last blog post you read recommended.

Related reads: Kubernetes Explained: Why Companies Actually Use It · Microservices Architecture Explained: Is It Worth It? · Why Apps Crash Under High Traffic