Azure VM stopped vs deallocated: only one stops the bill

You shut an Azure VM down last month and assumed the bill stopped with it. Open the cost analysis and the compute charge is still there, every hour, for a machine nobody has touched. A stopped Azure VM keeps its CPU and memory reserved, so Azure keeps charging you 100% of the compute rate until you release it. Deallocate the VM and that compute charge drops to zero. The fix is one word, and most teams have never run it.

Why a stopped VM still costs full price

Azure has two different off states, and only one of them stops the compute meter.

When you shut a VM down from inside the guest OS, the machine powers off but Azure keeps the physical host reserved for it. That state is called Stopped (allocated). The vCPUs and RAM are still set aside for you, so you keep paying the full compute rate as if the VM were running.

Deallocation is the state that saves money. When a VM is deallocated, Azure releases the compute back to the pool. You stop paying for vCPUs and memory completely. You keep paying only for the managed disks attached to the VM, because those still exist on storage whether the VM runs or not.

Here are the two off states side by side.

State How you get there Compute billed Disks billed Dynamic public IP Temp disk
Stopped (allocated) Guest OS shutdown, az vm stop, Stop-AzVM -StayProvisioned Yes, full rate Yes Kept Kept
Deallocated Portal Stop, az vm deallocate, Stop-AzVM No Yes Released Wiped

Put real numbers on it. As of June 2026, a Standard_D4s_v5 (4 vCPU, 16 GiB) lists around $0.192 per hour for pay-as-you-go Linux in East US on the Azure VM pricing page, which is about $140 per month. Its 128 GiB Premium SSD P10 OS disk lists around $19.71 per month on the managed disks pricing page. Leave that VM Stopped (allocated) and you pay the full $140 of compute for a machine doing no work. Deallocate it and compute drops to zero, leaving roughly $20 a month for the disk you chose to keep.

One caveat if you run Azure Reservations or a savings plan. That commitment bills whether the VM runs or not, so deallocating a covered VM only saves money when the reservation moves to another running VM of the same size. For pay-as-you-go VMs, the full compute charge simply stops.

The trap: "Stop" does not mean deallocate

Here is the part that catches people, and it catches them through the tools they trust most.

Shutting down from inside the operating system never deallocates. The guest has no way to tell Azure to release the host, so the VM sits in Stopped (allocated) and bills on. A shutdown /s on Windows or a shutdown on Linux saves you nothing.

The Azure CLI sets the same trap. az vm stop powers the VM off but leaves it allocated and billing. The command that actually stops the meter is az vm deallocate. Two commands that sound interchangeable, one of them still charges you.

Azure PowerShell flips the trap around. Stop-AzVM on its own deallocates the VM, so the default does the right thing and the meter stops. Add -StayProvisioned and it stops the VM without deallocating, and Azure keeps charging you for the stopped machine. The safe command and the expensive one sit one flag apart, so an old runbook that still passes -StayProvisioned keeps paying full compute for a box that does nothing.

The portal is the exception. The Stop button in the Azure portal deallocates the VM for you. So a team that stops VMs by clicking in the portal pays nothing extra, while a team that automated the same task with az vm stop or a guest shutdown script has been paying full compute the whole time.

Find your stopped-but-billing VMs by hand

Power state does not appear in a plain az vm list. You need the -d flag, which pulls the instance view and includes powerState.

List every VM with its power state in the current subscription:

az vm list -d \
  --query "[].{name:name, rg:resourceGroup, state:powerState}" \
  --output table

The state you are hunting is VM stopped. That is the allocated-but-off state that still bills compute. VM deallocated is the safe one, and VM running is working as intended.

The exact string changes with the surface you read it on, which trips people up when they search for it. The portal shows Stopped (allocated) and Stopped (deallocated). The CLI returns VM stopped and VM deallocated. The REST API and ARM templates use PowerState/stopped and PowerState/deallocated. All three describe the same two states.

Azure Advisor flags low-use running VMs under its cost recommendations, but a VM that is stopped and still allocated can slip past it, so the sweep below is worth running yourself.

Filter straight to the VMs that are costing you money for nothing:

az vm list -d \
  --query "[?powerState=='VM stopped'].{name:name, rg:resourceGroup, size:hardwareProfile.vmSize}" \
  --output table

Every row is a VM you are paying full compute for while it does no work. The size column tells you which ones hurt most, since a stopped GPU or memory-optimized VM burns far more than a stopped B-series box.

