Text size
On My Radar5 min read

Serverless or containers?

Lambda, Cloud Run or Kubernetes: which fits which workload.

Serverless or containers?
AI-generated editorial illustration

Few architecture debates have produced as many conference talks as this one. Serverless fans say servers are a distraction and you should only write business logic. Container fans say serverless is expensive, limiting and locks you in. Both have a point.

They're tools for different jobs, and the line between them has blurred a lot.

The options

Functions as a Service (FaaS): AWS Lambda, Azure Functions, Google Cloud Run functions. You upload a function, the platform runs it when an event arrives (an HTTP request, a file upload, a queue message), and you pay per invocation and execution time. No servers, no containers to think about.

Serverless containers: Google Cloud Run, AWS Fargate (with ECS), Azure Container Apps, AWS App Runner. You provide a container image, the platform runs and scales it, often down to zero. You don't manage nodes or clusters.

Kubernetes: managed (EKS, AKS, GKE) or self-hosted. You run containers on a cluster you control, with full flexibility and full responsibility.

Knative and similar projects bring serverless-style scale-to-zero and event-driven behaviour onto Kubernetes.

These form a spectrum: more control on one end, less operational work on the other.

Serverless doesn't mean there are no servers. It means they're someone else's problem, and you pay for that.

Where serverless shines

  • Event-driven glue: reacting to file uploads, database changes, webhooks, scheduled jobs.
  • Spiky or unpredictable traffic: zero cost when idle, instant scaling for bursts.
  • Small teams who want to ship without running infrastructure.
  • Prototypes and new products, where speed matters more than optimisation.
  • Low-traffic services, where paying for an always-on container would be wasteful.

Where containers on Kubernetes shine

  • Steady, high traffic: at constant high load, paying per request becomes more expensive than paying for capacity.
  • Long-running processes: workers, streaming, WebSockets, anything that runs longer than a function's time limit.
  • Complex systems with many services, service meshes, custom networking.
  • Special hardware: GPUs, high memory, specific instance types.
  • Portability across clouds and on-premises.
  • Teams with platform engineering capacity who can run Kubernetes well.

Cold starts and limits

The classic serverless drawbacks:

  • Cold starts: when a function hasn't run for a while, the first request takes longer while the platform starts a new instance. Usually small for lightweight runtimes, more noticeable for heavy ones like Java with big frameworks. Mitigations exist: provisioned concurrency, snapshot-based fast start features, minimum instances on Cloud Run.
  • Time limits: functions have maximum execution times (15 minutes on Lambda, for example).
  • Resource limits: memory, CPU, package size, temporary storage.
  • Statelessness: no local state between invocations. Everything goes to external storage.
  • Observability and debugging across many small functions can be harder.
  • Lock-in: function code often ends up tightly tied to provider-specific events and services.

Serverless containers like Cloud Run soften many of these: standard containers, longer request times, concurrency per instance, and much easier portability.

Where the costs cross over

Cost depends on usage pattern:

  • Low or bursty usage: serverless is often dramatically cheaper because you pay nothing when idle.
  • Moderate usage: roughly comparable, and serverless saves on operations work.
  • High, steady usage: containers on reserved or Spot capacity usually win on raw infrastructure cost.

Don't forget people cost. A Kubernetes platform needs engineers to run it. For a small team, serverless can be cheaper overall even if the cloud bill is higher.

Mixing both

Most mature architectures use both:

  • Core APIs and long-running services on Kubernetes or serverless containers.
  • Event handlers, scheduled jobs and glue code as functions.
  • Data pipelines with managed serverless services (queues, workflows, stream processing).

And the choice doesn't have to be permanent. A service can start on Cloud Run or Lambda and move to Kubernetes when traffic grows, especially if it's packaged as a standard container from the start.

So, which one?

My default advice:

  • Small team, new product: start serverless, ideally serverless containers. Ship fast.
  • Event-driven tasks: functions.
  • Large, steady workloads, many services, platform team in place: Kubernetes.
  • Not sure: package everything as containers. That keeps your options open.

My take: choose the least infrastructure you can get away with today, and keep your code portable enough to change your mind tomorrow.

— Desly