Serverless#

Serverless is managed compute, scaled by the platform, billed per execution. The operator never sees the underlying machines, which is the point and the trade-off. A function fires on a trigger, runs, and goes away; a redirector, a webhook, a scheduled collector, or a defensive automation lands and disappears without leaving a fleet to maintain.

Serverless from the infrastructure angle: where it fits in a DevOps stack, what operational properties it has, and what trade-offs make it the right (or wrong) choice for a given mission. For the architectural view, see Serverless.

        flowchart LR
  subgraph Triggers
    HTTP[HTTP]
    Sched[Schedule]
    Q[Queue]
    Obj[Storage Event]
    Pub[Pub-Sub]
  end

  HTTP --> F1[Function]
  Sched --> F2[Function]
  Q --> F3[Function]
  Obj --> F4[Function]
  Pub --> F5[Function]

  F1 --> DB[(Managed DB)]
  F2 --> Bucket[(Object Storage)]
  F3 --> Cache[(Managed Cache)]
  F4 --> Out[Other Service]
  F5 --> Topic[(Topic)]
    

Forms of Serverless#

The five flavors an operator may meet. FaaS for one-function triggers; container-based serverless for whole apps; edge runtimes for sub-10 ms cold starts; backend-as-a-service for managed primitives; workflow-as-a-service for durable orchestration. Each fits a different shape of workload.

  • FaaS, AWS Lambda, Google Cloud Functions, Azure Functions. One function, one trigger; runs in a sandboxed lightweight VM.

  • Container-based, AWS Fargate, Cloud Run, Azure Container Apps. Bring an OCI image; the platform schedules and scales it.

  • Edge runtimes, Cloudflare Workers, Vercel Edge Functions, Deno Deploy, AWS Lambda@Edge. V8 isolates or tiny WASM runtimes; sub-10 ms cold starts.

  • Backend-as-a-Service, Firebase, Supabase, Hasura. Managed primitives that replace code for common cases.

  • Workflow-as-a-Service, AWS Step Functions, GCP Workflows, Temporal Cloud. Durable orchestration on managed compute.

Function Topologies#

A single function is rarely the whole job. The shapes below cover how functions chain into composed workflows; each one trades control for coupling.

Sequential chain#

The simplest composition. One function’s output becomes the next function’s input, end to end. Easy to reason about; latency is the sum of every step; one failure breaks the chain unless every function is idempotent and the caller retries.

        flowchart LR
  T[trigger] --> A[fn A]
  A --> B[fn B]
  B --> C[fn C]
  C --> OUT[(result)]
    

Fan-out#

One trigger, many parallel workers. Used when the work is embarrassingly parallel, fetching N URLs, scoring N candidates, hashing N files. Latency is the slowest worker.

        flowchart LR
  T[trigger] --> A[fn A: split]
  A --> W1[fn W1]
  A --> W2[fn W2]
  A --> W3[fn W3]
  W1 --> SINK[(sink)]
  W2 --> SINK
  W3 --> SINK
    

Fan-in (aggregator)#

Many producers, one consumer. The aggregator merges results, deduplicates, and emits the combined output. Backed by a queue or a stream so the aggregator never blocks producers.

        flowchart LR
  P1[producer 1] --> Q[(queue / stream)]
  P2[producer 2] --> Q
  P3[producer 3] --> Q
  Q --> AGG[fn aggregator]
  AGG --> OUT[(result)]
    

Map-reduce#

Fan-out plus fan-in. A coordinator splits work; mappers process shards in parallel; a reducer combines the partial results. The canonical batch shape; works for ad-hoc data pipelines and scheduled rollups.

        flowchart LR
  IN[(input)] --> SPL[fn splitter]
  SPL --> M1[map 1]
  SPL --> M2[map 2]
  SPL --> M3[map 3]
  M1 --> RED[reduce]
  M2 --> RED
  M3 --> RED
  RED --> OUT[(output)]
    

Event bus / pub-sub#

