Skip to main content
← All Articles · · 15 min read

AWS Lambda vs Google Cloud Functions vs Azure Functions vs Cloudflare Workers: Serverless 2026

A detailed technical comparison of AWS Lambda, Google Cloud Functions, Azure Functions, and Cloudflare Workers for serverless development in 2026 — covering cold starts, pricing, limits, and real-world use cases.

aws-lambdaserverlesscloud-functionsazure-functionscloudflare-workerscloudbackend

Serverless functions have matured from a niche deployment model into mainstream infrastructure for APIs, event processing, and edge computing. But “serverless” means different things on AWS, Google Cloud, Azure, and Cloudflare — and picking the wrong platform can mean fighting cold starts, surprise bills, or hitting hard limits at the worst possible time.

This guide gives you the honest technical comparison you need to choose the right serverless platform for your workload in 2026.


The Core Architecture Difference

Before comparing numbers, understand the fundamental split:

PlatformExecution modelRuntime isolation
AWS LambdaContainer-based, warm instance reuseFirecracker microVMs
Google Cloud FunctionsContainer-based, warm instance reusegVisor containers
Azure FunctionsProcess-based host modelHyper-V isolation
Cloudflare WorkersV8 isolate-basedV8 JavaScript isolates

This matters because Cloudflare Workers uses a fundamentally different model — V8 isolates instead of containers — which enables near-zero cold starts but restricts what you can run.


Quick Comparison Table

FeatureAWS LambdaGCF Gen2Azure FunctionsCF Workers
Cold start (P50)100–500ms100–600ms200–800ms<5ms
Warm start (P50)1–5ms1–5ms1–5ms<1ms
Max execution time15 minutes60 minutes230 seconds (Consumption)30s (CPU time)
Max memory10,240 MB32,768 MB14,336 MB128 MB (Workers Free) / 2 GB (Paid)
Languages (native)Node, Python, Ruby, Java, Go, .NET, custom runtimeNode, Python, Go, Java, Ruby, PHPNode, Python, Java, .NET, PowerShell, customJavaScript, TypeScript, Rust, Python (beta)
Pricing modelPer request + GB-secondPer request + GB-secondPer request + GB-secondPer request (flat)
Free tier1M req/mo + 400K GB-sec2M req/mo + 400K GB-sec1M req/mo100K req/day
Global edgeLambda@Edge (limited)No native edgeNo native edgeYes — 300+ PoPs
VPC supportYesYesYesNo (Workers) / Yes (Workers for Platforms)
Max deployment size250 MB (zip) / 10 GB (container)100 MB (zip) / 1 GB (container)1 GB (zip)1 MB (Workers Free) / 10 MB (Paid)

AWS Lambda: The Default Choice

Architecture Deep Dive

Lambda pioneered the modern serverless model. Each function invocation runs in an isolated Firecracker microVM. Lambda reuses warm execution environments for subsequent invocations — the key optimization is minimizing cold start frequency.

// AWS Lambda handler (Node.js 20)
export const handler = async (event, context) => {
  // context.callbackWaitsForEmptyEventLoop = false;  // Important for DB connections

  const { httpMethod, path, body } = event;

  // Parse body only once outside handler for warm invocations
  const payload = body ? JSON.parse(body) : null;

  try {
    const result = await processRequest(payload);

    return {
      statusCode: 200,
      headers: {
        "Content-Type": "application/json",
        "Access-Control-Allow-Origin": "*",
      },
      body: JSON.stringify({ success: true, data: result }),
    };
  } catch (error) {
    console.error("Handler error:", error);
    return {
      statusCode: 500,
      body: JSON.stringify({ success: false, error: error.message }),
    };
  }
};
# AWS Lambda handler (Python 3.12)
import json
import boto3
from typing import Any

# Initialize outside handler — reused across warm invocations
dynamodb = boto3.resource("dynamodb")
table = dynamodb.Table("users")

def handler(event: dict, context: Any) -> dict:
    user_id = event.get("pathParameters", {}).get("userId")

    if not user_id:
        return {"statusCode": 400, "body": json.dumps({"error": "userId required"})}

    response = table.get_item(Key={"userId": user_id})
    item = response.get("Item")

    if not item:
        return {"statusCode": 404, "body": json.dumps({"error": "User not found"})}

    return {
        "statusCode": 200,
        "body": json.dumps(item, default=str),
    }

Lambda Function URL (direct HTTPS endpoint)