The query is scoped to one subscription at a time, so sweep every subscription you own in a loop:

for sub in $(az account list --query "[].id" -o tsv); do
  az vm list -d --subscription "$sub" \
    --query "[?powerState=='VM stopped'].{name:name, rg:resourceGroup, size:hardwareProfile.vmSize}" \
    --output table
done

Deallocate without surprises

Once you have the list, deallocating is one command per VM.

az vm deallocate \
  --resource-group my-rg \
  --name my-vm

The VM moves to VM deallocated, the compute charge stops on the next billing interval, and the disks stay exactly where they are. Starting it again is just as direct.

az vm start \
  --resource-group my-rg \
  --name my-vm

That is your rollback. Deallocation does nothing destructive to your managed disks, so bringing a VM back is a single start call.

Two things are worth checking before you deallocate, because both can bite on restart.

First, the public IP. A dynamic public IP is released when a VM deallocates, and the VM can come back on a different address. If anything points at that IP (DNS records, a firewall allowlist, a partner's config), convert it to a static address first so it survives the restart (the update below flips the allocation method in place; the public IP SKU is immutable on update, and a Basic Static IP survives deallocation just fine, so no SKU change is needed here).

az network public-ip update \
  --resource-group my-rg \
  --name my-vm-ip \
  --allocation-method Static

Second, the temporary disk. The local temp disk (D: on Windows, /dev/sdb1 on Linux) is wiped when a VM deallocates. It is meant for scratch data like page files and caches, so confirm nothing important lives there before you release the host.

One more thing for specialty hardware. Deallocation returns the VM's compute capacity to the regional pool. For common sizes that is fine. For scarce SKUs like GPU or HPC series in a busy region, the same size may not be free when you try to start the VM again. If a machine has to come back on demand, a capacity reservation holds the size for you while it sits deallocated.

A quick decision tree

graph TD
  A[VM doing no work] --> B{Need it again soon?}
  B -- No --> C[Delete the VM and its disks]
  B -- Yes --> D{Power state from az vm list -d}
  D -- VM deallocated --> E[Already not billing compute]
  D -- VM stopped --> F{Dynamic public IP or temp-disk data to keep?}
  D -- VM running --> I[Still powered on, so deallocate stops it. Confirm it is truly idle first]
  I --> F
  F -- Yes --> G[Make the IP static and move temp data first]
  F -- No --> H[Run az vm deallocate now]
  G --> H

The same waste hides wherever you reserve capacity and forget to use it.

Common questions

Does a stopped Azure VM still cost money? If it reads Stopped (allocated), yes. Azure holds the vCPUs and memory for that VM, so the full compute rate keeps billing until you deallocate it. A Stopped (deallocated) VM bills nothing for compute.

What is the difference between stopped and deallocated? Stopped means the VM is powered off but its host is still reserved for you, so compute keeps billing. Deallocated means the host goes back to Azure, so compute drops to zero, unless an Azure Reservation or savings plan covers the VM, in which case that commitment keeps billing until it moves to another running VM of the same size. Either way the managed disks keep billing at their usual rate.

How do I stop paying for an Azure VM I am not using? Deallocate it with az vm deallocate, or hit the portal Stop button, which deallocates for you. If you will never need the VM again, delete it and its disks so the storage charge stops too.

Do I still pay for storage after deallocating? Yes. The managed disks attached to the VM sit on storage whether the VM runs or not, so they bill every month until you delete them. Only the compute charge stops when you deallocate.

Does Azure auto-shutdown deallocate the VM? The built-in Auto-shutdown schedule on a VM deallocates it, so it does stop the compute bill on the hours you set. A shutdown triggered from inside the guest OS leaves the VM Stopped (allocated) and billing, so confirm which one your automation actually runs.

How OhChimp approaches this

Doing this by hand works. The catch is that stopped VMs hide in subscriptions and resource groups nobody checks, and the ones costing the most are the ones everyone forgot.

OhChimp scans your Azure subscriptions, finds the VMs sitting in Stopped (allocated), and writes a reviewable plan that names the monthly compute you are wasting, with a confidence score, a risk level, and the rollback spelled out. The public IP and temp-disk questions are answered per VM, so you read a recommendation instead of redoing the checks. You click apply, nothing moves until you do, and the saving is then measured against your real Azure bill.

See how the Azure connection works on the Azure integration page.