Your Dev Environment Works a 168-Hour Week
Your developers work about 50 hours a week. Their Azure VMs work 168. You pay for every one of those hours, and on most invoices we look at, nobody decided that on purpose.
Non-production running around the clock is the least glamorous line in Azure cost management, and one of the most consistently ignored. It isn't a design flaw or a licensing puzzle. It's a default nobody went back to change.
Because dev, test and UAT subscriptions rarely have a business owner watching them, the bill just sits there, month after month, looking normal.
Why it survives every cost review
Three reasons come up again and again in our reviews.
- Nobody owns it. Production has an owner because production has outages. Non-prod has a project code, a team that moved on, and a tag that says "dev" if you're lucky.
- The fix looks too easy to be the problem. People hunt for the clever saving, such as a reservation or a SKU change, while forty VMs idle through every weekend.
- The "fix" was already applied, and it doesn't work. Someone shut the servers down from inside Windows, or wrote a script that does, and everyone assumed it was handled.
The first trap: "Stopped" is not "off"
This is the most expensive misunderstanding in non-prod. When a VM is shut down from inside the guest operating system, or through the PowerOff API, Azure shows it as Stopped. It is still allocated to a host, and Microsoft's documentation lists that state as billed. Only Stopped (deallocated) releases the hardware and stops the compute charge.
So a team that diligently runs shutdown /s at 7pm every night may be saving almost nothing.
Even when a VM is deallocated, its managed disks and some networking keep billing. Deallocation stops the biggest meter, not all of them.
A worked example
The figures below are illustrative, not a client case. Take a mid-size organisation with 30 non-production VMs across dev, test and UAT, at an assumed pay-as-you-go compute rate of $0.45 an hour per VM (AUD). That rate is a round number for the arithmetic, not a quote: your region and SKU will differ.
- Running 24/7: 30 VMs × $0.45 × 730 hours a month = $9,855 a month in compute.
- Business hours only: 7am to 7pm, Monday to Friday, is 60 hours a week, or roughly 260 hours a month. 30 × $0.45 × 260 = $3,510 a month.
- Difference: $6,345 a month, or about $76,000 a year, from compute alone. Disks keep billing either way.
Now the realistic adjustments. Five of those VMs host overnight build agents or batch tests, so they stay on. Two are covered by a reservation (more on that below). On the remaining 23, the gap is about $4,860 a month.
That's still a meaningful number, and it shows why the arithmetic has to be done VM by VM rather than with a headline percentage. Moving from 168 hours to 60 removes roughly 64% of compute hours, but only for the VMs that can actually sleep.
The second trap: scheduling VMs you've already prepaid
Reservations and savings plans are hourly commitments. Microsoft describes the reservation discount as "use-it-or-lose-it": if no matching resource is running in that hour, the reserved hour is lost and can't be carried forward.
So switching off a reserved VM only saves money if another matching VM in the reservation's scope picks up the discount. If nothing does, utilisation drops, the commitment is wasted and the invoice doesn't move.
The working rule: schedule pay-as-you-go non-prod, commit to always-on production, and don't do both to the same workload. If reservations were sized for non-prod running 24/7, make the scheduling decision and the reservation decision together, usually at renewal.
What works on Azure
- VM auto-shutdown. The setting in each VM's Operations section deallocates on a daily schedule. It's the quickest fix, but it only stops. There is no matching start, so it suits VMs people start on demand.
- Start/Stop VMs v2. Starts and stops VMs on user-defined schedules across multiple subscriptions, using Azure Functions and Logic Apps. Microsoft has stated there will be no further development beyond keeping its components supported, so treat it as a stable tool rather than a growing one.
- Beyond VMs. An AKS cluster can be stopped outside hours, which stops its control plane and agent nodes. An App Service plan is charged by its pricing tier and instance count whether or not the apps are running, so stopping the app does nothing: scale the plan down instead. Azure SQL Database serverless can auto-pause when idle, and compute cost is zero while it's paused.
The FinOps Foundation describes workload optimisation as making sure resources "only run when needed". Non-prod scheduling is the most literal version of that idea, and usually the cheapest to deliver.
How to check your own estate
These checks are read-only until the last step.
1. Find the "Stopped" VMs that are still billing
In the Azure portal, open Virtual machines and look at the Status column. Filter or sort by it. Anything showing Stopped without the word deallocated is still billing for compute right now. You can check the same thing from the Azure CLI:
az vm list -d --query "[?powerState=='VM stopped'].{name:name, rg:resourceGroup}" -o table
2. Pull 30 days of non-prod compute
In Cost Management, open Cost analysis. Filter by your non-prod subscriptions or the environment tag, set Service name to Virtual Machines, and group by resource. Rank the top 20 VMs.
3. Cross-check against reservations
Open Reservations and check utilisation and which VM sizes each one covers. Any VM a reservation depends on comes off your scheduling list, or goes into the renewal conversation.
4. Agree hours with one team
Pick a single team, agree a window such as 7am to 7pm on weekdays, and apply auto-shutdown or Start/Stop v2. Measure for a month before rolling it out further.
5. Tag the exceptions
Anything that must stay on gets a tag with a reason and an owner. Exceptions without an owner aren't exceptions.
The point
None of this is clever. That's the point. The biggest non-prod savings come from deciding, deliberately, which hours you're willing to pay for, and confirming that "off" actually means off.
Sources
- States and billing status of Azure Virtual Machines (Microsoft Learn)
- How the Azure reservation discount is applied to virtual machines (Microsoft Learn)
- Start/Stop VMs v2 overview (Microsoft Learn)
- Stop and start an AKS cluster (Microsoft Learn)
- Plan and manage costs for App Service (Microsoft Learn)
- Serverless compute tier for Azure SQL Database (Microsoft Learn)
- FinOps Framework: Workload Optimization (FinOps Foundation)
- FinOps on Azure (Microsoft Azure)
- State of FinOps 2026 (Linux Foundation press release)
An independent view of your Azure spend. I sell no licences, managed services or remediation, so my findings have nothing attached to them.
Book a 30-minute call