DEV Community

Cover image for Azure Compute Options: App Service, Azure Functions, and AKS
Rhuturaj Takle
Rhuturaj Takle

Posted on

Azure Compute Options: App Service, Azure Functions, and AKS

Azure Compute Options: App Service, Azure Functions, and AKS

A practical guide to Azure's three main compute hosting models for .NET applications — App Service for web apps and APIs, Azure Functions for serverless event-driven code, and Azure Kubernetes Service (AKS) for container orchestration — covering how each works, when to choose which, and how to deploy to them.


Table of Contents

  1. Introduction
  2. App Service
  3. Azure Functions
  4. Azure Kubernetes Service (AKS)
  5. Scaling Models Compared
  6. Networking and Security
  7. CI/CD and Deployment
  8. Cost Models
  9. Choosing Between Them
  10. Combining All Three
  11. Quick Reference Table
  12. Conclusion

Introduction

Azure offers a spectrum of compute options, from fully managed and opinionated (App Service) to fully serverless and event-driven (Functions) to fully flexible but more operationally demanding (AKS). None of them is universally "better" — each trades control for convenience differently, and most real-world Azure architectures use more than one at once.

Less operational control, more managed  ◄──────────────────►  More control, more responsibility
        Azure Functions          App Service                    AKS
Enter fullscreen mode Exit fullscreen mode

This guide walks through each option, then compares them directly so you can match the right tool to the right workload.


1. App Service

Azure App Service is a fully managed platform for hosting web apps, REST APIs, and mobile backends. You deploy your code (or a container), and Azure handles the underlying VMs, OS patching, load balancing, and most infrastructure concerns.

What you get out of the box

  • Managed OS and runtime patching — you never SSH into a VM to apply updates.
  • Built-in load balancing across multiple instances.
  • Deployment slots for zero-downtime releases (see below).
  • Auto-scaling based on CPU, memory, or schedule.
  • Native support for .NET, Node.js, Java, Python, PHP, and custom containers.
  • Integrated custom domains, free-managed SSL certificates, and easy VNet integration.

Deploying an ASP.NET Core app

az webapp up --name my-api --resource-group my-rg --runtime "DOTNETCORE:9.0"
Enter fullscreen mode Exit fullscreen mode

Or via a Dockerfile if you'd rather bring your own container:

az webapp create --name my-api --resource-group my-rg \
    --plan my-app-service-plan --deployment-container-image-name myregistry.azurecr.io/my-api:latest
Enter fullscreen mode Exit fullscreen mode

App Service Plans

Every App Service app runs within an App Service Plan — the underlying compute (VM size and instance count) that one or more apps can share. Picking a plan tier controls both cost and capability:

Tier Typical use
Free/Shared Prototypes, learning, no SLA
Basic Dev/test workloads, no auto-scale
Standard Production apps, auto-scale, deployment slots
Premium Higher-scale production, more slots, VNet integration
Isolated Dedicated network (App Service Environment), high compliance needs

Deployment slots

Slots let you deploy a new version to a separate, fully live environment (e.g., my-api-staging.azurewebsites.net) and then swap it into production with no downtime:

az webapp deployment slot create --name my-api --resource-group my-rg --slot staging
az webapp deployment source config-zip --name my-api --resource-group my-rg --slot staging --src release.zip
az webapp deployment slot swap --name my-api --resource-group my-rg --slot staging --target-slot production
Enter fullscreen mode Exit fullscreen mode

This is one of App Service's most valuable built-in features: you can smoke-test the new version against production-like traffic and settings before it ever serves real users, and roll back instantly by swapping again if something's wrong.

Best fit

Traditional web applications, REST/GraphQL APIs, and backend services that run continuously and benefit from a fully managed platform without needing container orchestration complexity.


2. Azure Functions

Azure Functions is Azure's serverless compute platform: you write small, focused units of code ("functions") that run in response to triggers — an HTTP request, a queue message, a timer, a blob upload — and Azure handles provisioning, scaling, and (depending on the hosting plan) billing you only for actual execution time.

Triggers and bindings

A function is defined by its trigger (what starts it) and optionally input/output bindings (declarative ways to read/write data without hand-writing SDK calls):

