Azure Functions

A senior-engineer revision guide: serverless architecture, internals, hosting-plan trade-offs, and running Functions on Kubernetes/AKS with KEDA.

Flex Consumption = recommended serverless default Runtime v4 (v1.x EOL Sep 2026) Updated Jul 2026

Executive Summary

Azure Functions is Azure's Functions-as-a-Service (FaaS) platform: you write a small unit of code bound to a trigger, and the platform handles process lifecycle, scaling, and (on serverless plans) billing per execution. The same Functions runtime and programming model can run in five very different places — Consumption, Flex Consumption, Premium, Dedicated (App Service Plan), or as a plain container on Kubernetes/AKS scaled by KEDA — and picking the wrong one is the single most consequential (and most common) mistake teams make.

Why this matters The "serverless vs Kubernetes" debate is a false dichotomy for Functions: the runtime is portable. The real decision is about who operates the scaling substrate — Azure (Consumption/Flex/Premium), your App Service plan (Dedicated), or your own cluster (AKS + KEDA). Interviewers and architecture reviews probe exactly this distinction.

Functions core

Event-driven execution: a trigger (HTTP, queue, timer, blob, Event Hub...) invokes your function; bindings declaratively wire inputs/outputs without SDK boilerplate.

Scale controller

On Azure-hosted plans, a scale controller (not KEDA) monitors trigger sources and adds/removes host instances. On Kubernetes, that same signal is exposed through KEDA to the standard Horizontal Pod Autoscaler.

2026 default

Flex Consumption is almost always the right pick over classic Consumption for new projects — same idle cost, greater scale, VNet support, and always-ready instances for latency-sensitive paths.

Core Concepts

Trigger, binding, function app

Azure Functions uses triggers to start function execution and bindings to connect code to other services like storage, queues, and databases, without hand-written SDK plumbing for the common case.

ConceptPurposeKey trait
TriggerDefines what invokes the function (exactly one per function)HTTP, Timer, Queue, Blob, Event Hub, Service Bus, Cosmos DB change feed, Durable orchestration/entity/activity
Input/Output bindingDeclarative data in/out without manual SDK callsZero or more per function; e.g. write a Cosmos DB doc as an output binding
Function appDeployment/scale unit — a collection of functions sharing a process and configurationAll functions in an app scale together (per Consumption); Flex scales more granularly by trigger type
host.jsonApp-wide runtime configuration (timeouts, extension bundle version, concurrency)Shared across every function in the app
Durable FunctionsStateful orchestration on top of stateless functionsOrchestrator, Activity, and Entity triggers; orchestrator code must be deterministic

Programming models

The v2 programming model uses a decorator-based approach to define triggers and bindings directly in code, replacing the v1 model's separate function.json metadata file per function. This is now the recommended model across Python, Node.js (v4), and other supported languages.

Flash: Why can't Durable Functions orchestrator code call an HTTP API directly?
Orchestrator functions replay their history on every reactivation (the "event sourcing" execution model), so any non-deterministic operation (I/O, random numbers, current time) must be pushed into an Activity function — otherwise a replay produces different results.

Architecture Overview

Every hosting option is built from the same two logical layers: the Functions host/runtime (your code + the language worker + the extension/binding framework) and a scaling substrate that decides how many instances of that host should run.

Functions Host (portable — same everywhere) Language worker .NET / Node / Python / Java... Trigger listeners poll/subscribe event sources Binding framework input/output wiring Your function code business logic runs inside, scaled by ↓ (pick one substrate) Consumption / Flex Consumption Azure scale controller watches trigger source rate directly Scale to zero: yes Billing: per-execution + GB-seconds Fully managed, no cluster to run Premium plan Same scale controller logic + pre-warmed ("always ready") instances Scale to zero: no Billing: vCPU/memory per hour No cold starts, full VNet Dedicated (App Service Plan) App Service autoscale rules (scheduled/metric), slower than event-driven Scale to zero: no Billing: fixed VM cost Reuses spare capacity on existing App Service Kubernetes / AKS + KEDA KEDA exposes custom metrics to the standard Kubernetes HPA Scale to zero: yes Billing: your cluster's compute, not per-exec You operate the scaling substrate

