Dev/test VM scheduling patterns that actually stick

2026-07-18 · ~6 min read · by the Just Software engineering team

Everyone agrees dev and test VMs shouldn't run around the clock. The reason so many still do isn't ignorance — it's that the first scheduling attempt annoyed somebody (a VM stopped mid-debug, a demo box down when sales needed it) and got rolled back. Scheduling that sticks is a design problem: matching the pattern to how people actually work, and giving them an escape hatch. This guide covers the patterns, the implementation options from built-in to bought, and the savings math with the caveats included.

First, the units

A month averages about 730 hours. Every pattern below is best understood as "what fraction of 730 does this VM run?", because compute savings scale linearly with hours removed. One fact to anchor everything: only a deallocated VM stops billing for compute. A VM shut down from inside the OS shows "stopped" and continues to bill; scheduling solutions must deallocate. And deallocation stops the compute meter only — disks and static public IPs bill regardless, so a scheduled VM's total cost never falls quite as far as its hours do.

Pattern 1: office hours

The workhorse. Start at 07:00, deallocate at 19:00, weekdays only. That's 12 × 21 ≈ 260 hours of a 730-hour month — a 64% reduction in compute hours. Tighten to 8-hour days and skip the padding and it's nearer 75%; most teams deliberately pad the window so early birds and late finishers never notice the schedule exists, and 60–65% is the honest planning number.

Where it fits: the general dev/test population — individual dev boxes, test environments used during the working day, internal tooling. Where it breaks: anything consumed outside the window (overnight test runs, CI agents, databases queried by other time zones) and any team that isn't actually in one time zone — which leads to the next pattern.

Design notes that separate schedules that stick from schedules that get disabled:

  • Warn before stopping. A stop that lands mid-task without warning is how scheduling projects die. Even a simple notification a few minutes ahead changes the experience completely.
  • Provide an override. "Keep this VM up tonight" must be a self-service, one-click action, not a ticket. The override should expire automatically — tonight, not forever — or overrides become the new always-on.
  • Order matters for multi-VM environments. An app tier that boots before its database tier starts broken. If environments have dependencies, stops and starts need sequencing, which is where DIY scripts start growing complexity.

Pattern 2: follow the team

For distributed teams, one window is wrong for everyone. The pattern: group VMs by the team (and time zone) that uses them, and run each group on its own local office hours. A VM set used by a team in Copenhagen and a team in Singapore might run two windows — or one long window if the sets overlap — but the point is the schedule derives from usage, not headquarters time.

The savings are the same per-window math; the cost is administrative: you need to know who uses what, which in practice means tagging (schedule:weekdays-cet-7-19 or a named schedule group). This is also the pattern where a scheduling product's UI earns its keep over a pile of cron expressions, because ownership and exceptions get complicated exactly here.

Pattern 3: on-demand with auto-shutdown

For infrequently used VMs — demo environments, the quarterly-report box, reproduction environments for support — even office hours is 260 hours of mostly-idle. The better pattern inverts control: the VM is off by default, users start it when needed (portal, a script, a chat command, or a self-service page), and an idle or fixed-duration shutdown reclaims it a few hours later.

Utilisation drops to actual usage — often 10–40 hours a month, a 95%+ compute reduction on those machines. The trade-off is the start-up wait (a couple of minutes) and the requirement that starting is genuinely easy for the people who need it; if starting the demo box needs an engineer, the demo box will end up always-on again within a quarter.

Implementation options

Azure built-in auto-shutdown. Every VM has an auto-shutdown setting: one daily stop time, with optional notification. It's free, takes thirty seconds, and is stop-only — no start, no weekend/weekday distinction, no sequencing. Perfect for pattern 3's reclaim step and as a safety net; insufficient alone for office-hours, because mornings become a manual chore that everyone resents by week two.

Azure DevTest Labs bakes in scheduled start/stop and per-user quotas, but requires adopting the Labs model for those VMs — a good fit for genuinely lab-shaped workloads, less so for retrofitting an existing estate.

DIY with Automation, Functions or Logic Apps. Runbooks or timer-triggered functions calling the start/deallocate APIs, driven by tags (autoschedule:weekdays-7-19). Microsoft has shipped versions of this as the Start/Stop VMs solution over the years. DIY is flexible and cheap and the first version is a pleasant afternoon; the hidden cost is that the backlog above — notifications, self-service overrides with expiry, dependency ordering, holiday calendars, a view of what's scheduled and what actually happened — arrives one user complaint at a time, and you end up maintaining a small internal product. Teams with platform-engineering capacity do this well; teams without it usually shouldn't start.

Scheduling products. A category of tools (Azure-focused schedulers and multi-cloud FinOps suites alike) that provide the whole backlog off the shelf: calendars, time zones, overrides, sequencing, notifications, reporting. The evaluation question is simple arithmetic — the product's cost against the incremental savings and the DIY maintenance you avoid — plus one softer criterion that decides long-term success: how good the exception experience is for end users, because exceptions are where schedules survive or die.

Whatever the mechanism, drive it with tags rather than hardcoded VM lists. New VMs then join the schedule by being tagged (enforceable with Azure Policy), instead of joining the untracked always-on population by default.

The savings math, honestly

Worked example: 20 dev/test VMs averaging €150/month in compute each, plus €25/month each in disks — €3,500/month all-in.

  • Office-hours schedule on all 20 at 64% compute reduction: saves 20 × €150 × 0.64 = €1,920/month. Disks are untouched, so the bill falls from €3,500 to €1,580 — a 55% total reduction, not 64%. That gap between compute savings and bill savings surprises people; quote the bill number.
  • Moving five of those to on-demand (they were demo/occasional boxes) at ~95% compute reduction adds roughly €230/month more.
  • Against these savings, price whatever runs the schedules (product licence, or a realistic hours-per-month estimate of DIY maintenance).

The other honest caveats: savings only accrue if the VMs would otherwise really have run 24/7 (check the current state before promising a percentage — some estates already have partial auto-shutdown); reserved-instance-covered VMs save nothing by being stopped (the reservation bills anyway — schedule pay-as-you-go VMs, reserve the always-on ones, and don't mix the strategies on the same machine); and non-VM PaaS (App Service plans, SQL databases) needs different levers, since most of it can't be "deallocated" the same way.

Rollout that survives contact with users

  1. Inventory and tag candidate VMs; confirm with owners what usage actually looks like (five minutes per team, prevents 90% of incidents).
  2. Start with the pattern-3 easy wins — VMs everyone agrees are rarely used — to bank credibility and savings.
  3. Roll office-hours to one friendly team first, with notifications and overrides working before the first scheduled stop, then expand team by team.
  4. Report the savings monthly in currency, not percentages. A recurring "scheduling saved €2,100 this month" line is what keeps the practice funded, tagged, and enforced long after the project ends.

Scheduling is the rare optimisation that's both large and reversible — the worst realistic outcome is turning a VM back on. Sized against how little else on the bill offers 60% reductions for a day's work, it belongs at the top of nearly every small team's cost list.

An unhandled error has occurred. Reload 🗙