Skip to main content

Azure Functions vs AWS Lambda for C#: 2026 Cost & Speed

Azure Functions vs AWS Lambda for C# in 2026: cold starts, Native AOT, pricing math and real code. See the pricing math and pick the right platform.

If you're building serverless apps in .NET, you'll hit the Azure Functions vs AWS Lambda question sooner or later. Both run C# well. Both charge per execution. Both claim to scale to zero. In 2026, though, the details that matter have changed. Azure's in-process model is being retired, the Flex Consumption plan has matured, AWS now bills the Lambda INIT phase, and Native AOT and SnapStart have made .NET cold starts much faster. This guide compares cold starts, throughput, and real pricing math, with runnable C# code for both platforms, so you can pick based on numbers.

Azure Functions vs AWS Lambda: The 2026 Landscape at a Glance

Before looking at benchmarks, here's what each platform offers C# developers today:

  • Azure Functions: The isolated worker model is now the only supported way forward. Support for the in-process model ends on November 10, 2026. Hosting options are Flex Consumption (the recommended serverless plan), the classic Consumption plan, Premium (Elastic Premium), and Dedicated App Service plans. Native AOT isn't supported for the isolated worker, but ReadyToRun is.
  • AWS Lambda: Managed .NET runtimes run on Amazon Linux 2023. Your options include the managed runtime, Native AOT on the provided.al2023 OS-only runtime, SnapStart for .NET, and Graviton (ARM64) processors, which cost about 20% less per GB-second.

Both platforms move quickly, and supported .NET versions (.NET 8 LTS, .NET 10 LTS) change during the year. Check each vendor's supported-runtime page before you commit to a target framework.

Writing the Same Function in C# on Both Platforms

The best way to keep this decision reversible is to put your business logic in a plain class library and keep each cloud's code as a thin adapter. Here's the shared core:

// PriceCore/PriceService.cs  (plain netstandard/net8.0+ class library)
namespace PriceCore;

public record Price(string Sku, decimal Amount, string Currency);

public interface IPriceService
{
    Task<Price?> GetAsync(string sku, CancellationToken ct = default);
}

public sealed class PriceService : IPriceService
{
    private static readonly Dictionary<string, Price> Catalog = new(StringComparer.OrdinalIgnoreCase)
    {
        ["SKU-100"] = new("SKU-100", 19.99m, "USD"),
        ["SKU-200"] = new("SKU-200", 49.00m, "USD")
    };

    public Task<Price?> GetAsync(string sku, CancellationToken ct = default)
        => Task.FromResult(Catalog.TryGetValue(sku, out var p) ? p : null);
}

Azure Functions C# (Isolated Worker, ASP.NET Core Integration)

The isolated worker runs your code in its own .NET process. That lets you pick your own .NET version, use normal middleware, and use standard dependency injection. Using the ASP.NET Core integration package (Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore) gives you the familiar HttpRequest and IActionResult types.

// Program.cs
using Microsoft.Azure.Functions.Worker.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using PriceCore;

var builder = FunctionsApplication.CreateBuilder(args);
builder.ConfigureFunctionsWebApplication();
builder.Services.AddSingleton<IPriceService, PriceService>();
builder.Build().Run();

// GetPrice.cs
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.Functions.Worker;
using PriceCore;

public class GetPrice(IPriceService prices)
{
    [Function("GetPrice")]
    public async Task<IActionResult> Run(
        [HttpTrigger(AuthorizationLevel.Function, "get", Route = "price/{sku}")] HttpRequest req,
        string sku)
    {
        var price = await prices.GetAsync(sku, req.HttpContext.RequestAborted);
        return price is null ? new NotFoundResult() : new OkObjectResult(price);
    }
}

AWS Lambda C# (Lambda Annotations Framework)

The Amazon.Lambda.Annotations source generator removes most of the old API Gateway plumbing. It generates the handler and updates your CloudFormation/SAM template when you build.