Producer functions emit events to a bus; any number of consumer functions subscribe. Producers and consumers do not know each other; adding a consumer never touches a producer. The default shape for telemetry, audit, and any workflow where the write rate exceeds the read rate.

        flowchart LR
  P1[producer A] --> BUS{{event bus}}
  P2[producer B] --> BUS
  BUS --> C1[fn consumer X]
  BUS --> C2[fn consumer Y]
  BUS --> C3[fn consumer Z]
    

Choreography#

Each function reacts to events emitted by the previous step and emits its own event when done. No central orchestrator; the flow emerges from event subscriptions. Loosely coupled but the end-to-end shape lives implicitly in the subscriptions, hard to inspect.

        flowchart LR
  ORDER[fn order] -->|OrderPlaced| BUS{{event bus}}
  BUS --> PAY[fn payment]
  PAY -->|PaymentCaptured| BUS
  BUS --> INV[fn inventory]
  INV -->|StockReserved| BUS
  BUS --> SHIP[fn shipping]
    

Orchestration via state machine#

A workflow service (AWS Step Functions, GCP Workflows, Azure Logic Apps, Temporal) drives the sequence explicitly. State machine definition declares branches, retries, parallel tasks, and waits; each step is a function. The shape is auditable, testable, and survives long-running waits (hours, days).

        flowchart LR
  START([start]) --> VAL[fn validate]
  VAL --> CHK{ok?}
  CHK -->|no| ERR([reject])
  CHK -->|yes| PAR{parallel}
  PAR --> P1[fn fraud-check]
  PAR --> P2[fn inventory]
  P1 --> JOIN((join))
  P2 --> JOIN
  JOIN --> SHIP[fn ship]
  SHIP --> NOTIFY[fn notify]
  NOTIFY --> END([done])
    

Saga (compensation)#

A long-running transaction split into local steps; each with a compensating action that undoes its effect on failure. Used when the workflow crosses systems that cannot share a transaction (payment provider plus inventory plus shipping). Either choreography- or orchestration-driven.

        flowchart LR
  S1[fn reserve stock] -->|ok| S2[fn charge card]
  S2 -->|ok| S3[fn create shipment]
  S3 -->|ok| OK([commit])
  S3 -.fail.-> R3[fn refund card] --> R2[fn release stock] --> FAIL([compensated])
  S2 -.fail.-> R2
  S1 -.fail.-> FAIL
    

Pipes-and-filters#

Streaming version of the sequential chain. A producer writes records to a stream; one or more functions transform records in flight; a sink writes the final form. Each filter is independently scaled by stream lag.

        flowchart LR
  SRC[(stream)] --> F1[fn filter]
  F1 --> F2[fn enrich]
  F2 --> F3[fn transform]
  F3 --> DST[(sink)]
    

Properties That Matter#

The operational characteristics that distinguish serverless from always-on compute. Scale-to-zero, automatic scale-up, no fleet operations, per-execution billing, cold-start tax, runtime constraints, and stateless-by-design; each shapes which workloads fit and which don’t.

  • Scale to zero, no idle cost.

  • Auto-scale up, platform handles spikes.

  • No fleet to operate, no patching, no autoscaling configuration, no node management.

  • Per-execution pricing, proportional to use; can surprise under sustained load.

  • Cold starts, first request after idle pays a startup tax.

  • Runtime constraints, max execution time (15 min Lambda, more for container-based), memory, payload size, no local filesystem of consequence.

  • Stateless by design, state goes in managed services.

Cold Starts in Practice#

Runtime

Typical cold start (after idle)

Lambda Node.js / Python

100-500 ms

Lambda Java / .NET

500 ms - 3 s

Lambda Go / Rust

~50-150 ms

Lambda + provisioned conc.

sub-50 ms (paid pre-warming)

Cloud Run (container)

500 ms - 2 s

Edge runtimes (Workers etc.)

<10 ms

Mitigations: smaller deployment packages, faster runtimes (Go / Rust / Bun), provisioned concurrency, edge runtimes, “warmer” pings (a ritual that’s mostly been replaced by provisioned concurrency).

Where Serverless Wins#

