Choosing Between Azure Functions, AKS, and Virtual Machines
Azure Functions, AKS, and Virtual Machines are the three primary compute options in Azure for most application workloads. Each represents a different point on the management-control spectrum. Choosing correctly from the start avoids expensive re-architecturing — and the right choice depends on workload characteristics, team capability, and operational investment capacity, not on which service sounds most modern.
The management spectrum
These three services occupy different positions on the infrastructure management spectrum:
- Virtual Machines (IaaS): You manage everything — OS installation, patches, runtime, application deployment, scaling, and networking. Maximum control, maximum operational responsibility.
- AKS (Container as a Service): Microsoft manages the Kubernetes control plane; you manage node pools, cluster configuration, workload deployment, and Kubernetes resources. Control over the orchestration layer; Microsoft owns the control plane infrastructure.
- Azure Functions (FaaS): Microsoft manages all infrastructure. You write code and configure triggers. Minimum control, minimum operational responsibility.
As you move from VM toward Functions, you trade control for simplicity. As you move from Functions toward VMs, you gain control but inherit operational responsibility. Neither direction is inherently better — it depends on what the workload needs and what the team can operate reliably.
Three-way comparison
| Dimension | Azure Functions | AKS | Virtual Machines |
|---|---|---|---|
| Execution model | Event-driven, ephemeral | Persistent pods, orchestrated containers | Persistent process, always-on OS |
| Billing model | Per execution (Consumption plan) | Node VM hours regardless of pod utilisation | VM hours regardless of workload utilisation |
| Scale to zero | Yes (Consumption plan) | Pods yes (with KEDA); nodes still billed | No |
| Cold start latency | 200ms–2s (Consumption) | Pod startup 1–30s; no cold start for running pods | None — VM always running |
| Team skill requirement | Low — code and trigger config only | High — Kubernetes, networking, cluster management | Medium — OS administration and application deployment |
| Execution duration limit | 10 min (Consumption); 60 min (Premium) | No limit | No limit |
| Custom OS / kernel | No — managed runtime only | Limited — node OS choice, containers share host kernel | Yes — any OS, any kernel version |
| Stateful workloads | Stateless; Durable Functions for orchestration | StatefulSets with persistent volumes | Full in-process and disk state |
| Networking | No VNet by default (Premium required) | Full VNet, pod-level Network Policies | Full VNet from creation |
| Workload density | High (scale to zero and up automatically) | High — multiple pods per node via bin-packing | Low — typically one workload per VM |
| Vendor lock-in risk | High — Functions SDK and trigger model are Azure-specific | Low — standard Kubernetes, portable to any cloud | Medium — VHD format ties to Azure; application code is portable |
Cost at different scales
The cost profile of each option changes significantly based on workload volume and utilisation. At low volume, Functions is cheapest. At sustained high volume with multiple services, AKS becomes most cost-effective. VMs are rarely the cheapest option except for specific always-on single-service scenarios.
| Scenario | Functions cost | AKS cost | VM cost | Winner |
|---|---|---|---|---|
| 1 event-driven job, 1M executions/month, 200ms avg | ~$0.20/month | ~$70+ (1 node minimum) | ~$15 (B1ms) | Functions |
| 1 always-on HTTP API, 50M req/month | ~$50/month | ~$70+ (1 node) | ~$70 (D2s_v5) | VM or Functions (comparable) |
| 10 microservices, moderate traffic | ~$50–200 (variable) | ~$140–280 (2 nodes shared) | ~$350–700 (10 individual VMs) | AKS |
| Legacy app, 1 service, always-on | Not applicable | ~$70+ (1 node) | ~$70 (D2s_v5) | VM (no containerisation overhead) |
Reserved Instances reduce VM and AKS node costs by 36–56% for 1 or 3-year commitments. Azure Savings Plans provide similar discounts with more SKU flexibility. Factor these discounts into cost comparisons — pay-as-you-go pricing makes VMs and AKS look less competitive than they are at steady-state production scale.
Team capability as a decision factor
The technical capability available in your team is as important as workload characteristics. A technically correct choice for the workload that the team cannot operate reliably is not the right choice.
Functions: Requires minimal infrastructure knowledge. The main skills are understanding trigger and binding types, the Functions programming model, Durable Functions for orchestration, and Application Insights for monitoring. No Kubernetes or OS administration knowledge needed.
AKS: Requires Kubernetes expertise — pod scheduling, resource requests and limits, rolling updates, HPA and KEDA configuration, Ingress controllers, cluster networking (CNI choice), and upgrade management. Without this expertise in-house, operating AKS in production carries significant incident risk. Budget for Kubernetes training or an experienced hire.
Virtual Machines: Requires OS administration knowledge — patch management, startup configuration, monitoring agent installation, and networking. Most engineering teams have sufficient VM expertise. The risks are configuration drift over time and deployment processes that are slower and less consistent than container-based workflows at scale.
The 5-question decision framework
Answer these five questions in order to reach a compute recommendation:
Question 1: Is the workload event-driven or continuously running?
- Event-driven (triggered by HTTP, queue, blob, timer) → consider Functions first; return to this list if a limitation disqualifies it
- Continuously running server or persistent background process → skip to Question 2
Question 2: Does the workload require a specific OS, kernel module, or runtime not available in containers?
- Yes (Windows Service, COM component, specific kernel module, legacy binary with no container port) → Virtual Machine
- No → continue to Question 3
Question 3: How many services need to be deployed?
- 1–3 services → VMs are simpler and likely cheaper; the density advantage of AKS does not materialise at this count
- 4+ services, all containerisable → continue to Question 4
Question 4: Does your team have Kubernetes expertise, or is it a realistic investment?
- No Kubernetes expertise and not willing to acquire it → Azure Container Apps (provides orchestration without Kubernetes management)
- Yes, or building a dedicated platform team → continue to Question 5
Question 5: Do you need full Kubernetes API access?
- Yes (CRDs, admission webhooks, StatefulSets with complex storage, GPU nodes, service mesh) → AKS
- No → Azure Container Apps (simpler, cheaper for most containerised workloads)
Clear decision scenarios
| Workload description | Recommended platform | Reason |
|---|---|---|
| Webhook processing payment events from Stripe | Azure Functions | Event-driven, short execution, scale-to-zero saves cost |
| Nightly 45-minute data aggregation job | VM or AKS Job | Exceeds Functions 10-min Consumption limit; use VM or Kubernetes CronJob |
| SQL Server 2019 app with Windows Service dependencies | VM (Windows) | OS requirements that cannot be containerised |
| 8 containerised microservices serving a REST API | AKS or Container Apps | Density savings justify orchestration at this service count |
| ML inference endpoint, containerised, accessed occasionally | Azure Container Apps | Scale-to-zero when idle; no GPU needed; simpler than AKS |
| IoT telemetry processor at 50M events/day continuous | AKS or VMSS | Sustained throughput; Functions Consumption cost too high at this volume |
| Dev/test environment used 4 hours per day | VM with auto-shutdown | Auto-shutdown at end of day eliminates idle compute cost |
Common mistakes
- Choosing AKS for a single microservice because “we plan to have more later.” Speculative future complexity is not a good reason to accept real present operational burden. Start with Container Apps or a VM; migrating to AKS when you have 5+ services is a well-trodden path that works cleanly.
- Using Functions for a long-running process without checking the timeout limit. A batch job that needs 20 minutes will fail on Consumption plan’s 10-minute timeout. Verify duration limits before committing Functions to batch workloads. Use Durable Functions for multi-step long-running orchestrations.
- Defaulting to VMs for all workloads because they are familiar. Deploying 10 individual VMs for 10 services that could share 2 AKS nodes means paying roughly 5x more in compute costs and managing 10x more OS instances. Familiarity is a valid operational consideration; excessive spend is not.
- Not modelling actual cost before deciding. Each service has a different cost structure that produces very different outcomes depending on execution frequency, duration, and utilisation patterns. Running a 30-minute estimate in the Azure Pricing Calculator prevents expensive surprises after deployment.
Summary
- Azure Functions is optimal for event-driven, short-duration, variable-traffic workloads — scale-to-zero billing makes it dramatically cheaper than VMs or AKS for infrequent jobs.
- AKS delivers density and deployment consistency across 5+ containerised services and provides full Kubernetes API access for teams that need it — operational overhead is only justified at scale.
- VMs are the right choice for legacy applications with OS or kernel requirements, single-service deployments where container orchestration is overkill, and lift-and-shift migrations.
- The 5-question decision framework — event-driven? OS-specific? service count? Kubernetes expertise? full Kubernetes API needed? — guides the decision systematically without assuming any platform is the default.
Frequently asked questions
Is there a scenario where all three are used together?
Yes, and it is common. A typical architecture might use VMs for legacy databases and third-party software that cannot be containerised, AKS for containerised microservices that need orchestration and consistent networking, and Azure Functions for event-driven processing — blob triggers, queue workers, timers. Each serves a different layer of the same system.
What is the total cost of ownership for AKS vs VMs at small scale?
At small scale (1–3 services), VMs are typically cheaper in total cost of ownership. A 2-node AKS cluster for 1 service costs similar to a single VM but adds Kubernetes operational overhead. AKS delivers cost advantages through density at scale (5+ containerised services sharing nodes), not at small scale.
Can I switch between these options later without re-architecting?
Moving from VMs to AKS requires containerisation — significant but manageable work for modern applications. Moving from AKS to Functions requires re-writing to the Functions programming model — a larger change. Moving from Functions to VMs or AKS is straightforward if the code is containerisable. Compute model decisions are not trivially reversible for complex applications.