using Amazon.Lambda.Annotations;
using Amazon.Lambda.Annotations.APIGateway;
using Amazon.Lambda.Core;
using Microsoft.Extensions.DependencyInjection;
using PriceCore;

[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]

[LambdaStartup]
public class Startup
{
    public void ConfigureServices(IServiceCollection services)
        => services.AddSingleton<IPriceService, PriceService>();
}

public class Functions(IPriceService prices)
{
    [LambdaFunction(MemorySize = 1024, Timeout = 10)]
    [HttpApi(LambdaHttpMethod.Get, "/price/{sku}")]
    public async Task<IHttpResult> GetPrice(string sku, ILambdaContext context)
    {
        var price = await prices.GetAsync(sku);
        return price is null ? HttpResults.NotFound() : HttpResults.Ok(price);
    }
}

Both versions have the same shape: a DI container, a singleton service, and a thin HTTP adapter. Since your domain logic never references Microsoft.Azure.Functions.Worker or Amazon.Lambda.Core, moving between clouds later means rewriting adapters, not the application.

Performance Comparison: Cold Starts and Throughput

Why .NET Cold Starts Happen

A cold start has three parts: provisioning a sandbox or instance, starting the .NET runtime, and running your startup code, where JIT compilation, DI container setup, and SDK client creation dominate. The first part belongs to the platform. The other two are yours to shrink.

Typical Cold Start Ranges (Indicative)

Results vary a lot with package size, memory setting, region, and dependencies. These are the typical ranges teams report for a small HTTP function:

  • Lambda, managed .NET runtime, JIT: roughly 600 ms–1.5 s at 1 GB memory. It gets much worse at 128–256 MB, because CPU scales with memory.
  • Lambda + SnapStart: often under 400 ms, because Lambda restores a snapshot of an already-initialized environment.
  • Lambda + Native AOT: commonly 200–400 ms, with no JIT at all.
  • Azure Functions Flex Consumption (isolated, ReadyToRun): commonly about 1–2 s on a cold instance. With always-ready instances it's effectively zero.
  • Azure Functions classic Consumption: the slowest of these, often 2–5+ s for .NET isolated apps.

Treat these as starting points, not guarantees. Benchmark your own function with production-sized dependencies.

Native AOT on AWS Lambda

Native AOT compiles your function to a self-contained native binary, so nothing gets JIT-compiled at startup. This is currently the strongest advantage Lambda has for C# latency.

<PropertyGroup>
  <OutputType>Exe</OutputType>
  <TargetFramework>net8.0</TargetFramework>
  <PublishAot>true</PublishAot>
  <AssemblyName>bootstrap</AssemblyName>
  <InvariantGlobalization>true</InvariantGlobalization>
  <StripSymbols>true</StripSymbols>
</PropertyGroup>

The catch is that AOT needs trim-safe code. Reflection-heavy libraries, some older AWS SDK patterns, and reflection-based System.Text.Json will break or emit warnings. Use a source-generated JSON context:

using System.Text.Json.Serialization;
using PriceCore;

[JsonSerializable(typeof(Price))]
[JsonSerializable(typeof(Amazon.Lambda.APIGatewayEvents.APIGatewayHttpApiV2ProxyRequest))]
[JsonSerializable(typeof(Amazon.Lambda.APIGatewayEvents.APIGatewayHttpApiV2ProxyResponse))]
public partial class LambdaJsonContext : JsonSerializerContext { }

Treat every AOT/trim warning as an error. If you ignore one, you'll get a runtime failure that only shows up in production.

ReadyToRun on Azure Functions

Azure's isolated worker doesn't support Native AOT, but ReadyToRun precompiles most of your IL and cuts JIT time noticeably:

<PropertyGroup>
  <PublishReadyToRun>true</PublishReadyToRun>
  <RuntimeIdentifier>linux-x64</RuntimeIdentifier>