Fig 1. One portable Functions host, four different scaling substrates.

Internal Working

How event-driven scaling actually decides "add an instance"

When a function is deployed, it runs inside the Functions runtime host process, which pulls events from the source defined by the trigger and passes them to your function code. One instance of the runtime is rarely adequate: it's wasteful if you get one message a day, and insufficient if you get thousands of events per second — so a separate autoscaling mechanism monitors the trigger source and adjusts instance count.

  • On Azure-hosted plans: the built-in scale controller polls the trigger source directly (e.g., Storage Queue length, Event Hub partition lag) roughly every few seconds and issues scale decisions independent of Kubernetes/HPA — there's no cluster involved at all.
  • On Kubernetes: KEDA exposes custom metrics for the Kubernetes Horizontal Pod Autoscaler; by default HPA can only use CPU/memory, which is limiting when you want to scale on e.g. requests/second or queue depth — KEDA's metrics adapter fills that gap.

Cold start mechanics

A cold start = provisioning a brand-new host instance: pull the runtime/worker image (or spin up a new App Service worker), load your app's dependencies, run any startup code, then invoke the trigger. Pre-warmed ("always ready") instances on Premium/Flex sidestep this by keeping idle-but-ready hosts around.

Common confusion "Serverless" does not mean "no scaling infrastructure" — it means Azure operates it for you. On AKS + KEDA, you get the same event-driven, scale-to-zero behavior, but you are the one who owns node capacity, cluster upgrades, and the metrics adapter's health.

Key Components

Functions runtime host

Cross-platform process (also ships as a Docker image) that loads your function app, dispatches triggers, and manages bindings.

Language worker

Out-of-process worker per language (Node, Python, Java, PowerShell, custom handlers) that the host communicates with over gRPC.

Extension bundle

Versioned bundle of binding extensions (Storage, Service Bus, Cosmos DB, Durable Task, etc.) referenced in host.json.

Scale controller (Azure-hosted)

Azure-managed component monitoring trigger sources to add/remove instances on Consumption/Flex/Premium plans.

KEDA operator (Kubernetes)

Allows workloads to scale from 0 to N with support for Deployments, Jobs, StatefulSets, or any custom resource defining a /scale subresource.

KEDA metrics server

Exposes external metrics to the Kubernetes HPA for autoscaling purposes, such as messages in a queue or events in an Event Hub.

Durable Task extension

Adds orchestration/entity/activity triggers and the durable store (Storage/Netherite/SQL/Durable Task Scheduler) backing orchestration state.

Azure Functions Core Tools

Local CLI for scaffolding, running, and — for Kubernetes — generating deployment manifests (func kubernetes commands).

Request / Execution Flow

Trace of a queue-triggered function scaling out under load, contrasting the Azure-hosted path with the Kubernetes/KEDA path:

Path A — Azure-hosted (Consumption/Flex/Premium) Queue Scale Controller ARM / plan Host instance 1. Messages accumulate 2. Controller polls queue length, decides "need +N instances" 3. Plan allocates new worker instance(s) 4. New instance's queue trigger listener dequeues and invokes function No Kubernetes, no HPA — this loop is entirely internal to the Azure platform. Path B — Kubernetes / AKS + KEDA Queue KEDA Scaler Metrics Adapter HPA Pod (Functions host) 5. KEDA scaler polls the same queue on a schedule 6. Publishes queue length as a custom metric 7. HPA reads the external metric via the metrics API 8. HPA scales the Deployment/ScaledObject replica count (0→N) 9. New pod's Functions host dequeues and invokes function — identical code path to Path A Scheduling of the new pod still goes through the normal Kubernetes scheduler + node autoscaler.

Fig 2. Same function code, two different scaling substrates for the same queue trigger.

Hosting Plans Compared

