Node.js Serverless on AWS Lambda · Running Node.js Serverless in Production
Lambda, Cloudflare Workers, Vercel or Cloud Run?
When Lambda is the wrong tool: matching workload shape to platform for Node.js services.
Scenario
Example app: a team runs a high-traffic Node.js API (steady 400 requests per second, 60 ms average, mostly waiting on a database) on Lambda, plus a WebSocket chat feature and a few long video-processing jobs.
What you see in production
The Lambda bill for the API exceeds what a few containers would cost. WebSocket connections need extra infrastructure (API Gateway WebSocket APIs plus connection tracking), and video jobs hit the 15-minute timeout. Meanwhile a separate marketing site on an edge platform serves pages faster worldwide.
The naive code
Everything is a Lambda function by default, regardless of workload shape:
Api: { Type: AWS::Serverless::Function, Properties: { Timeout: 30 } } # steady, high traffic
Chat: { Type: AWS::Serverless::Function } # long-lived connections
VideoEncode: { Type: AWS::Serverless::Function, Properties: { Timeout: 900 } } # needs hours
Reproduce it
A rough model of a steady, I/O-bound API: Lambda bills each request's duration, while a container serves many concurrent requests per instance:
const rps = 400, avgMs = 60, memoryGb = 0.5, secondsPerMonth = 30 * 24 * 3600;
const lambdaPerGbSecond = 0.0000166667, lambdaPerMillionRequests = 0.2; // check current pricing
const lambdaCost = rps * secondsPerMonth * ((avgMs / 1000) * memoryGb * lambdaPerGbSecond) + (rps * secondsPerMonth / 1e6) * lambdaPerMillionRequests;
const concurrentPerInstance = 80; // an I/O-bound Node.js server handles many requests at once
const instances = Math.ceil((rps * (avgMs / 1000)) / concurrentPerInstance) + 2; // plus headroom
const containerPerInstanceMonth = 30; // illustrative price of a small always-on container
console.log("Lambda monthly: $" + lambdaCost.toFixed(0));
console.log("Containers monthly: $" + instances * containerPerInstanceMonth, `(${instances} instances)`);
console.log("Same API at 4 rps: Lambda $" + (lambdaCost / 100).toFixed(2), "vs containers $" + 2 * containerPerInstanceMonth);
Output
Lambda monthly: $726 Containers monthly: $90 (3 instances) Same API at 4 rps: Lambda $7.26 vs containers $60
Root cause
Platforms optimise for different shapes. Lambda bills per invocation and duration with one request per environment at a time: excellent for spiky, event-driven and low-to-moderate traffic, less efficient for steady high-throughput I/O-bound APIs, long-lived connections or very long jobs.
The fix
Match each workload to a platform:
- AWS Lambda: event-driven processing, spiky or unpredictable traffic, glue between AWS services, APIs with modest steady load. Deep AWS integration.
- Container platforms with request concurrency (AWS Fargate/ECS, App Runner, Google Cloud Run): steady high-throughput APIs, WebSockets and streaming, long jobs (Cloud Run jobs, ECS tasks, AWS Batch), anything needing more control of the runtime.
- Edge runtimes such as Cloudflare Workers: latency-sensitive logic near users (routing, auth checks, personalisation, caching) using V8 isolates with very fast start-up, with a more limited Node.js API surface and CPU-time limits.
- Vercel Functions: front-end frameworks (Next.js) where the platform handles routing, rendering and deployment workflow.
Many systems mix them: an edge layer for routing and caching, containers for the core API, Lambda for events and integrations.
Trade-offs
- Containers need capacity management and patching (less with Fargate or Cloud Run) but are cheaper at steady load.
- Edge runtimes are fast and global but restrict libraries and long CPU work, and data often lives in one region anyway.
- Multiple platforms add operational surface; standardise tooling (IaC, observability) across them.
Detect it in production
- Cost per request and utilisation trends per workload; steady high concurrency on Lambda is a signal to reconsider.
- Timeouts and workarounds (chunking jobs to fit 15 minutes, connection tracking tables) indicate a shape mismatch.
- Latency by user region for user-facing endpoints.
Key points
- Lambda excels at event-driven, spiky and integration workloads.
- Steady high-throughput APIs, WebSockets and long jobs often fit containers better.
- Edge runtimes for latency-critical logic near users; mix platforms deliberately.
Further reading: AWS Lambda quotas · Cloudflare Workers documentation · Vercel Functions · Google Cloud Run documentation
Practice questions
What workload characteristics suggest a container platform instead of Lambda?
Answer
Steady, high-throughput, I/O-bound traffic where one instance can serve many concurrent requests; long-lived connections such as WebSockets; jobs longer than 15 minutes; or the need for specific runtimes and system dependencies.
What are edge runtimes like Cloudflare Workers good at, and what are their limits?
Answer
Running small pieces of logic close to users with very fast start-up (V8 isolates): routing, authentication checks, personalisation and caching. They limit available Node.js APIs and CPU time, and data usually lives in a central region.
Why might one system use Lambda, containers and an edge runtime together?
Answer
Each fits a different workload shape: edge for latency-sensitive request handling, containers for the steady core API and long-running work, and Lambda for event-driven processing and AWS integrations. Standardised IaC and observability keep the mix manageable.
Open in the app to mark lessons complete, take notes and track your progress.