</PropertyGroup>

Throughput and Concurrency: The Hidden Difference

This is the most misunderstood part of the comparison. A standard Lambda execution environment handles one request at a time, so 100 concurrent requests means 100 environments, and each one bills separately. Azure Functions instances handle many executions at once. On Flex Consumption you set per-instance concurrency, for example 16 concurrent HTTP requests on a 2 GB instance. For I/O-bound C# code that spends most of its time awaiting databases or HTTP calls, that concurrency means fewer instances, fewer cold starts, and, as the next section shows, a smaller bill.

Cost Comparison: AWS Lambda Pricing vs Azure Functions Pricing

The Published Rates (Pay-as-you-go, US Regions)

  • AWS Lambda (x86): $0.20 per 1M requests plus $0.0000166667 per GB-second, billed in 1 ms increments. ARM64/Graviton: $0.0000133334 per GB-second. Free tier: 1M requests and 400,000 GB-s per month.
  • Azure Functions Consumption: $0.20 per 1M executions plus $0.000016 per GB-second. Free grant: 1M executions and 400,000 GB-s. Memory rounds up to the nearest 128 MB, with a 100 ms minimum per execution.
  • Azure Functions Flex Consumption (on-demand): about $0.40 per 1M executions plus about $0.000026 per GB-second. Free grant: 250,000 executions and 100,000 GB-s. It bills instance memory for the time the instance is active, not per execution, so concurrent executions share the cost.

Prices change and vary by region, so check both official pricing pages before you publish a budget.

Run the Numbers Yourself: A C# Serverless Cost Calculator

This console app compares a realistic workload: 10 million requests a month, 512 MB, 200 ms average duration. Paste it into dotnet new console and run it.

const long Requests = 10_000_000;
const decimal MemoryGb = 0.5m;
const decimal AvgSeconds = 0.200m;
const decimal FlexEffectiveConcurrency = 4m; // avg executions sharing one Flex instance

decimal gbSeconds = Requests * AvgSeconds * MemoryGb;

decimal Cost(decimal gbs, decimal freeGbs, decimal gbsRate,
             long reqs, long freeReqs, decimal perMillion)
{
    var compute = Math.Max(0, gbs - freeGbs) * gbsRate;
    var requests = Math.Max(0, reqs - freeReqs) / 1_000_000m * perMillion;
    return Math.Round(compute + requests, 2);
}

var results = new (string Name, decimal Monthly)[]
{
    ("AWS Lambda x86",               Cost(gbSeconds, 400_000, 0.0000166667m, Requests, 1_000_000, 0.20m)),
    ("AWS Lambda ARM64 (Graviton)",  Cost(gbSeconds, 400_000, 0.0000133334m, Requests, 1_000_000, 0.20m)),
    ("Azure Consumption",            Cost(gbSeconds, 400_000, 0.000016m,     Requests, 1_000_000, 0.20m)),
    ("Azure Flex (no concurrency)",  Cost(gbSeconds, 100_000, 0.000026m,     Requests, 250_000,   0.40m)),
    ("Azure Flex (concurrency x4)",  Cost(gbSeconds / FlexEffectiveConcurrency, 100_000, 0.000026m, Requests, 250_000, 0.40m)),
};

Console.WriteLine($"Workload: {Requests:N0} req, {MemoryGb} GB, {AvgSeconds * 1000} ms => {gbSeconds:N0} GB-s\n");
foreach (var (name, monthly) in results.OrderBy(r => r.Monthly))
    Console.WriteLine($"{name,-30} ${monthly,8:N2}/month");

The approximate output:

  • Azure Flex (concurrency ×4): ~$7.80
  • AWS Lambda ARM64: ~$9.80
  • Azure Consumption: ~$11.40
  • AWS Lambda x86: ~$11.80
  • Azure Flex (no concurrency): ~$27.30