PlanScale to zeroCold startsVNetBillingMax instances
Consumption legacyYesYes, alwaysNoPer-execution + GB-sPer-function-app cap
Flex Consumption recommendedYesReduced (supports always-ready instances)Yes — virtual network integration is a core featurePer-execution, rounded per 100msUp to 1,000 (default limit 100, configurable)
PremiumNoNone — no cold starts, essential for production APIs and latency-sensitive workloadsYes, in/outboundvCPU/memory per hourUp to 100 in some regions (Linux)
Dedicated (App Service Plan)NoNo (always-on)YesFixed VM cost, shared with other App Service appsManual or App Service autoscale
Kubernetes / AKS + KEDAYesDepends on image pull/pod start timeNative (your cluster's VNet)Your cluster's compute costContainer Apps default 10, configurable up to 1,000; AKS bounded by cluster capacity
Retirement notice Function apps still on the end-of-life v3 runtime on Linux Consumption stop running after September 30, 2026, and the Linux Consumption plan itself is retiring on 30 September 2028 with no new features or language versions in the meantime — migrate to Flex Consumption.

Azure Functions + KEDA + AKS

Every Azure Functions trigger implementation has a matching KEDA scaler, which is what makes it possible to run the Functions runtime in a Docker container with event-driven scaling through KEDA on any Kubernetes cluster, including AKS — replicating serverless scale-to-zero/scale-to-N behavior outside Azure's managed platform entirely.

Why you'd choose this over a managed plan

Reasons to run Functions on AKS

  • You already operate an AKS cluster and want one platform (RBAC, observability, networking) for both microservices and event-driven functions.
  • You need scalers KEDA supports that aren't native Azure Functions triggers (Kafka, RabbitMQ, Prometheus, Postgres, etc.).
  • Data residency/network isolation requires functions to run entirely inside your own cluster's VNet without any Azure-managed compute layer.
  • You want a single autoscaling model (HPA) across both regular Deployments and Functions-based workloads.

Trade-offs you accept

  • You now own node capacity, cluster upgrades, and the KEDA install itself — none of that is "serverless" anymore.
  • HTTP-triggered autoscaling on Kubernetes is trickier than event-source triggers — default resource-based HPA doesn't give real scale-to-zero for HTTP, so an HTTP-aware KEDA scaler (e.g., Kedify's) or KEDA's own HTTP add-on is required.
  • No Azure billing simplicity — you pay for cluster compute whether or not functions are actively invoked (aside from scaling nodes down).

How the pieces fit together on AKS

AKS Cluster KEDA add-on operator + metrics server (built-in on AKS Automatic, add-on on AKS Standard) ScaledObject defines trigger (queue/Kafka/ Event Hub/HTTP) + min/max replicas, target CR = Deployment Kubernetes HPA (native) reads external metric published by KEDA's metrics adapter, scales the target 0→N Deployment: Functions runtime container image your function code + host, same programming model as Azure-hosted plans Entra Workload ID recommended for auth to Azure services (Storage, Service Bus, etc.)

Fig 3. KEDA sits between the event source and the standard Kubernetes HPA; the Functions container is just a Deployment.

AKS-native KEDA integration

AKS ships a managed KEDA add-on supporting ScaledObjects (Deployments, StatefulSets, or any custom resource with a /scale subresource) and ScaledJobs for job-like workloads, with production-grade security by decoupling autoscaling authentication from workloads, support for bring-your-own external scalers, and integration with Microsoft Entra Workload ID. On AKS Automatic, KEDA is preconfigured by default; on AKS Standard it's an opt-in add-on, and if you plan to use workload identity you must enable that before enabling KEDA.

Building and deploying a Functions container to AKS

# 1. Scaffold and containerize a function app locally
func init OrdersProcessor --docker
cd OrdersProcessor
func new --name ProcessOrder --template "Azure Queue Storage trigger"

# 2. Build and push the image
docker build -t myacr.azurecr.io/orders-processor:1.0 .
az acr login --name myacr
docker push myacr.azurecr.io/orders-processor:1.0

# 3. Generate Kubernetes manifests (Deployment + KEDA ScaledObject) from the func app
func kubernetes deploy \
  --name orders-processor \
  --image-name myacr.azurecr.io/orders-processor:1.0 \
  --namespace orders