public class OrderFunctions
{
    [Function("ProcessOrder")]
    public async Task Run(
        [QueueTrigger("orders-queue")] string orderJson,
        [CosmosDBOutput("Orders", "OrdersContainer", Connection = "CosmosDbConnection")] IAsyncCollector<Order> output)
    {
        var order = JsonSerializer.Deserialize<Order>(orderJson);
        await output.AddAsync(order);
    }
}
Enter fullscreen mode Exit fullscreen mode
public class HttpFunctions
{
    [Function("GetProduct")]
    public HttpResponseData GetProduct(
        [HttpTrigger(AuthorizationLevel.Function, "get", Route = "products/{id}")] HttpRequestData req,
        int id)
    {
        var product = _repository.GetById(id);
        var response = req.CreateResponse(product is not null ? HttpStatusCode.OK : HttpStatusCode.NotFound);
        if (product is not null) response.WriteAsJsonAsync(product);
        return response;
    }
}
Enter fullscreen mode Exit fullscreen mode

Common trigger types: HTTP, Timer (cron-style schedules), Queue (Storage Queue or Service Bus), Blob (file uploads), Event Grid/Event Hub (event streaming), and Cosmos DB (change feed).

Timer-triggered scheduled jobs

[Function("NightlyCleanup")]
public void Run([TimerTrigger("0 0 2 * * *")] TimerInfo timer)
{
    // runs daily at 2:00 AM
}
Enter fullscreen mode Exit fullscreen mode

This is a common, lightweight alternative to a full background-worker process for a job that runs briefly and infrequently — no long-running host to manage at all.

Hosting plans

Plan Scaling Cold start Billing
Consumption Automatic, scales to zero Yes (can be noticeable) Pay only per execution + resource consumption
Premium Automatic, pre-warmed instances Minimal/none Pay for pre-warmed capacity + usage
Dedicated (App Service Plan) Manual/autoscale like App Service None (always running) Pay for the underlying VM plan regardless of usage
Container Apps / AKS-hosted (KEDA) Event-driven scaling via KEDA Depends on config Pay for underlying compute

The Consumption plan is the purest "serverless" experience — you pay per execution and per GB-second of memory used, and Azure scales instance count (including down to zero when idle) automatically. The tradeoff is cold start: the first request after a period of inactivity pays the cost of spinning up a fresh instance, which can add a noticeable delay for latency-sensitive workloads.

Durable Functions