This is why "which is cheaper?" has no single answer. For CPU-bound, one-request-at-a-time work, Lambda on ARM64 wins. For I/O-bound APIs where one instance can juggle many awaits, Flex Consumption's per-instance billing can beat it.

Costs People Forget

  • API front door: Lambda behind API Gateway HTTP APIs adds about $1.00 per million requests (REST APIs about $3.50). At 10M requests that can cost more than the compute. Lambda Function URLs are free but have fewer features. Azure Functions includes HTTP triggers, and API Management is optional.
  • Lambda INIT billing: Since August 2025, AWS bills the initialization phase for on-demand managed-runtime functions. Slow cold starts now cost money as well as latency, which is one more reason to use Native AOT or SnapStart.
  • Warm capacity: Lambda Provisioned Concurrency, Flex always-ready instances, and Azure Premium all charge a baseline even when idle. Azure Premium has an always-on minimum that often runs well over $100 a month.
  • Logging: CloudWatch Logs and Application Insights ingestion can cost more than the function itself. Set sampling and log levels on purpose.
  • Storage and networking: Azure Functions needs a storage account. Both platforms charge for egress and for VNet/NAT gateway setups.

Best Practices for Serverless .NET on Either Cloud

  • Reuse clients. Register HttpClient (through IHttpClientFactory), CosmosClient, DynamoDBContext, and SDK clients as singletons. Creating them per invocation wastes CPU and can exhaust sockets.
  • Right-size Lambda memory. CPU scales linearly with memory, and one full vCPU arrives at about 1,769 MB. A .NET function at 1,024 MB often finishes more than twice as fast as at 512 MB, so it can cost the same or less. Use AWS Lambda Power Tuning to find the sweet spot.
  • Keep startup lean. Avoid scanning assemblies, loading big configuration files, or calling remote services in Program.cs or constructors.
  • Use ARM64 on Lambda unless a native dependency blocks it. You get about 20% lower cost, often with equal or better performance.
  • Tune Flex concurrency for I/O-bound functions, and lower it for CPU-heavy ones so requests don't compete for cores.
  • Make handlers idempotent. Both platforms retry queue and event triggers, so assume at-least-once delivery.
  • Always pass CancellationTokens so timeouts stop downstream work cleanly.

Common Pitfalls to Avoid

  • Staying on the Azure in-process model. Support ends November 10, 2026. Migrate to the isolated worker now, because trigger bindings and DI registration differ.
  • Ignoring timeouts. Lambda's maximum is 15 minutes. Azure Consumption allows up to 10 minutes. Flex allows longer runs, but HTTP requests are still capped at about 230 seconds by the front-end load balancer. For long work, use Durable Functions or Step Functions.
  • Turning on Native AOT without testing. Trimmed code can fail at runtime in reflection paths that tests never touched. Run integration tests against the published AOT binary, not the Debug build.
  • SnapStart uniqueness bugs. Anything generated during init, such as random seeds, GUIDs, or cached timestamps, is shared by every restored environment. Regenerate these in runtime hooks.
  • Choosing classic Consumption for new Linux apps. Microsoft steers new serverless workloads to Flex Consumption, so start there.
  • Comparing list prices only. Model concurrency, gateways, logging, and warm capacity, or your estimate could be off by several times.

Which Should You Choose in 2026?

Choose AWS Lambda if…

  • You need the lowest possible cold start for C#. Native AOT and SnapStart are unmatched here.
  • Your workloads are CPU-bound or event-driven (S3, SQS, Kinesis, EventBridge) and you're already on AWS.
  • You want fine-grained memory sizing from 128 MB to 10 GB with 1 ms billing.

Choose Azure Functions if…

  • Your team works in the Microsoft stack: Entra ID, SQL Server, Cosmos DB, Service Bus, Visual Studio.
  • Your APIs are I/O-bound, where per-instance concurrency on Flex lowers cost and cold starts.
  • You want built-in stateful orchestration with Durable Functions, written in plain C#.
  • You want HTTP endpoints without paying for a separate API gateway.