Explanation: func kubernetes deploy generates the Deployment, Secret (for trigger connection strings), and a KEDA ScaledObject wired to the function's trigger — the same artifact you could hand-author for full GitOps control.

Example: hand-authored ScaledObject for a queue-triggered function

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: orders-processor-scaledobject
  namespace: orders
spec:
  scaleTargetRef:
    name: orders-processor
  minReplicaCount: 0
  maxReplicaCount: 20
  cooldownPeriod: 60
  triggers:
    - type: azure-queue
      metadata:
        queueName: orders-incoming
        queueLength: "5"
      authenticationRef:
        name: azure-queue-auth

Explanation: minReplicaCount: 0 is what gives true scale-to-zero; queueLength: "5" tells KEDA to add a replica for roughly every 5 queued messages, mirroring the Consumption plan's own queue-scaling heuristic. authenticationRef points at a TriggerAuthentication object — ideally backed by Entra Workload ID rather than a static connection string.

Decision guide
  • Default: Flex Consumption — for nearly all new event-driven serverless workloads.
  • Need zero cold starts + full VNet, still don't want to run a cluster: Premium plan.
  • Already run AKS, want one platform for services + functions, or need a non-Azure-native KEDA scaler: Functions-on-Kubernetes with the AKS KEDA add-on.
  • Want managed KEDA scaling without owning a Kubernetes cluster at all: consider Azure Container Apps, which uses a managed KEDA component under the hood to power container autoscaling including scale to zero.

Triggers, Bindings & APIs

Trigger typeFires onKEDA scaler available?
HTTPInbound HTTP requestYes (HTTP add-on / third-party HTTP scalers)
TimerCRON scheduleYes (cron scaler)
Storage QueueNew message in an Azure Storage queueYes (azure-queue)
Service Bus Queue/TopicNew message / subscription messageYes (azure-servicebus)
Event HubNew events on a partitionYes (azure-eventhub)
Blob StorageNew/updated blob (Event Grid-based)Indirect, via Event Grid/queue scaler
Cosmos DBChange feed activityYes (azure-cosmos-db)
Durable orchestration/entity/activityInternal Durable Task Framework eventsNot applicable — internal to the Durable Task extension

Trigger types requiring runtime-driven triggers are supported only in Kubernetes, Azure IoT Edge, and other self-hosted modes — not on the Consumption plan, which is one reason some workloads deliberately choose Kubernetes hosting.

Command Cheat Sheets

Azure Functions Core Tools (func)

CommandWhat it does
func init MyApp --dockerScaffold a new function app with a Dockerfile
func new --template "Timer trigger"Add a new function to the app
func startRun the app locally with the Functions host
func azure functionapp publish NAMEDeploy to an Azure-hosted plan
func kubernetes deploy --name NAME --image-name IMGGenerate/apply Deployment + KEDA ScaledObject manifests
func kubernetes install --namespace NSInstall the KEDA component into a cluster

az CLI — function app lifecycle

CommandWhat it does
az functionapp create --flexconsumption-location eastus ...Create a Flex Consumption function app
az functionapp create --consumption-plan-location eastus ...Create a legacy Consumption function app
az functionapp plan create --sku EP1Create a Premium plan
az functionapp deployment source config-zip --src app.zipZip-deploy code to an existing app
az functionapp config appsettings set --settings KEY=VALUESet app configuration/connection strings
az functionapp log tailStream live logs

kubectl / helm — Functions-on-Kubernetes operations

CommandWhat it does
helm repo add kedacore https://kedacore.github.io/chartsAdd the KEDA Helm chart repo
helm install keda kedacore/keda --namespace keda --create-namespaceInstall KEDA on a self-managed cluster (unnecessary on AKS with the add-on)
az aks addon enable -a keda -g RG -n NAMEEnable the managed KEDA add-on on AKS Standard
kubectl get scaledobjects -AList all KEDA ScaledObjects and their current state
kubectl describe scaledobject NAMEInspect scaler status/events for a specific object
kubectl get hpaConfirm KEDA's generated HPA and current replica count

