Compute & App Services
Azure Virtual Machines, App Service for deploying web apps without managing servers, and when to choose a VM vs App Service vs a container.
Two ways to run code in Azure
Every cloud provider ultimately answers the same question: "where does my code actually execute?" Azure gives you a whole spectrum of answers, ranging from "I control everything down to the operating system" to "I don't think about servers at all." This page covers the two ends of that spectrum you'll meet first — Virtual Machines and App Service — plus where containers fit between them.
Azure Virtual Machines (IaaS)
An Azure Virtual Machine (VM) is a full, isolated computer running inside Microsoft's datacenters that behaves exactly like a physical machine you're used to — except you didn't have to buy, rack, or plug it in. This model is called Infrastructure as a Service (IaaS): Azure gives you the raw infrastructure (compute, storage, networking), and you are responsible for everything above that — the operating system, security patches, installed software, scaling, backups.
Think of it like renting an unfurnished apartment: you get four walls, plumbing, and electricity, but you bring your own furniture, and you're the one who has to fix a leak if one shows up.
A few core concepts you'll configure whenever you create a VM:
- Image — the starting operating system and software (
Ubuntu Server 22.04,Windows Server 2022, or a pre-built image with software already installed). - Size (SKU) — the amount of CPU, memory, and disk performance you're renting, named things like
B1s(a cheap, "burstable" size good for light/dev workloads) orD2s_v5(a general-purpose size with steadier performance). Bigger size = more capability = more cost, billed per second while the VM is running. - Disks — persistent storage attached to the VM, which (unlike the VM's memory) survives a reboot.
- Networking — a virtual network (VNet), a public IP if you want it reachable from the internet, and a Network Security Group (NSG) acting as a basic firewall — which ports are open, and to whom.
Creating one via the CLI looks like this:
az group create --name learning-rg --location eastus
az vm create \
--resource-group learning-rg \
--name my-first-vm \
--image Ubuntu2204 \
--size Standard_B1s \
--admin-username azureuser \
--generate-ssh-keys
Once it's running, you SSH into it exactly as you would any Linux server, install whatever you want, and manage it yourself — including remembering to apply security patches, since Azure will never do that for you on an IaaS VM.
VM Scale Sets let you define a VM configuration once and automatically run (and scale) many identical copies behind a load balancer, adding more instances under high traffic and removing them when it's quiet — but you're still managing "a fleet of servers," just an elastic one.
Azure App Service (PaaS)
Azure App Service takes a completely different approach: it's a Platform as a Service (PaaS) for hosting web apps, REST APIs, and mobile backends. You give Azure your code (or a container image); Azure handles the operating system, the web server, patching, and (optionally) scaling — you never SSH into anything.
Using the apartment analogy again: App Service is a furnished, serviced apartment. You move in with your suitcase (your code); the building handles maintenance, security, and utilities.
App Service is built around two pieces:
- An App Service Plan — defines how much compute you're paying for (a pricing tier like
Free,Basic,Standard,Premium) and, crucially, is what actually gets billed. One plan can host multiple apps. - A Web App — the actual application running on top of that plan, with its own URL (
https://yourapp.azurewebsites.net), its own configuration, and its own deployment history.
App Service natively supports .NET, Java, Node.js, Python, PHP, and Ruby, and it also has a container mode where you point it at a Docker image instead of raw code — you get the deployment simplicity of App Service with the environment control of a container.
A real example: deploying a simple web app
Say you have a small Python Flask app in a local folder with an app.py and a requirements.txt. Deploying it end-to-end takes three commands:
# 1. Create a resource group (skip if you already have one)
az group create --name learning-rg --location eastus
# 2. Create the App Service plan (the compute tier) and the web app in one step
az webapp up \
--resource-group learning-rg \
--name my-flask-demo-app \
--runtime "PYTHON:3.12" \
--sku B1
az webapp up is a convenience command that detects your app's language, creates the App Service Plan and Web App if they don't exist yet, zips up your local folder, and deploys it — all in one shot. Within a minute or two, your app is live at https://my-flask-demo-app.azurewebsites.net, on a real public URL, with HTTPS already configured for you.
To ship an update after changing your code, you just run the same command again — it redeploys the new version:
az webapp up --name my-flask-demo-app
App Service also plugs directly into CI/CD: you can connect a GitHub repository so that every push to main triggers an automatic build and deploy via GitHub Actions, with zero manual deployment steps after the initial setup.
Scaling in App Service
App Service supports both:
- Scaling up — moving to a bigger pricing tier (more CPU/memory per instance).
- Scaling out — running more instances of your app behind a load balancer, either manually or automatically based on rules (e.g., "add an instance when average CPU exceeds 70%").
Both are a slider and a checkbox in the portal — no load balancer to configure by hand, no fleet of VMs to keep patched.
Where containers fit
A container (via Docker) packages your app together with its exact dependencies and runtime into one portable image that runs identically anywhere. Azure gives you two main ways to run one:
- Azure Container Instances (ACI) — the simplest option: hand Azure a container image, and it runs it, billed per second, with no cluster to manage. Great for a single short-lived job, a batch task, or a simple isolated service — the "serverless" way to run one container.
- Azure Kubernetes Service (AKS) — a managed Kubernetes cluster for when you have many interdependent containerized services that need orchestration: automatic restarts, service discovery, rolling deployments, complex scaling rules. This is the right tool once "run one container" grows into "run a whole system of coordinated services."
Choosing between them
| Azure VM | App Service | Container (ACI / AKS) | |
|---|---|---|---|
| Management level | You manage the OS, patches, runtime | Azure manages the OS and runtime | You manage the container image; Azure runs it |
| Best for | Full OS control, custom software, legacy apps, specific OS/driver needs | Web apps, REST APIs, mobile backends — the common case | Portable, isolated workloads; microservices at scale (AKS) |
| Scaling | Manual, or via Scale Sets | Built-in scale up/out | ACI: manual; AKS: full orchestrated scaling |
| Time to first deploy | Slowest — you configure everything | Fastest — az webapp up and you're live |
Fast for one container (ACI); AKS has real setup overhead |
| You SSH in? | Yes, it's your machine | No, and you generally shouldn't need to | Not typically |
A simple rule of thumb: default to App Service for a standard web app or API — it's the least amount of your own time spent on infrastructure. Reach for a VM only when you genuinely need OS-level control (a specific driver, a legacy stack, software that assumes it owns the whole machine). Reach for containers when your app is already containerized (perhaps because it needs to run identically across dev/staging/prod, or alongside other teams' containerized services), and reach for AKS specifically once you have multiple services that need to be orchestrated together rather than just one.
Common mistakes
- Reaching for a VM by default. Many students' first instinct is "spin up a VM and install everything myself," because it feels the most familiar. For a typical web app, this means manually doing work (patching, scaling, HTTPS certificates) that App Service gives you for free.
- Forgetting the App Service Plan is what's billed, not the app. You can have five web apps on one Basic-tier plan and pay for one plan — but people often assume each app costs separately.
- Leaving a VM "stopped" instead of "deallocated." In the Azure Portal, stopping a VM from within the OS (e.g.,
shutdowncommand) still leaves it allocated and billing for compute. Useaz vm deallocate(or "Stop" from the portal, which deallocates) to actually stop paying for compute. - Using ACI for something that needs orchestration. ACI is great for one container, but if you need service discovery, automatic restarts across multiple interdependent services, or rolling updates, you actually need AKS — ACI won't grow into that role gracefully.