Conclusion: Azure Functions vs AWS Lambda Key Takeaways

For C# developers, neither platform clearly beats the other in the Azure Functions vs AWS Lambda choice for 2026. It depends on your workload. Lambda leads on raw cold-start speed thanks to Native AOT and SnapStart, and on price for CPU-bound work on Graviton. Azure Functions, especially Flex Consumption, often wins for concurrent I/O-bound APIs and for teams already on Azure.

  • Performance: Lambda + Native AOT is the fastest .NET cold start. On Azure, use ReadyToRun plus always-ready instances.
  • Cost: Lambda bills per request-environment. Flex bills per instance, and concurrency changes the math.
  • Hidden costs: API Gateway, INIT billing, logging, and warm capacity often matter more than GB-second rates.
  • Deadline: Move Azure in-process functions to the isolated worker before November 10, 2026.
  • Portability: Keep business logic in a cloud-agnostic class library so switching clouds is cheap.

Run the cost calculator above with your own traffic numbers, deploy the same function to both clouds, and measure. An afternoon of benchmarking will tell you more than any vendor pricing page.

About csharp-coder.com
Your go-to resource for C#, .NET, and modern software development. Follow along for daily tutorials, tips, and real-world examples.

Comments

Popular posts from this blog

.NET MAUI Tutorial 2026: Build Cross-Platform Apps in C#

Learn .NET MAUI in 2026 to build iOS, Android, Windows & Mac apps from one C# codebase. Start this cross-platform tutorial with code examples today. .NET MAUI (Multi-platform App UI) is Microsoft's framework for building native iOS, Android, Windows, and macOS apps from a single C# codebase . If you've ever wanted to ship a mobile app without learning Swift, Kotlin, and Win32 separately, this .NET MAUI tutorial for 2026 is your starting point. In this guide you'll learn what .NET MAUI is, why it matters for cross-platform app development in C#, and how to build your first working app — with runnable code examples and the best practices senior engineers actually use in production. What Is .NET MAUI and Why Use It in 2026? .NET MAUI is the evolution of Xamarin.Forms, fully integrated into the modern .NET runtime. With one project and one language — C# — you target four platforms. The framework compiles to native UI controls on each device, so a button on iOS...

Angular 14 : 404 error during refresh page after deployment

In this article, We will learn how to solve 404 file or directory not found angular error in production.  Refresh browser angular 404 file or directory not found error You have built an Angular app and created a production build with ng build --prod You deploy it to a production server. Everything works fine until you refresh the page. The app throws The requested URL was not found on this server message (Status code 404 not found). It appears that angular routing not working on the production server when you refresh the page. The error appears on the following scenarios When you type the URL directly in the address bar. When you refresh the page The error appears on all the pages except the root page.   Reason for the requested URL was not found on this server error In a Multi-page web application, every time the application needs to display a page it has to send a request to the web server. You can do that by either typing the URL in the address bar, clicking on the Me...

Angular 14 CRUD Operation with Web API .Net 6.0

How to Perform CRUD Operation Using Angular 14 In this article, we will learn the angular crud (create, read, update, delete) tutorial with ASP.NET Core 6 web API. We will use the SQL Server database and responsive user interface for our Web app, we will use the Bootstrap 5. Let's start step by step. Step 1 - Create Database and Web API First we need to create Employee database in SQL Server and web API to communicate with database. so you can use my previous article CRUD operations in web API using net 6.0 to create web API step by step. As you can see, after creating all the required API and database, our API creation part is completed. Now we have to do the angular part like installing angular CLI, creating angular 14 project, command for building and running angular application...etc. Step 2 - Install Angular CLI Now we have to install angular CLI into our system. If you have already installed angular CLI into your system then skip this step.  To install angular CLI ope...