# terraform/lambda.tf
resource "aws_lambda_function_url" "api" {
  function_name      = aws_lambda_function.api.function_name
  authorization_type = "NONE"  # or "AWS_IAM"

  cors {
    allow_credentials = true
    allow_origins     = ["https://example.com"]
    allow_methods     = ["GET", "POST"]
    allow_headers     = ["Content-Type", "Authorization"]
    max_age           = 86400
  }
}

Lambda Pros and Cons

Pros:

  • Deepest AWS ecosystem integration (DynamoDB, S3, SQS, EventBridge, RDS Proxy)
  • Container image support up to 10 GB — run arbitrary workloads
  • SnapStart (Java) and Lambda Layers for cold start optimization
  • Mature tooling: SAM, Serverless Framework, CDK, Terraform

Cons:

  • Cold starts are noticeable for latency-sensitive APIs (especially Java/.NET)
  • Pricing complexity: execution duration + memory + requests + data transfer
  • VPC Lambda cold starts are much slower (ENI attachment, though improved with Hyperplane)
  • 15-minute max execution time; not suitable for long-running jobs

Lambda Pricing Example

For an API handling 10 million requests/month, averaging 200ms at 512 MB memory:

Requests: 10M × $0.0000002 = $2.00
Duration: 10M × 0.2s × 0.5 GB × $0.0000166667/GB-sec = $16.67
Total: ~$18.67/month

(After free tier deduction: ~$15/month)


Google Cloud Functions (Gen2): Container-First

Architecture

GCF Gen2 is built on Cloud Run — functions are deployed as containers with concurrency support. Unlike Lambda’s one-invocation-per-instance model, GCF Gen2 can handle multiple concurrent requests per instance, dramatically reducing cold start frequency at high throughput.

# Google Cloud Functions Gen2 (Python 3.12)
import functions_framework
from google.cloud import firestore

# Initialized once at module load
db = firestore.Client()

@functions_framework.http
def handle_request(request):
    if request.method == "OPTIONS":
        headers = {
            "Access-Control-Allow-Origin": "*",
            "Access-Control-Allow-Methods": "POST",
            "Access-Control-Allow-Headers": "Content-Type",
        }
        return ("", 204, headers)

    data = request.get_json(silent=True)
    if not data or "userId" not in data:
        return ({"error": "userId required"}, 400)

    doc = db.collection("users").document(data["userId"]).get()

    if not doc.exists:
        return ({"error": "Not found"}, 404)

    return (doc.to_dict(), 200)
// Google Cloud Functions Gen2 (Node.js 20)
const { Firestore } = require("@google-cloud/firestore");

// Reuse Firestore connection across warm invocations
const db = new Firestore();

exports.handleRequest = async (req, res) => {
  res.set("Access-Control-Allow-Origin", "*");

  if (req.method === "OPTIONS") {
    res.set("Access-Control-Allow-Methods", "GET, POST");
    res.set("Access-Control-Allow-Headers", "Content-Type");
    return res.status(204).send("");
  }

  const { userId } = req.body;
  if (!userId) {
    return res.status(400).json({ error: "userId required" });
  }

  const doc = await db.collection("users").doc(userId).get();

  if (!doc.exists) {
    return res.status(404).json({ error: "Not found" });
  }

  return res.json(doc.data());
};

GCF Gen2 Pros and Cons

Pros:

  • Concurrent request handling per instance (Cloud Run model) — better cold start economics
  • 60-minute max execution time — useful for batch processing
  • 32 GB max memory — handles memory-intensive ML inference
  • BigQuery, Pub/Sub, and GCS native triggers

Cons:

  • Smaller ecosystem than AWS
  • Cold start performance slightly worse than Lambda for single-request patterns
  • Concurrency model requires stateless design discipline
  • IAM configuration can be complex

GCF Pricing Example

Same workload: 10 million requests/month, 200ms at 512 MB:

Requests: 10M × $0.0000004 = $4.00
Duration: 10M × 0.2s × 0.5 GB × $0.0000100/GB-sec = $10.00
Total: ~$14.00/month

(After free tier: ~$10/month)


Azure Functions: Enterprise .NET Home

Architecture

Azure Functions uses a host process model — multiple function instances share a host process. This enables Durable Functions for stateful orchestration, a unique capability in the serverless space.

// Azure Functions (C# .NET 8 Isolated)
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using System.Net;

public class UserFunction
{
    private readonly IUserRepository _repo;

    public UserFunction(IUserRepository repo)
    {
        _repo = repo;  // Injected via DI — reused across invocations
    }

