Always-on Azure VMs with 60+ days of steady use and no reservation or savings plan
What does ZopNight detect here?
ZopNight flags a running Azure VM that is production-named or tagged, or has simply never been stopped, when it has at least 60 days of history, at least 70% uptime and average `Percentage CPU` of 30% or more, yet no reservation or savings plan covers it. A 1-year Reserved VM Instance for that size and region is the suggested fix.
Signal and threshold
| Field | Value |
|---|---|
| Rule IDs | RC-211 |
| Category | discount |
| Severity | low |
| Metric | Percentage CPU |
| Threshold | 60+ days history, 70%+ uptime, 30%+ average CPU |
| Source | ZopNight |
| Permissions used | Microsoft.Compute/virtualMachines/read · Microsoft.Insights/Metrics/Read · Microsoft.Capacity/reservationorders/read |
Where it applies
How a Reserved VM Instance discounts a running VM
A Reserved VM Instance is a one- or three-year commitment to a VM size in a region. You do not attach it to a VM: the discount lands automatically on running VMs that match the reservation’s scope and attributes, and with instance size flexibility (on by default) it can spread across sizes in the same size group. It covers compute only. For Windows VMs the license part of the meter is billed separately and is not included.
The catch is that a reservation is use-it-or-lose-it: any hour without a matching running VM is lost and cannot be carried forward. That is why the candidates worth reserving are the VMs that are on nearly all the time.
Listing running VMs to size a reservation
az vm list -d \ --query "[?powerState=='VM running'].{name:name, rg:resourceGroup, size:hardwareProfile.vmSize, location:location}" \ -o tableGroup by size and region. Microsoft advises checking daily usage in the usage file, and using
the AdditionalInfo field rather than meter names to read the true VM size.
Evidence a VM must show
- The VM is running.
- No reservation or savings plan already covers it, and it has no
reserved=truetag. - It is not a Databricks-managed VM and not a member of a scale set.
- Either a production signal (a
prod,production,prdorlivename, or a production environment tag), or a record of running continuously: no stop events and a name that does not look like dev or test. - At least 60 days of cost history, at least 70% measured uptime, and a measured utilization signal, in practice average CPU, of at least 30%. A VM below that level is treated as a rightsizing candidate first, not as a commitment.
When ZopNight holds back
Missing history, unknown uptime or no CPU data all mean no finding; the check fails closed because the downside of a wrong answer is a year of billing. If the break-even comparison shows the reservation costing as much as or more than the VM costs now, ZopNight stays silent too.
Break-even saving on measured hours
current month = what the VM is billed now, for the hours it actually ranreserved month = 1-year reserved hourly rate x 730saving = current month - reserved monthBecause the current cost already reflects the hours the VM ran, a VM that is off part of the month is not credited with a full-time discount. The 1-year term is the smaller commitment; a 3-year term is priced lower again. Without those rates, ZopNight falls back to the current cost times a discount derived from the rate tiers, provided uptime is still high enough.
Buying and checking the reservation
- Confirm the VM will run at this size for at least 12 months.
- In the portal, open Reservations, select Add and Virtual machine, and pick the size, region, scope and 1-year term.
- Leave instance size flexibility on unless you need the discount pinned to one size.
- A few days later, check utilization under Reservations; the
az consumption reservation summary list --grain daily --reservation-order-id <id>command returns the same data.