Configuration Examples

host.json — extension bundle and concurrency

{
  "version": "2.0",
  "extensionBundle": {
    "id": "Microsoft.Azure.Functions.ExtensionBundle",
    "version": "[4.*, 5.0.0)"
  },
  "extensions": {
    "queues": {
      "maxPollingInterval": "00:00:02",
      "batchSize": 16
    }
  }
}

Python v2 — HTTP + Service Bus trigger in one app

import azure.functions as func
import json

app = func.FunctionApp(http_auth_level=func.AuthLevel.FUNCTION)

@app.route(route="embed", methods=["POST"])
def embed(req: func.HttpRequest) -> func.HttpResponse:
    body = req.get_json()
    # ... call model, build response ...
    return func.HttpResponse(json.dumps({"ok": True}), mimetype="application/json")

@app.service_bus_queue_trigger(
    arg_name="msg", queue_name="orders-incoming", connection="ServiceBusConnection"
)
def process_order(msg: func.ServiceBusMessage):
    payload = json.loads(msg.get_body().decode())
    # ... process payload ...

Explanation: the runtime opens the connection, holds the message lock, calls the function, and completes/abandons the message automatically based on disposition — no manual receive/complete boilerplate is required.

Dockerfile for a Functions app targeting Kubernetes

FROM mcr.microsoft.com/azure-functions/python:4-python3.11
ENV AzureWebJobsScriptRoot=/home/site/wwwroot \
    AzureFunctionsJobHost__Logging__Console__IsEnabled=true
COPY . /home/site/wwwroot
RUN pip install -r /home/site/wwwroot/requirements.txt

TriggerAuthentication backed by Entra Workload Identity

apiVersion: keda.sh/v1alpha1
kind: TriggerAuthentication
metadata:
  name: azure-queue-auth
  namespace: orders
spec:
  podIdentity:
    provider: azure-workload
    identityId: "<managed-identity-client-id>"

Explanation: this avoids putting a Storage/Service Bus connection string in a Kubernetes Secret — KEDA authenticates to Azure using the same Workload Identity federation pattern used for application pods.