The shapes of workload that serverless was built for. Spiky and event-driven, glue between cloud services, low-traffic APIs, edge-near work, periodic jobs, and internal tools that can’t justify a server; each plays to the no-idle-cost story.

  • Spiky / event-driven workloads, Lambda triggered by S3 uploads, queue messages, schedule.

  • Glue between cloud services, thin code with managed primitives doing the heavy lifting.

  • Low-traffic APIs, the no-idle-cost story dominates.

  • Edge-near work, A/B routing, personalization, auth middleware close to users.

  • Periodic jobs, cron without a host.

  • Internal tools that don’t justify a server.

Where Serverless Loses#

The mirror image. Sustained high traffic loses to always-on containers; long-running computations bump against time limits; heavy startup costs hurt latency; state-heavy workloads strain the surrounding services; hard real-time fights cold starts; and vendor lock-in is real.

  • Sustained high-traffic workloads, always-on containers are usually cheaper.

  • Long-running computations, function time limits bite.

  • Heavy startup costs, JVM / .NET in a latency-sensitive path.

  • State-heavy workloads, the surrounding managed services need to scale alongside.

  • Hard-real-time, predictable latency is hard with cold starts.

  • Local dev parity, emulators are imperfect.

  • Vendor lock-in, function signatures, triggers, IAM, observability are all platform-specific.

Serverless and the Rest of the Stack#

Serverless is not a separate world from the rest of DevOps:

  • IaC still applies, Terraform / CDK / SAM provision functions.

  • CI/CD still applies, build artifact → deploy via SAM / CDK / Pulumi / Terraform.

  • Observability still applies, platform integrations (CloudWatch Logs, X-Ray) plus OpenTelemetry.

  • Security still applies, IAM scoping, secrets management, least privilege.

  • GitOps, frameworks like SST / Encore / Architect provide Git-driven deploy semantics.

Frameworks#

The frameworks an operator picks among when authoring serverless applications. AWS SAM and CDK lead inside AWS; Serverless Framework is the multi-cloud abstraction; SST is the modern TypeScript-first option; Knative and OpenFaaS self-host the model on Kubernetes.

  • AWS Lambda + SAM / AWS CDK, AWS-native.

  • Serverless Framework, multi-cloud abstraction (declining a bit in favor of CDK / SAM / SST).

  • SST, TypeScript-first serverless framework.

  • Encore, Go / TypeScript framework with built-in infra.

  • OpenFaaS / Knative / Fission, run serverless on your own Kubernetes.

  • Wrangler, Cloudflare Workers tooling.

For greenfield serverless work in 2026, SST (TypeScript) and CDK (any of its languages) are the strongest defaults.

Cost Model#

A useful mental model for steady-state cost:

  • FaaS, per-request charge + memory × duration. Sub-second responses are cheap; minute-long ones aren’t.

  • Container serverless, per-second-while-running. Cheaper for steady traffic.

  • Always-on containers, pay for the always-on capacity.

The break-even between Lambda and Fargate / Cloud Run is usually around 10-30% utilization. Above that, containers are cheaper.

Operational Cautions#

The traps that catch teams running serverless in production. Account-level concurrency throttles surprise; connection storms drown databases without proxies; async trigger retries demand idempotent design; logs and traces spread thinly across many short-lived processes.

  • Concurrency limits, AWS Lambda has account-level reserved concurrency; can throttle unexpectedly.

  • Connection storms to databases, thousands of warm function instances all open DB connections; use proxies (RDS Proxy, PgBouncer) or pools.

  • Retries on async triggers, not always idempotent; design for at-least-once delivery.

  • Logs and traces, spread across many short-lived processes; invest in structured logging and tracing early.

The 2026 Take#

Serverless settled into a clear sweet spot:

  • For event-driven, glue, edge, and low-traffic, the right default.

  • For full applications, container-based serverless (Cloud Run, Fargate, Container Apps) is often the right middle ground.

  • For high-traffic core services, always-on containers on Kubernetes (or simpler PaaS) usually win on cost and operational parity.

The “serverless or Kubernetes” debate has mostly been resolved into “both, in the right places”.

See Also#