# GCP VM Non-Production Scheduling

> Dev/test GCE instances commonly sit idle overnight and on weekends yet bill for 168 hours a week. ZopNight builds a weekly usage heatmap per VM, prices only the hours the suggested cron actually removes, and vetoes production, DR/standby, and stateful database VMs before ever proposing a stop.

Source: https://zop.dev/integrations/gcp/recommendations/gcp-vm-non-production-scheduling
Updated: 2026-08-19

---

## 168 hours billed, 45 used

A development VM used weekdays 9-to-6 is busy about 45 of the 168 hours it bills each
week. The rest is pure idle spend, and unlike rightsizing there is nothing to migrate.
Stopping and starting on a schedule recovers it with the machine unchanged. Candidates
are running instances whose name or environment label reads dev, test, qa, staging, or
sandbox; the env label is read from the nested labels object the GCP discoverer stamps,
since GCP resources carry no separate tag sink.

## Four vetoes run before any math

Production wins over everything: a prod name or an env=prod label ends evaluation
immediately. So does a critical-role name (DR, standby, backup, failover, replica),
because a standby stopped overnight is a standby that fails its one job. Stateful
data-service VMs (self-managed databases, Kafka, Elasticsearch, Redis) are vetoed too; a
forced stop of a single-replica stateful service is an outage, not a saving. Finally, a
`schedule` label on the VM is an explicit customer opt-out the rule honours silently.

## Priced on the prescription, not the diagnosis

The saving is not "idle percentage times cost". The heatmap yields a concrete cron
window, and the rule prices only the currently-running hours that window would actually
remove. Hours the team already keeps the box off contribute nothing, and a near-100%
idle VM is refused outright because that is an idle/delete case wearing a schedule
costume. Stops and starts execute through `compute.instances.stop` and
`compute.instances.start` in `roles/compute.instanceAdmin.v1`.

## Spot the always-on dev fleet

```bash
gcloud compute instances list \
  --filter="status=RUNNING AND name~'(dev|test|qa|staging|sandbox)'" \
  --format="table(name,zone,machineType.basename(),labels.schedule)"
```

Anything listed with an empty schedule column is what this rule evaluates.

## What produces no schedule

Terminated instances, any veto above, unknown or zero pricing, a VM with no
heatmap-derived idle measurement, an empty cron in the derived window, or a measured idle
fraction at the near-total ceiling. The old fixed ~67% assumption is gone; every
percentage shown was measured on that specific machine.