Best Practices

  • Default new serverless workloads to Flex Consumption; reserve Premium for genuinely latency-sensitive, always-warm requirements.
  • Keep Durable Functions orchestrator code deterministic — push all I/O, randomness, and wall-clock reads into Activity functions.
  • Set a realistic functionTimeout in host.json rather than relying on plan defaults, especially on Consumption where the ceiling is short.
  • On Kubernetes, size minReplicaCount/maxReplicaCount per ScaledObject deliberately — an unbounded maxReplicaCount can starve cluster capacity from other workloads during a burst.
  • Use Entra Workload Identity (via KEDA's TriggerAuthentication with podIdentity.provider: azure-workload) instead of static connection strings for both the function's own bindings and KEDA's scaler authentication.
  • For HTTP-triggered functions on Kubernetes, don't rely on default resource-based HPA for scale-to-zero — use an HTTP-aware KEDA scaler.
  • Version and pin your extension bundle range in host.json; an unpinned range can silently pull breaking binding changes.
  • Load-test with realistic trigger burst patterns before committing to a plan — Consumption's per-function-app scale cap is a common surprise under multi-function fan-out.

Common Mistakes

Mistake Choosing Premium "just in case" for a low-traffic webhook, paying for always-on compute that Flex Consumption's always-ready instances would have handled far more cheaply.
Mistake Performing non-deterministic operations (HTTP calls, DateTime.Now, random numbers) directly inside a Durable Functions orchestrator instead of an Activity function.
Mistake Assuming Kubernetes-hosted Functions get "free" scale-to-zero for HTTP triggers the same way Consumption/Flex do — plain HPA cannot scale to zero and needs a KEDA HTTP scaler.
Mistake Leaving apps on the end-of-life v3 runtime on Linux Consumption past the retirement date instead of migrating proactively.
Mistake Embedding Storage/Service Bus connection strings as plaintext Kubernetes Secrets for KEDA's TriggerAuthentication instead of using Workload Identity federation.
Mistake Not setting a cooldownPeriod on a ScaledObject, causing thrashing (rapid scale-to-zero-and-back) under bursty, intermittent traffic.

Performance Considerations

  • Cold starts: most pronounced on classic Consumption; Flex Consumption and Premium mitigate via always-ready/pre-warmed instances; on Kubernetes, cold start = pod scheduling + image pull + container start, so image size and node capacity headroom matter directly.
  • Trigger scaling granularity: Flex Consumption scales HTTP/SignalR, Durable, and blob (Event Grid) triggers as separate groups, while everything else scales independently per function — this avoids one noisy timer trigger starving HTTP capacity, a real problem on classic Consumption.
  • KEDA polling interval: the default polling interval for most scalers is ~30 seconds — tune pollingInterval down for latency-sensitive scale-out, understanding the added load on the metrics source.
  • Node capacity on AKS: KEDA can decide to scale a Deployment out, but if no node has room, the pod sits Pending until the cluster's node autoprovisioner (Karpenter) reacts — plan for that extra latency versus Azure-hosted plans' effectively instant compute allocation.
  • Durable Functions orchestrator replay: long-running orchestrations with large history logs replay slower over time; use sub-orchestrations or continue-as-new to bound history size.

Security Considerations

  • Prefer Microsoft Entra ID-based authentication (managed identity / Workload Identity) over connection-string-based bindings wherever the extension supports it.
  • Set the appropriate authLevel (Function/Admin) on HTTP triggers, and rotate function keys regularly if not using Entra auth for HTTP endpoints.
  • Restrict inbound access via VNet integration and private endpoints on Flex Consumption/Premium/Dedicated; on AKS, use NetworkPolicy to scope which namespaces can reach the Functions pods.
  • On Kubernetes, never store event-source credentials as plain Kubernetes Secrets when a Workload Identity-based TriggerAuthentication is available.
  • Keep extension bundles and the Functions runtime major version current — the v1.x runtime is end-of-life and no longer receives security fixes.
  • Apply least-privilege RBAC/Azure role assignments to the managed identity backing both the function app and any KEDA scaler authentication.

Troubleshooting Guide

Function never fires despite messages in the queue

Diagnose: confirm the trigger's connection app setting name matches exactly what's referenced in code/bindings; check Application Insights (Azure-hosted) or kubectl logs (Kubernetes) for connection errors.

Fix: verify the storage/Service Bus connection string or managed identity role assignment; on Kubernetes also check the KEDA ScaledObject's authenticationRef resolved correctly.

Excessive cold starts despite Premium/Flex plan

Diagnose: check the configured "always ready"/pre-warmed instance count — it may be zero.

Fix: set a nonzero always-ready instance count for the specific trigger group experiencing cold starts.

KEDA ScaledObject shows but replicas never scale down to zero

Diagnose: kubectl describe scaledobject NAME — check cooldownPeriod and confirm the underlying metric is actually reporting zero.

Fix: verify minReplicaCount: 0 is set (default is often non-zero); confirm no other HPA or PodDisruptionBudget is preventing scale-in.

HTTP-triggered function on AKS doesn't scale down under no load

Diagnose: plain CPU/memory-based HPA keeps at least 1 replica warm because there's no "zero requests" signal it understands.

Fix: add an HTTP-aware KEDA scaler (KEDA HTTP Add-on or a third-party HTTP scaler) in front of the Deployment.

Durable Function orchestration appears "stuck"

Diagnose: check the orchestration history table/instance status via the Durable Functions HTTP management API or Durable extension's status query.

Common cause: a non-deterministic call inside the orchestrator broke replay consistency, or an Activity function is throwing and retries are exhausted.

Fix: move offending code into an Activity; check Activity function logs for the underlying exception.

Function app hitting Flex Consumption regional quota

Diagnose: executions/deployments delayed or throttled — check subscription+region core usage against the shared 250-core default quota.

Fix: request a quota increase, redistribute apps across regions, or reduce per-instance memory size if over-provisioned.

Interview Questions

Q: What's the fundamental difference between how Consumption and AKS+KEDA scale a Functions app?
Consumption's Azure-managed scale controller polls the trigger source directly and provisions Azure-managed host instances outside Kubernetes entirely. On AKS, KEDA polls the same kind of trigger source but publishes it as a custom metric that the standard Kubernetes HPA consumes to scale a Deployment — the scaling decision logic is conceptually the same, but the execution substrate and who operates it are completely different.
Q: Why is HTTP-triggered scale-to-zero harder on Kubernetes than on Consumption?
Native Kubernetes HPA only understands CPU/memory/custom metrics that already have a value — it can't detect "zero incoming requests" the way Azure's scale controller can for HTTP. KEDA needs a dedicated HTTP-aware scaler (proxying requests and reporting request-rate as a metric) to achieve true HTTP scale-to-zero.
Q: Why must Durable Functions orchestrator code be deterministic?
The Durable Task Framework uses event sourcing: it replays the orchestrator's history from the start every time it's reactivated. Non-deterministic operations (I/O, random values, current time) would produce different results on replay, corrupting orchestration state — so they must be isolated in Activity functions instead.
Q: When would you pick Flex Consumption over Premium?
When you want serverless per-execution billing and true scale-to-zero but still need VNet integration and reduced (not necessarily zero) cold starts via always-ready instances — Premium is for workloads that cannot tolerate any cold start and are willing to pay for continuously running compute.
Q: What does the KEDA add-on give you on AKS that vanilla open-source KEDA wouldn't automatically have?
Managed lifecycle (upgrades tied to AKS releases), decoupled autoscaling authentication (so scaler credentials aren't workload credentials), and native Microsoft Entra Workload ID integration — plus, on AKS Automatic, zero setup since it's preconfigured by default.
Q: Why might a team deliberately run Functions on AKS instead of any Azure-hosted plan?
To use a KEDA scaler with no native Azure Functions trigger equivalent (e.g., Kafka, RabbitMQ, Prometheus-based scaling), to unify the operational platform (RBAC, network policy, observability) with existing AKS microservices, or to satisfy network-isolation requirements that keep all compute inside a self-managed VNet/cluster.

Revision Cheat Sheet

2026 serverless default: Flex Consumption
Zero cold starts: Premium plan
Legacy, still exists: Consumption plan (Linux retiring Sep 2028)
v1.x runtime EOL: Sep 14, 2026
Scale on Azure-hosted plans: built-in scale controller
Scale on Kubernetes: KEDA → Kubernetes HPA
KEDA scale-to-zero unit: ScaledObject.minReplicaCount = 0
HTTP scale-to-zero on K8s: needs HTTP-aware KEDA scaler
Deterministic-only code: Durable orchestrator functions
Non-deterministic work goes in: Activity functions
KEDA on AKS Automatic: preconfigured by default
KEDA on AKS Standard: opt-in add-on (enable workload identity first)
Auth best practice: Entra Workload ID for both functions and KEDA TriggerAuthentication
Recommended programming model: v2 (decorator-based)

Glossary

TriggerThe event source that invokes a function; exactly one per function.
BindingDeclarative input/output wiring to a service, no manual SDK calls needed.
Scale controllerAzure-managed component that adds/removes Functions host instances on Consumption/Flex/Premium.
KEDAKubernetes Event-Driven Autoscaling — CNCF project exposing custom metrics to the native Kubernetes HPA.
ScaledObjectKEDA custom resource defining a scale target, min/max replicas, and one or more triggers.
ScaledJobKEDA custom resource for scaling Kubernetes Jobs (run-to-completion) rather than long-running Deployments.
TriggerAuthenticationKEDA custom resource defining how the scaler itself authenticates to the event source (secret, managed identity, workload identity).
Durable Task FrameworkEvent-sourcing engine underlying Durable Functions orchestrations.
Always-ready instanceA pre-warmed Flex Consumption/Premium instance kept running to avoid cold starts.
Extension bundleVersioned package of binding extensions referenced in host.json.
Entra Workload IdentityOIDC-federation mechanism letting pods (or KEDA scalers) authenticate to Azure without static secrets.