For workflows that need to coordinate multiple steps, wait for external events, or run for a long time (well beyond a single function's execution limit), Durable Functions provides an orchestration framework on top of regular Functions:

[Function("OrderOrchestrator")]
public static async Task<string> RunOrchestrator([OrchestrationTrigger] TaskOrchestrationContext context)
{
    var order = context.GetInput<Order>();
    await context.CallActivityAsync("ValidateOrder", order);
    await context.CallActivityAsync("ChargePayment", order);
    await context.CallActivityAsync("ShipOrder", order);
    return "Completed";
}
Enter fullscreen mode Exit fullscreen mode

This handles the hard parts of long-running, stateful workflows (checkpointing progress, surviving process restarts mid-workflow, fan-out/fan-in patterns) without you building that machinery yourself.

Best fit

Event-driven processing (queue/blob/event triggers), scheduled jobs that run briefly, lightweight APIs with spiky or unpredictable traffic, and workloads where "pay only when it runs" materially matters — as opposed to something that needs to be always-warm and continuously running.


3. Azure Kubernetes Service (AKS)

AKS is Azure's managed Kubernetes offering — Azure runs and patches the Kubernetes control plane for you, while you manage (and pay for) the worker node pools that actually run your containers.

What "managed" means here

  • Azure operates the Kubernetes API server and control plane — no manual etcd/control-plane patching.
  • You define and manage node pools (the VMs that run your workloads), including autoscaling them.
  • You still write and manage standard Kubernetes manifests (Deployments, Services, Ingress, etc.) — AKS doesn't hide Kubernetes from you, it just removes the control-plane operational burden.

A basic deployment

az aks create --resource-group my-rg --name my-cluster --node-count 3 --generate-ssh-keys
az aks get-credentials --resource-group my-rg --name my-cluster
Enter fullscreen mode Exit fullscreen mode
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: product-api
spec:
  replicas: 3
  selector:
    matchLabels: { app: product-api }
  template:
    metadata:
      labels: { app: product-api }
    spec:
      containers:
        - name: product-api
          image: myregistry.azurecr.io/product-api:latest
          ports:
            - containerPort: 8080
          resources:
            requests: { cpu: "250m", memory: "256Mi" }
            limits: { cpu: "500m", memory: "512Mi" }
---
apiVersion: v1
kind: Service
metadata:
  name: product-api-service
spec:
  type: LoadBalancer
  selector: { app: product-api }
  ports:
    - port: 80
      targetPort: 8080
Enter fullscreen mode Exit fullscreen mode
kubectl apply -f deployment.yaml
Enter fullscreen mode Exit fullscreen mode

Autoscaling at two levels

AKS supports scaling both the application (pods) and the underlying infrastructure (nodes):

kubectl autoscale deployment product-api --cpu-percent=70 --min=3 --max=10  # Horizontal Pod Autoscaler
az aks nodepool update --resource-group my-rg --cluster-name my-cluster --name nodepool1 --enable-cluster-autoscaler --min-count 2 --max-count 10  # Cluster Autoscaler
Enter fullscreen mode Exit fullscreen mode

The Horizontal Pod Autoscaler (HPA) scales the number of running pod replicas based on CPU/memory or custom metrics; the Cluster Autoscaler scales the number of underlying VM nodes when there isn't enough room to schedule the pods the HPA wants to run — together they let a cluster grow and shrink automatically at both layers.

Why teams choose AKS

  • Multiple services, one platform — dozens or hundreds of microservices can share a cluster's infrastructure, with Kubernetes handling scheduling, service discovery, and networking between them.
  • Fine-grained control — resource limits, custom scheduling rules, sidecar patterns (like a service mesh), and complex networking policies are all available if you need them.
  • Portability — a workload packaged as standard Kubernetes manifests isn't Azure-specific; the same manifests largely work on any conformant Kubernetes cluster (EKS, GKE, on-prem).
  • Ecosystem — the vast Kubernetes/CNCF ecosystem (Helm charts, Prometheus, Istio, ArgoCD, KEDA) is available directly.

The real tradeoff

AKS gives you the most control and flexibility of the three options, but also the most operational surface area: you're responsible for node pool sizing and patching cadence, namespace and RBAC design, ingress configuration, monitoring the cluster itself (not just your apps), and generally understanding Kubernetes concepts that App Service and Functions abstract away entirely.

Best fit

Organizations already running (or committed to) Kubernetes, complex microservice architectures needing fine-grained orchestration control, workloads needing portability across clouds, or teams with existing Kubernetes expertise who want that control without managing the control plane themselves.


4. Scaling Models Compared

App Service Azure Functions (Consumption) AKS
Scale unit App instance Individual function execution Pod (and, underneath, node)
Scale to zero No (lowest tier still runs) Yes No (unless using KEDA-driven scale-to-zero patterns)
Cold start No (always-on instances) Yes, on Consumption plan No, but pod scheduling has its own startup latency
Scaling trigger CPU/memory/schedule rules Automatic, per-trigger-event HPA (app) + Cluster Autoscaler (infrastructure)
Granularity Whole-app instances Per-function, per-event Per-pod, highly granular

5. Networking and Security

App Service

  • VNet integration lets an App Service app reach resources inside a private virtual network.
  • Private Endpoints can make the app itself only reachable from within a VNet.
  • Managed Identity lets the app authenticate to other Azure resources (Key Vault, Storage, SQL) without storing credentials in config.

Azure Functions

  • Same VNet integration and Managed Identity support as App Service on Premium/Dedicated plans (Consumption plan has more limited networking control).
  • Function-level authorization keys (AuthorizationLevel.Function) provide simple built-in access control for HTTP triggers, though production APIs usually front Functions with Azure API Management or proper OAuth/JWT validation for anything beyond internal use.

AKS

  • Network Policies control pod-to-pod traffic within the cluster.
  • Azure CNI or kubenet control how pod networking integrates with the Azure VNet.
  • Azure AD (Entra ID) integration for RBAC over the Kubernetes API itself, layered on top of standard Kubernetes RBAC.
  • Secrets typically flow through Azure Key Vault via the Secrets Store CSI Driver, rather than plain Kubernetes Secrets (which are only base64-encoded, not encrypted, by default without extra configuration).

Shared building blocks

All three commonly sit behind Azure Front Door or Application Gateway for global load balancing, WAF (Web Application Firewall) protection, and TLS termination — the choice of App Service/Functions/AKS is about compute hosting, not about how traffic reaches your edge.


6. CI/CD and Deployment

App Service

# GitHub Actions
- uses: azure/webapps-deploy@v3
  with:
    app-name: my-api
    package: ./publish
Enter fullscreen mode Exit fullscreen mode

Deployment slots (Section 1) make this safe by default — deploy to staging, verify, then swap.

Azure Functions

- uses: azure/functions-action@v1
  with:
    app-name: my-function-app
    package: ./publish
Enter fullscreen mode Exit fullscreen mode

Functions support the same slot-swapping pattern as App Service on Premium/Dedicated plans.

AKS

- run: |
    az acr build --registry myregistry --image product-api:${{ github.sha }} .
    kubectl set image deployment/product-api product-api=myregistry.azurecr.io/product-api:${{ github.sha }}
Enter fullscreen mode Exit fullscreen mode

Most teams layer a GitOps tool (ArgoCD, Flux) or a progressive-delivery controller (Flagger) on top of raw kubectl commands for anything beyond a simple demo — AKS deployment pipelines tend to be the most involved of the three to set up well, in exchange for the most control over the rollout process (canary releases, blue/green, automated rollback on failed health checks).


7. Cost Models

App Service Azure Functions (Consumption) AKS
Billing basis Per-instance-hour of the App Service Plan, regardless of traffic Per-execution + memory×time (GB-seconds); free monthly grant Per-VM-hour for node pools, regardless of pod utilization; AKS control plane itself is free on the Standard tier
Idle cost You pay for provisioned instances even at zero traffic Near-zero at zero traffic (Consumption plan) You pay for provisioned nodes even if pods are idle
Cost predictability High — fixed plan cost Lower for spiky workloads, harder to predict at high, constant volume Depends heavily on node pool sizing and bin-packing efficiency

A rough rule of thumb: Functions on Consumption is cheapest for spiky, infrequent, or unpredictable workloads; App Service is more cost-predictable for steady, continuous traffic; AKS can be the most cost-efficient at scale (many services efficiently packed onto shared nodes) but requires active capacity planning to avoid paying for idle, oversized nodes.


8. Choosing Between Them

Is this event-driven, spiky, or scheduled work
(queue processing, file triggers, cron jobs, lightweight APIs)?
        │
        ├── Yes → Azure Functions
        │
        └── No — it's a continuously running web app/API
                │
                ├── Do you need Kubernetes-level orchestration control,
                │   run many interdependent microservices, or need
                │   portability across clouds?
                │       │
                │       ├── Yes → AKS
                │       │
                │       └── No → App Service
Enter fullscreen mode Exit fullscreen mode

Concrete scenarios

  • A REST API for a mobile app backend, moderate steady traffic → App Service. Managed, simple, deployment slots for safe releases.
  • Processing images uploaded to Blob Storage, resizing them, a few times a minute → Azure Functions (Blob trigger, Consumption plan).
  • A platform with 40 microservices, service mesh requirements, and a dedicated platform team → AKS.
  • A nightly batch job that runs for 10 minutes → Azure Functions (Timer trigger) — no reason to run a continuously-billed App Service instance just to fire once a day.
  • An internal admin dashboard with predictable, low, constant traffic → App Service on a Basic/Standard plan.

9. Combining All Three

Real Azure architectures rarely pick just one. A common pattern:

  • App Service hosts the main customer-facing web application and its REST API.
  • Azure Functions handles asynchronous, event-driven side work triggered by that API — sending confirmation emails off a queue, processing uploaded documents, running nightly reports.
  • AKS runs a set of internal, more complex microservices (perhaps including gRPC services communicating with each other) that need finer orchestration control, alongside supporting infrastructure like a message broker or a service mesh.

None of these need to know about the others' hosting model — a Function can enqueue a message that an AKS-hosted worker consumes, and an App Service app can call an AKS-hosted internal API over a private VNet connection — the compute layer is an implementation detail, not something that needs to be uniform across an entire system.


Quick Reference Table

Concept App Service Azure Functions AKS
Model PaaS — managed web hosting Serverless — event-driven functions Managed container orchestration
Unit of deployment Web app (code or container) Individual functions Pods via Kubernetes manifests
Scale to zero No Yes (Consumption plan) No (without extra tooling)
Best for Continuously running web apps/APIs Event-driven, spiky, scheduled workloads Complex microservices, orchestration control
Ops overhead Low Lowest Highest
Zero-downtime deploys Deployment slots Deployment slots (Premium/Dedicated) Rolling updates / canary via tooling
Networking control VNet integration, Private Endpoints VNet integration (Premium/Dedicated) Full Kubernetes networking (CNI, policies)

Conclusion

App Service, Azure Functions, and AKS aren't competing answers to the same question — they're answers to three different questions. App Service asks "how do I run a web app/API without managing infrastructure?" Functions asks "how do I run small units of code only when something specific happens, paying only for that?" AKS asks "how do I orchestrate many containerized services with full control over networking, scheduling, and scaling?"

Most production systems end up using more than one, and the right starting point is usually the option with the least operational overhead that still satisfies the workload's actual requirements — reach for AKS when you genuinely need Kubernetes-level control, not by default, and let a workload's shape (continuous vs. event-driven, simple vs. many interdependent services) point you to the right compute model rather than picking one and forcing every workload to fit it.


Found this useful? Feel free to star the repo, open an issue with corrections, or share which Azure compute model ended up being the wrong first choice for your workload.

Top comments (0)