    [Function("GetUser")]
    public async Task<HttpResponseData> GetUser(
        [HttpTrigger(AuthorizationLevel.Function, "get", Route = "users/{userId}")] HttpRequestData req,
        string userId,
        FunctionContext context)
    {
        var logger = context.GetLogger("GetUser");
        logger.LogInformation("Getting user {UserId}", userId);

        var user = await _repo.GetByIdAsync(userId);

        var response = req.CreateResponse();

        if (user is null)
        {
            response.StatusCode = HttpStatusCode.NotFound;
            await response.WriteAsJsonAsync(new { error = "User not found" });
            return response;
        }

        response.StatusCode = HttpStatusCode.OK;
        await response.WriteAsJsonAsync(user);
        return response;
    }
}

Durable Functions — Unique to Azure

// Durable Functions: orchestrating multi-step workflows
const df = require("durable-functions");

// Orchestrator function
module.exports = df.orchestrator(function* (context) {
  const orderId = context.df.getInput();

  // Fan-out / fan-in pattern
  const tasks = [
    context.df.callActivity("ValidateInventory", orderId),
    context.df.callActivity("CheckFraud", orderId),
    context.df.callActivity("GetShippingRate", orderId),
  ];

  const [inventory, fraud, shipping] = yield context.df.Task.all(tasks);

  if (!inventory.available || fraud.flagged) {
    return { status: "rejected", reason: fraud.flagged ? "fraud" : "out-of-stock" };
  }

  yield context.df.callActivity("ProcessPayment", { orderId, shipping });
  yield context.df.callActivity("SendConfirmation", orderId);

  return { status: "fulfilled", orderId };
});

Azure Functions Pros and Cons

Pros:

  • Best-in-class .NET and C# support — native to Azure ecosystem
  • Durable Functions for stateful orchestration (unique capability)
  • Deep integration with Azure Event Hubs, Service Bus, Cosmos DB
  • Enterprise security features (VNET integration, Managed Identity)

Cons:

  • Consumption plan has 230-second execution limit (use Premium/Dedicated for longer)
  • Cold starts are the worst of the four for .NET workloads
  • Azure-specific patterns lock you in more than Lambda/GCF
  • Premium plan pricing is significantly higher for low-traffic functions

Cloudflare Workers: Edge-First, V8 Isolates

Architecture

Workers uses V8 isolates — the same isolation primitive as Chrome tabs. This eliminates container startup overhead entirely: cold starts are consistently under 5ms globally because there’s no OS process to start.

The trade-off: you can only run JavaScript/TypeScript/Wasm natively. No OS system calls, no native binaries, no traditional file system access.

// Cloudflare Worker (TypeScript)
export interface Env {
  USERS: KVNamespace;
  API_KEY: string;
}

export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
    const url = new URL(request.url);

    if (request.method === "OPTIONS") {
      return new Response(null, {
        status: 204,
        headers: {
          "Access-Control-Allow-Origin": "*",
          "Access-Control-Allow-Methods": "GET, POST",
          "Access-Control-Allow-Headers": "Content-Type, Authorization",
        },
      });
    }

    const userId = url.pathname.split("/").pop();
    if (!userId) {
      return Response.json({ error: "userId required" }, { status: 400 });
    }

    // KV is globally distributed — low latency reads
    const user = await env.USERS.get(userId, { type: "json" });

    if (!user) {
      return Response.json({ error: "Not found" }, { status: 404 });
    }

    return Response.json(user, {
      headers: {
        "Access-Control-Allow-Origin": "*",
        "Cache-Control": "s-maxage=60",
      },
    });
  },
};

Workers KV + D1 (SQLite at the edge)

// Cloudflare D1 — SQLite database at the edge
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);
    const userId = url.searchParams.get("userId");

    const result = await env.DB.prepare(
      "SELECT id, name, email, created_at FROM users WHERE id = ?1"
    )
      .bind(userId)
      .first();

    if (!result) {
      return Response.json({ error: "User not found" }, { status: 404 });
    }

    return Response.json(result);
  },
};

Workers Pros and Cons

Pros:

  • Sub-5ms cold starts globally — no other platform comes close
  • 300+ edge locations — requests served from the closest PoP
  • Simple, flat pricing model (no GB-second confusion)
  • Workers KV, D1 (SQLite), R2 (object storage), Queues — growing data ecosystem

Cons:

  • JavaScript/TypeScript/Wasm only for core logic
  • 128 MB memory limit on free tier; 2 GB on paid
  • No traditional filesystem access or native binary execution
  • 30-second CPU time limit (wall time can be longer for I/O-bound work)
  • No VPC support for Workers (only Workers for Platforms enterprise tier)

Workers Pricing Example

Same workload: 10 million requests/month:

Workers Paid: $5/month flat (includes 10M requests)
Excess: $0.30/million additional requests

Total: $5.00/month

Cloudflare Workers is dramatically cheaper for high-request-count, low-CPU workloads.


Cold Start Deep Dive

Cold starts are the most-cited serverless pain point. Here’s what actually causes them:

PhaseLambdaGCF Gen2Azure FunctionsCF Workers
Environment init50–200ms50–300ms100–400ms~0ms
Runtime init50–300ms50–300ms100–400ms~0ms
Code import10–100ms10–100ms10–200ms~1ms
Total (Node.js)100–500ms150–600ms200–800ms<5ms
Total (Python)150–700ms200–800ms300–1000msN/A
Total (Java)500–3000ms600–3000ms1000–5000msN/A

Mitigation strategies:

// Lambda: Provisioned Concurrency (keeps warm instances)
// terraform
resource "aws_lambda_provisioned_concurrency_config" "api" {
  function_name                  = aws_lambda_function.api.function_name
  qualifier                      = aws_lambda_alias.live.name
  provisioned_concurrent_executions = 5  // Keep 5 instances warm
}
# GCF: minimum instances via concurrency
# gcloud functions deploy api \
#   --min-instances=2 \
#   --concurrency=80

Pricing Calculator Scenarios

Scenario 1: Lightweight API (5M req/mo, 100ms, 256 MB)

PlatformMonthly Cost
AWS Lambda~$3
Google Cloud Functions~$2
Azure Functions (Consumption)~$2
Cloudflare Workers$5 (flat)

Scenario 2: High-Traffic API (100M req/mo, 200ms, 512 MB)

PlatformMonthly Cost
AWS Lambda~$185
Google Cloud Functions~$140
Azure Functions (Consumption)~$200
Cloudflare Workers~$32

Scenario 3: Memory-Heavy Processing (1M req/mo, 5 sec, 4 GB)

PlatformMonthly Cost
AWS Lambda~$330
Google Cloud Functions~$200
Azure Functions (Premium)~$400+
Cloudflare WorkersNot applicable (memory limit)

Decision Framework

Choose AWS Lambda when:

  • You’re already on AWS and need deep ecosystem integration (DynamoDB, SQS, EventBridge)
  • You need support for multiple languages including Ruby, Java, custom runtimes
  • You’re running long-running jobs (up to 15 minutes)
  • You need large deployment packages (ML models, binary dependencies)

Choose Google Cloud Functions Gen2 when:

  • You’re on GCP or need BigQuery/Pub/Sub/GCS native triggers
  • You want longer execution times (up to 60 minutes)
  • You need high memory (up to 32 GB) for ML inference workloads
  • You want concurrency-per-instance to reduce cold start costs

Choose Azure Functions when:

  • Your team is .NET-first and you’re in the Azure ecosystem
  • You need Durable Functions for stateful multi-step orchestration
  • You require deep Microsoft service integration (Teams, Office 365, AD)
  • Enterprise compliance requirements favor Microsoft’s compliance portfolio

Choose Cloudflare Workers when:

  • Latency is critical — you need sub-5ms cold starts globally
  • You’re building edge APIs, middleware, or A/B testing logic
  • Your workload is JavaScript/TypeScript-native and fits within memory limits
  • You want simple, predictable pricing without GB-second math
  • You’re building a globally distributed cache or CDN-like layer

When building serverless APIs, these tools accelerate your development:


Summary

If you need…Use…
Deep AWS ecosystem integrationAWS Lambda
Longer execution times + high memoryGoogle Cloud Functions Gen2
Stateful orchestration + .NET/AzureAzure Functions
Sub-5ms cold starts, global edge, simple pricingCloudflare Workers
Multi-cloud with JS/TS workloadsCloudflare Workers

Serverless in 2026 is not a one-size-fits-all solution. Lambda and GCF are workhorses for backend APIs. Azure Functions is the enterprise .NET choice. Cloudflare Workers is redefining what edge computing means for latency-sensitive applications. Understand your latency budget, execution time requirements, and existing cloud investments — then choose accordingly.

Free Download

50 CLI Commands That Save Hours Every Week

The exact Git shortcuts, file search tricks, Docker commands, and productivity hacks that senior developers use daily. Compiled from 5+ years of shipping production code.

  • Git mastery (10 cmds)
  • File & search wizardry
  • Docker & DevOps tricks
  • Process & network tools
  • Productivity boosters
  • macOS & Linux install guide

No spam. 1 email/week max. Unsubscribe anytime.

Found this guide useful? Check out our free developer tools.