# GCP Spot VM Opportunity

> Spot migration is recommended for GCE VMs with dev/test names or labels, where an env=prod label is an absolute veto. The savings fraction is 1 − Spot/OnDemand from live tier rates within the [0.05, 0.92] band; Windows VMs, DR/standby names, and unpriced SKUs abstain.

Source: https://zop.dev/integrations/gcp/recommendations/gcp-spot-vm-opportunity
Updated: 2026-08-19

---

## The vetoes that keep Spot off a workload

- resource.Status: statusTerminated
- env tag (env/environment/stage/tier): isProdEnvTag is an authoritative override (never recommend Spot for a prod-labelled workload, even if the name contains a dev/test substring); non-prod is established by isDevTestEnvTag OR the name heuristic (item T0282).
- resource.Name: dev/test pattern (dev, test, qa, staging, sandbox), now a secondary rather than the sole signal.
- critical-role name veto (#1906 B2): matchesCriticalRole skips DR / standby VMs (criticalRolePatterns = dr, standby, segment-boundary regex in gcp/spot_vm.go); a preemption during failover is a production outage.
- OS/license veto (#1906 B4): isSpotIncompatibleOS skips Windows / SQL-Server VMs (spotIncompatibleOSFamilies = windows, sql-server); the canonical GCP key is Metadata["osType"] (boot-disk license scan; windows fires on real data, while sql-server is inert on GCP today, retained as a forward-compat alias), with os_family/platform as cross-provider aliases; an absent OS signal abstains. Already-Spot guard reads Metadata["lifecycle"] (spot/preemptible) and Metadata["spot"]=="true" (the old hasTag(Tags,…) was a dead contract, since the GCP discoverer writes Metadata, not Tags). Declares Mechanism()="spot" → covered by cross-rec exclusivity.
- RC-114 (GKE Spot Node Pool) is intentionally not changed (#1906): a GKE node pool is structurally safe for Spot (the cluster autoscaler reschedules preempted pods across the pool), so it needs neither the critical-role nor the OS veto.

## A Spot fraction from live tier rates

concrete-or-abstain (cost-driven discount). The Spot fraction is derived only from live tier rates: spotFractionFromTiers = 1 − Spot/OnDemand, gated to the [0.05, 0.92] sanity band with a rate-inversion guard. savings = cost × fraction. RC-111 fires on a dev/test name (or env tag) signal with no observed utilization behind it, so the prior flat ~0.70 baseline fallback is removed. When no in-band live Spot rate exists, spotFractionFromTiers returns ok=false and the rule abstains (returns nil), never an advisory $0 or a fabricated fraction. Equivalence pin (Evaluate == EvaluateWithContext(...,nil)) preserved. (Was previously documented with a 0.70 baseline fallback.)

## Confirming the workload tolerates interruption

1. Verify this is a non-production workload tolerant of interruptions
