Comparing Serverless Platforms#
Choosing a serverless platform is not about which one is “best.” Each platform makes different tradeoffs around cold start latency, execution limits, pricing granularity, and ecosystem integration. The right choice depends on what you are building, what cloud you already use, and which constraints matter most.
This framework compares the four major serverless compute platforms as of early 2026: AWS Lambda, Google Cloud Run, Azure Functions, and Cloudflare Workers.
Execution Model Differences#
The platforms fall into two categories based on their execution model:
Function-as-a-Service (FaaS): Lambda and Azure Functions. You write a handler function. The platform invokes it per event. You do not think about servers, containers, or HTTP routing – the platform maps triggers to function invocations.
Container-as-a-Service (CaaS): Cloud Run. You deploy a container image that listens on a port. The platform routes HTTP requests to it and scales instances based on traffic. You have full control over the HTTP server, middleware, and routing.
Edge Runtime: Cloudflare Workers. Your code runs in V8 isolates at 300+ edge locations worldwide. No container, no operating system – just JavaScript/TypeScript/WASM executing at the network edge with sub-millisecond cold starts.
This distinction matters. Cloud Run can run any existing web application in a container. Lambda requires restructuring your code into event handlers. Workers impose V8 runtime constraints but deliver the lowest latency for global workloads.
Cold Start Comparison#
Cold starts are the defining constraint of serverless. When a new instance must be initialized, the first request pays a latency penalty. The magnitude varies dramatically across platforms.
| Platform | Typical Cold Start | Worst Case | Mitigation |
|---|---|---|---|
| Lambda (Node.js/Python) | 100-300ms | 1-2s (VPC, large package) | Provisioned concurrency ($), SnapStart (Java) |
| Lambda (Java/.NET) | 1-5s | 10s+ | SnapStart, GraalVM native image |
| Cloud Run | 300ms-2s | 5s+ (large image, slow app init) | Min instances (1+), startup CPU boost |
| Azure Functions (Consumption) | 500ms-2s | 10s+ (first cold start after idle) | Premium plan (pre-warmed instances) |
| Cloudflare Workers | 0-5ms | 5ms | None needed – V8 isolate startup is near-instant |
Lambda cold starts depend heavily on runtime, package size, and VPC attachment. Node.js and Python are fast. Java is slow unless you use SnapStart or GraalVM native images. Provisioned concurrency eliminates cold starts entirely but costs money whether invocations arrive or not.
Cloud Run cold starts depend on container image size and application startup time. A slim Alpine-based Go binary starts in under 500ms. A Spring Boot Java application can take 5+ seconds. Use minimum instances to keep at least one container warm, and enable startup CPU boost (Cloud Run temporarily allocates more CPU during container initialization).
Azure Functions on the Consumption plan can have the longest cold starts, especially after extended idle periods where the entire function app is deallocated. The Premium plan keeps instances pre-warmed and supports VNet integration. The Flex Consumption plan (newer) offers per-function scaling with always-ready instances.
Cloudflare Workers do not have meaningful cold starts. V8 isolate creation takes single-digit milliseconds. This is architecturally different – Workers do not start a process or container, they start a JavaScript context within an existing V8 engine.
Pricing Models#
All four platforms charge differently, and the cheapest option depends on your workload pattern.
AWS Lambda:
- $0.20 per 1M requests
- $0.0000166667 per GB-second of compute
- Free tier: 1M requests + 400,000 GB-seconds per month
- Provisioned concurrency adds a per-hour charge per provisioned instance
Google Cloud Run:
- $0.00002400 per vCPU-second
- $0.00000250 per GiB-second
- $0.40 per million requests
- Free tier: 2M requests + 180,000 vCPU-seconds per month
- Supports “always allocated” billing (pay while instances exist) or “request-based” (pay only during request processing)
Azure Functions (Consumption):
- $0.20 per 1M executions
- $0.000016 per GB-second
- Free tier: 1M requests + 400,000 GB-seconds per month
- Premium plan has per-second billing with pre-warmed instances
Cloudflare Workers:
- Free plan: 100K requests/day, 10ms CPU time per invocation
- Paid ($5/month): 10M requests included, $0.30 per additional million
- No per-GB-second charge; billing is per-request + CPU time
- Workers Unbound: 30s+ CPU time, billed per GB-second
Cost patterns:
- Low, bursty traffic (under 1M requests/month): All platforms are effectively free. Use the free tier.
- Moderate traffic (1-100M requests/month): Lambda and Azure Functions (Consumption) are cheapest for short-duration functions. Cloud Run with request-based billing is competitive if you can tolerate cold starts.
- High, steady traffic (100M+ requests/month): Cloud Run with committed use discounts or Kubernetes with Knative often beats Lambda. At scale, per-request pricing adds up.
- Global, latency-sensitive: Cloudflare Workers is the cheapest option for read-heavy, edge-distributed workloads. The per-request pricing is low and there is no data transfer cost within Cloudflare’s network.
Runtime Support#
| Feature | Lambda | Cloud Run | Azure Functions | Cloudflare Workers |
|---|---|---|---|---|
| Node.js | Yes (managed) | Yes (any version) | Yes (managed) | Yes (V8 runtime) |
| Python | Yes (managed) | Yes (any version) | Yes (managed) | No (WASM only) |
| Go | Yes (provided.al2023) | Yes (native) | No (custom handler) | No (WASM only) |
| Java | Yes (managed) | Yes (any version) | Yes (managed) | No |
| .NET | Yes (managed) | Yes (any version) | Yes (managed, first-class) | No (WASM limited) |
| Rust | Yes (provided.al2023) | Yes (native) | No (custom handler) | Yes (WASM) |
| Custom container | Yes (up to 10GB) | Yes (any OCI image) | Yes (custom handler) | No |
| Execution timeout | 15 minutes | 60 minutes (gen2) | 10 min (Consumption), 60 min (Premium) | 30s (Unbound), 10ms (Free) |
| Max memory | 10,240 MB | 32 GiB | 1.5 GB (Consumption), 14 GB (Premium) | 128 MB |
Cloud Run has the broadest runtime support because you deploy a container – anything that runs in Docker runs on Cloud Run. This is its strongest advantage for teams migrating existing applications.
Lambda supports the most runtimes natively and provides the deepest AWS ecosystem integration.
Azure Functions offers the best .NET experience and the deepest Azure integration.
Cloudflare Workers is limited to JavaScript/TypeScript and WASM but offers the lowest latency and the simplest deployment model. If your workload fits within the constraints, the developer experience is excellent.
Scaling Behavior#
| Aspect | Lambda | Cloud Run | Azure Functions | Cloudflare Workers |
|---|---|---|---|---|
| Scale-to-zero | Yes | Yes (configurable) | Yes (Consumption) | N/A (always hot) |
| Max concurrency per instance | 1 (default) | 80 (configurable, up to 1000) | Varies by plan | N/A |
| Max instances | 1,000 (default, can increase) | 100 (default, can increase to 1000+) | 200 (Consumption) | No limit |
| Scale-up speed | Fast (~1,000 instances/10s) | Moderate (new container startup) | Slow (Consumption) to Fast (Premium) | Instant |
A critical difference: Lambda runs one request per instance by default. If 100 concurrent requests arrive, Lambda creates 100 instances. Cloud Run handles 80 concurrent requests per instance by default, so 100 requests need only 2 instances. This affects both cold start frequency and cost.
Lambda’s reserved concurrency mode and Cloud Run’s max instances setting both prevent runaway scaling from overwhelming downstream systems like databases.
VPC and Network Access#
| Platform | VPC Access | Private Connectivity | Static IP |
|---|---|---|---|
| Lambda | VPC attachment (ENI-based) | PrivateLink, VPC endpoints | NAT Gateway for egress |
| Cloud Run | Serverless VPC Access connector, Direct VPC Egress | Private Google Access, Private Service Connect | Cloud NAT |
| Azure Functions | VNet integration (Premium/Dedicated) | Private Endpoints | NAT Gateway |
| Cloudflare Workers | Cloudflare Tunnel to private networks | Warp Connector | N/A (exits via Cloudflare IPs) |
For workloads that need to reach private databases or internal services, Lambda and Cloud Run offer mature VPC integration. Azure Functions requires the Premium plan for VNet access. Workers can reach private infrastructure through Cloudflare Tunnel but the model is fundamentally different from VPC peering.
Decision Matrix#
| If you need… | Choose |
|---|---|
| Lowest possible latency globally | Cloudflare Workers |
| Deepest AWS integration (S3, DynamoDB, SQS triggers) | Lambda |
| Run an existing containerized application without refactoring | Cloud Run |
| .NET workloads with Azure ecosystem | Azure Functions |
| Long-running tasks (10-60 minutes) | Cloud Run (gen2) |
| Scale-to-zero with Kubernetes control | Knative on your cluster |
| Lowest cost at very high scale | Cloud Run or self-managed Knative |
| Sub-second cold starts with Java | Lambda SnapStart or Cloud Run with GraalVM native |
| Simple key-value storage co-located with compute | Cloudflare Workers + KV/D1/R2 |
| Event-driven processing with complex routing | Lambda + EventBridge |
Practical Recommendations#
Start with your cloud provider. If you run on AWS, start with Lambda. On GCP, Cloud Run. On Azure, Azure Functions. The integration advantages – IAM, monitoring, networking, event sources – outweigh the theoretical benefits of another platform.
Use Cloudflare Workers for the edge layer. API routing, authentication, rate limiting, A/B testing, and content transformation are ideal Workers use cases. Workers complement rather than replace your primary serverless platform.
Do not optimize cold starts prematurely. Measure first. If your p99 latency is acceptable, cold starts are not a problem. Most serverless workloads are asynchronous (queue processing, event handling) where cold start latency is invisible to users.
Plan for vendor lock-in at the integration layer, not the compute layer. Your function code is portable. The event sources, IAM policies, and service integrations are not. Design business logic as importable modules that the serverless handler orchestrates, so the core is testable and portable even if the handler layer is provider-specific.