The most counterintuitive truth about estimating Azure Functions costs is that "serverless" doesn’t mean "free" — it means you pay for actual usage, which can be harder to predict than fixed infrastructure costs.
Let’s watch a simple function execute and see how the meters tick.
Imagine a C# function triggered by an HTTP request.
// Function App: MyFunctionApp
// Function Name: HttpTriggerCSharp
// Runtime: .NET 6
// Trigger: HTTP
// Language: C#
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
namespace MyNamespace
{
public static class HttpTriggerCSharp
{
[FunctionName("HttpTriggerCSharp")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = JsonConvert.DeserializeObject(requestBody);
string name = data?.name;
string responseMessage = string.IsNullOrEmpty(name)
? "This HTTP triggered function executed successfully. Pass a name in the request body for a personalized response."
: $"Hello, {name}! This HTTP triggered function executed successfully.";
return new OkObjectResult(responseMessage);
}
}
}
When this function runs, Azure tracks a few key metrics:
- Execution Count: Every time the function code is invoked, that’s one execution.
- Execution Time (GB-seconds): This is the core billing unit for consumption plans. It’s calculated by taking the memory allocated to the function instance and multiplying it by the duration it was running. If your function uses 512 MB of RAM and runs for 2 seconds, that’s 512 MB * 2 s = 1024 MB-seconds, or 1 GB-second.
- Bandwidth: Data transferred in and out of Azure.
The consumption plan is where the "pay-as-you-go" magic (and complexity) happens. You get a generous free grant each month: 1 million free executions and 400,000 GB-seconds of execution time.
Let’s say your HttpTriggerCSharp function is called 10 million times a month, and each execution averages 500 ms (0.5 seconds) with 256 MB of RAM allocated.
- Executions: 10,000,000 total - 1,000,000 free = 9,000,000 billable executions.
- GB-seconds:
- Each execution uses 256 MB * 0.5 s = 128 MB-seconds.
- Total MB-seconds: 10,000,000 executions * 128 MB-seconds/execution = 1,280,000,000 MB-seconds.
- Total GB-seconds: 1,280,000,000 MB-seconds / 1024 MB/GB = 1,250,000 GB-seconds.
- Billable GB-seconds: 1,250,000 GB-seconds - 400,000 free = 850,000 GB-seconds.
At the current (as of late 2023) consumption plan rates for East US:
- Executions: $0.40 per million executions.
- GB-seconds: $0.000016 per GB-second.
Your estimated monthly cost would be:
- Executions: 9 million * ($0.40 / 1,000,000) = $3.60
- GB-seconds: 850,000 * $0.000016 = $13.60
- Total: $17.20
This doesn’t include data transfer, which is usually negligible for simple HTTP APIs unless you’re transferring large payloads.
The key levers you control are:
- Memory Allocation: In the Azure portal (under the function’s "Configuration" settings), you can set the maximum memory your function can use. Lowering this saves money but can impact performance or cause out-of-memory errors if set too low for your workload.
- Execution Duration: Optimizing your function code (e.g., efficient algorithms, avoiding blocking I/O, using asynchronous patterns correctly) directly reduces the duration and thus the GB-seconds.
- Triggering Mechanism: Event-driven triggers (like Service Bus or Blob Storage) often have different cost structures or behaviors than HTTP triggers.
- Language Runtime: Different runtimes have varying startup times and memory footprints, which can subtly affect GB-second costs.
To estimate before deploying, you can use the Azure Functions Estimator tool, but more importantly, you need to understand your expected traffic patterns and the resource consumption of your specific code. A small proof-of-concept run locally or on a test environment, measuring actual execution time and memory usage (e.g., using tools like Application Insights or even simple Console.WriteLine statements for duration), is invaluable. You can then extrapolate based on your anticipated invocation count.
The most important factor for cost prediction is not just the number of executions, but the average duration and memory usage per execution. A function that runs 100 ms using 128 MB will be significantly cheaper than one that runs 5 seconds using 1024 MB, even if they are called the same number of times.
The next thing you’ll likely run into is understanding how cold starts impact your perceived latency and potentially your GB-second costs if they lead to longer execution times for the first invocation after a period of inactivity.