GCP Custom Recommendations

This page explains how Mavvrik savings recommendations are generated for Google Cloud Platform (GCP). For each recommendation it shows the logic or criteria used to identify the resource, default thresholds or lookback periods where applicable, the Reason and Action the recommendation presents in-product, and the metrics involved.

The Reason and Action columns show the exact wording each recommendation displays in the product; text in {braces} marks where live values — utilization, sizes, thresholds, resource names — are filled in for the specific resource.

Thresholds and lookback periods are configurable per policy. Recommendations are intended to support review and decision-making — customers should validate workload ownership, business criticality, performance requirements, and retention policies before taking action. Recommendations never change customer cloud resources.

Savings Type

What it means

Idle

Resources that are provisioned but showing no meaningful activity — no connections, no traffic, or near-zero utilization over the lookback period. Strong candidates for stopping, pausing, archiving, or deleting, since they incur cost without supporting an active workload.

Rightsize

Resources that are in use but don't match their actual workload demand — oversized and underutilized, on a pricier machine family, disk type, tier or compute class than needed, or retaining data longer than the workload requires. Candidates for resizing, configuration, retention or tier changes, scheduling, or a more cost-effective commitment or licensing option, rather than being removed outright.

Orphan

Resources that are no longer attached to or supporting any other active resource — such as unattached persistent disks, reserved external IP addresses associated with nothing, or snapshots and clusters whose originating workload is long gone. These typically serve no function on their own and are strong candidates for deletion.

License

Resources that may be using a more expensive licensing model than necessary, such as a premium operating-system or Marketplace application licence billed on its own meter, when a no-cost equivalent image or a cheaper licence band would serve. Candidates for replacing the image or crossing below a licence band boundary, without changing or removing the workload.


GCP Idle Recommendations

GCP resources that may be unused, inactive, or no longer required based on utilization, connection activity, age, or attachment status.

Recommendation

Description

How Recommendation Is Generated

Reason

Action

Metrics

Compute Engine Instance Idle

Running instances with near-zero CPU and network should be reviewed for stop or deletion rather than resizing.

Mavvrik evaluates instance usage over a configurable lookback period, with 30 days used by default. A running instance is flagged as idle when the 95th percentile of Max CPU % is within 0%–5% (default) and network throughput p95 is at or below 1 Mbps. Requires at least 80% hourly telemetry coverage of the window and an instance older than the lookback. GPU/TPU-accelerated instances, GKE nodes and managed-instance-group members are excluded, and the instance must still be billing.

Compute Engine instance is idle - CPU p95 {cpu_p95}% and network p95 {net_mbps_p95} Mbps over the last {days} days ({coverage}% coverage).

Stop the idle instance, or delete it. Stop: Stop the Compute Engine instance. Terminate: Delete the instance and its auto-delete boot disk.

CPU % (95th pct of hourly Max) + network total Mbps (95th pct)

Persistent Disk Idle

Attached disks doing no read/write work on a running instance should be reviewed for deletion.

Mavvrik evaluates disk IO over a configurable lookback period, with 30 days used by default. A non-boot persistent disk attached to a RUNNING instance is flagged when its hourly IO 95th percentile is 0 ops (default band 0–0) and 0 MiB, and its single busiest hour stays at or below 3,600 operations. Requires at least 80% hourly telemetry coverage and a READY disk that is still billing; boot disks, local SSD scratch devices and disks on stopped instances are excluded. GCP refuses to delete an attached disk, so remediation is a snapshot-detach-delete sequence, and it is irreversible.

Persistent {disk_type} disk ({size_gb} GiB) is idle over {days} days - IO p95 {iops} ops/{mib} MiB, peak {peak_iops} ops, active {active_hours} of {observed_hours} hours ({coverage}% coverage).

Delete the idle persistent disk.

Disk read+write ops (95th pct) + read+write MiB (95th pct) + peak hourly ops + active hours

Persistent Disk Attached to Stopped Instance

Data disks still billing while attached to a long-stopped instance should be reviewed for reclamation.

A configuration / state check (no lookback) — the 30-day default is a dwell gate, not an observation window. A non-boot persistent disk is flagged when its instance is in the SHUTDOWN state, the recorded last-stop timestamp is at least 30 days old, the disk is READY, and the disk has exactly one attachment which is that instance. Boot disks, local SSD scratch devices, and instances in the transient STOPPING state are excluded, as are instances with no last-stop timestamp — unknown is never read as stopped forever. The disk must still carry billed cost.

Persistent {disk_type} disk ({size_gb} GB) is attached to a stopped instance - stopped {days_stopped} days, past the {days}-day dwell window. When autoDelete is set on the attachment, appends: Auto-delete is enabled on this attachment.

Snapshot then delete the data disk attached to the stopped instance.

N/A (instance power state, last-stop timestamp, and disk attachment configuration)

Cloud SQL Instance Idle

Database instances serving no client sessions should be reviewed for stopping or deletion.

Mavvrik evaluates Cloud SQL activity over a configurable lookback period, with 30 days used by default. An instance is flagged when peak client sessions across the whole window are 0 (default band 0–0) — PostgreSQL reads user-database client backends, other engines read network connections — corroborated by disk read-ops p95 at or below 100 and absolute CPU at or below 0.5 vCPU. There is no CPU percentage band: CPU is measured and reported but does not gate candidacy, which keeps this the exact complement of the Cloud SQL underutilized policy. Requires at least 80% hourly telemetry coverage, a RUNNABLE instance and a priceable tier; the client-connections series needs roughly 30 days of accumulated history before the gate is meaningful.

Cloud SQL is idle - no client workload over {days} days - peak {client backends | client connections} {peak} (median {median}), CPU p95 {cpu_pct} pct, disk r/w ops p95 {read_ops}/{write_ops}, memory p95 {mem_pct} pct, {hours}/{window_hours} hrs ({coverage} pct), on {tier} tier ({db_version}, {availability_type}).

Set activationPolicy to NEVER to stop the Cloud SQL instance, or delete it. Stop: Stop the instance by patching the activation policy to NEVER. Terminate: Export the instance to Cloud Storage, then delete it.

Peak client sessions + median connections + CPU % (95th pct) + disk read/write ops (95th pct) + memory % (95th pct)

AlloyDB Instance Idle

Serving database instances with zero connections and zero committed transactions should be reviewed for deletion.

Mavvrik evaluates AlloyDB activity over a configurable lookback period, with 14 days used by default — shortened from the usual 30 because the AlloyDB telemetry backfill does not yet reach further and a longer window drops every instance at the coverage gate. An instance is flagged only when both peak concurrent connections and total committed transactions are 0 across the window (both bands ship 0–0); either signal alone is defeated by a pooler holding idle sockets, or by a batch job committing without a long-lived connection. The thresholds carry no aggregation metadata — the reductions are fixed in the query (peak for connections, sum for transactions) and are not tunable. Requires at least 80% hourly coverage; the delete is irreversible, so the recommendation is advisory.

AlloyDB {instance_type} instance is idle - {vcpu} vCPU served no database work over {days} days, peak connections {peak_connections}, committed transactions {total_transactions}, across {observed_hours} observed hours.

Delete the idle AlloyDB instance to release its vCPU and memory.

Peak concurrent connections + total committed transactions + observed hours

Cloud Run Service Idle (Bills While Idle)

Cloud Run services kept warm by a min-instance floor or CPU-always-on, with no traffic, should be reviewed.

Mavvrik evaluates Cloud Run traffic over a configurable lookback period, with 30 days used by default. A service is flagged when the number of hours in which it served any request is 0 (default band 0–0) and CPU p95 stays at or below 5%. Only services that genuinely bill while idle are in scope — those setting a minimum-instance floor of 1 or more, or CPU-always-allocated — because a default request-billed, scale-to-zero service already costs nothing when quiet. Requires at least 80% of the window in observed instance-hours; Cloud Functions-managed services are excluded so their cost is not double-counted.

Cloud Run service is idle - no traffic in {hours_with_traffic} of {instance_hours} instance-hours over {days} days, CPU p95 {cpu_pct}%, memory p95 {mem_pct}% of {cpu_limit}/{memory_limit} limits, inbound {rx_bytes} bytes/hour, yet bills continuously via {idle_billing_cause}.

Remove the idle-billing configuration: {idle_billing_cause}. Reconfigure: Update the Cloud Run service to drop the minimum-instance floor or CPU-always-allocated setting. Terminate: Delete the Cloud Run service.

Hours with any request + CPU % (95th pct of hourly p95) + memory % (95th pct) + inbound bytes/hour + instance-hours

Internal Application Load Balancer Idle Proxy Floor

Internal application load balancers billing a three-proxy floor while processing almost no data should be reviewed for deletion.

Mavvrik evaluates billed meters over a configurable lookback period, with 60 days used by default, taken as 2 complete invoice months. An internal managed forwarding rule is flagged when it billed a Proxy Instance Charge in every complete month of the window and averaged at most 1 GiB per month of client data processing (default band 0–1 GiB). Because no hourly telemetry exists for forwarding rules at all, the evidence is billed quantity rather than utilization, and the minimum-age guard reads the configuration creation timestamp rather than the asset snapshot date. The policy is enrolled as active but ships inert, so it produces nothing until an operator turns it on.

Internal ALB forwarding rule is idle at the 3-proxy floor - {proxy_hours} proxy-hours ({instances_held} instances held), {dp_gib} GiB/month data processed in {invoice_month} over {months} months.

Delete the internal ALB forwarding rule, its target proxy, URL map and backend service.

N/A (billed data-processing GiB/month + Proxy Instance Charge hours; no telemetry exists for forwarding rules)

Bigtable Empty Instance

Bigtable instances holding essentially no data yet still billing provisioned nodes should be reviewed for deletion.

Mavvrik evaluates billed meters over a configurable lookback period, with 90 days used by default, taken as complete invoice months. An instance is flagged when its busiest month stored at most 1 GiB-month (default band 0–1) while billing provisioned Server Node hours in every month of the window, including the latest complete month. Bigtable has no asset or telemetry rows on this data plane, so the whole check runs on cost-meter shape; an instance not present for the full window is treated as too young to judge rather than flagged. The delete is irreversible, so the recommendation is advisory.

Bigtable instance is empty - billed {node_hours} provisioned node-hours in {invoice_month} with {stored_gib} GiB-month stored, unchanged for {months} complete invoice months.

Delete the empty Bigtable instance to stop its Server Node meter.

N/A (billed SSD/HDD storage GiB-month, Server Node hours, and months present in the cost meters)

BigQuery Cross-Region Dataset Replica Unqueried

Cross-region dataset replicas billing storage but never queried should be reviewed for removal.

Mavvrik evaluates billed meters over a configurable lookback period, with 90 days used by default, requiring 3 complete invoice months. A replica region is flagged when the dataset bills storage in 2 or more regions, the replica's busiest month scanned at most 0.01 TiB (default band 0–0.01), the replica's size stays within 10% of the primary (default band 0–10), and the primary scanned strictly more than the replica. Region is recovered from the SKU description because the dataset resource name carries none, and BigQuery has no collected asset row. It ships as review-and-confirm rather than a delete: a deliberate disaster-recovery replica produces an identical signal and no available column separates the two.

Dataset bills storage in {regions} regions; the replica holds {replica_gib} GiB ({divergence_pct} pct size match to primary) and scanned at most {replica_tib} TiB per month across {months} months, versus {primary_tib} TiB for the primary.

Remove the unqueried cross-region replica; the primary remains unaffected.

N/A (billed per-region Analysis bytes scanned and replica-vs-primary storage GiB)

BigQuery Reservation Idle Baseline

Reservations whose billed slot-hours are mostly unattributed should be reviewed for a lower baseline.

Mavvrik evaluates billed slot-hours over a configurable lookback period, with 90 days used by default, requiring 3 complete invoice months. A reservation is flagged when at least 60% of its billed slot-hours are unattributed in every complete month of the window — the band is a floor, not a ceiling — it is the only reservation in its project, and it is still billing. The replacement baseline is sized at twice the busiest measured month of attributed slot-hours, and the across-months reduction is deliberately not tunable. Parked for review of the reservation savings model; BigQuery has no collected asset row, so the whole check runs on cost meters.

BigQuery reservation was idle in every one of the last {months} invoice months - latest month billed {total_slot_hours} slot-hours with only {attributed_slot_hours} attributed ({idle_pct} pct idle, floor {floor_pct} pct), peak attributed {peak_slot_hours} slot-hours.

Lower the baseline slots on the reservation and let autoscale supply peak demand.

N/A (billed slot-hours split by attributed vs unattributed resource)

AlloyDB Non Production Backup Cadence

Daily non-production AlloyDB backups should be reviewed for a weekly cadence to cut backup storage.

A configuration and cost check over complete invoice months, with 30 days used by default. Backup objects whose names carry the scheduled shape are grouped into 7-day cadence buckets (default cadence band 0–7 days) per project, region and backup hour, and every object in a bucket except the newest is flagged as surplus. A schedule key is skipped entirely when its object count exceeds its distinct backup dates by more than 20% (default band 0–20), which indicates two clusters sharing one backup hour. Non-production-scoped and advisory: the policy ships disabled and inert, and the query carries no environment predicate of its own, so the tenant's non-production scope must be supplied through advanced settings before it is enabled.

AlloyDB backup is surplus on a non-production daily schedule under a {cadence_days} day cadence - {objects_in_bucket} backups in the bucket, rank {bucket_rank}, {excess_pct} pct schedule excess.

Change the automated backup cadence from daily to one backup every {cadence_days} days.

N/A (scheduled-backup name shape, objects per cadence bucket, and per-schedule-key object-vs-date excess)

GKE Autopilot Non-Production Off-Hours Parking

Non-production Autopilot clusters running around the clock should be reviewed for an off-hours parking schedule.

A configuration and cost check with no measured lookback — the 30-day default acts only as a cluster-age maturity guard. Every RUNNING Autopilot cluster older than the window is selected, and savings are modelled as billed cost minus the per-cluster Autopilot management fee, multiplied by a parking share of 48 hours per week across 40% of the Pod footprint by default, with 24 h/week at 10% as the low bound and 112 h/week at 70% as the high bound. Nothing in the query checks whether a schedule already exists or whether the cluster is non-production, so with no configured scope it selects every mature cluster. Non-production-scoped and destructive: it ships disabled and inert and must not be enabled until the tenant's non-production scope is written into advanced settings.

Non-production GKE Autopilot cluster has no off-hours schedule after {days} days - parking window {hours_per_week} hours per week, {pct_of_week} pct of the week.

Scale non-production workloads to zero replicas for {hours_per_week} hours per week. Cautious: Park one weekend day (24 h/week) on the 10 pct of the Pod footprint the team confirms first. Default: Park the weekend (48 h/week) on the 40 pct of the Pod footprint that is stoppable. Maximum: Park weeknights plus weekends (112 h/week) on 70 pct of the Pod footprint - only where nobody works evenings and no overnight batch or CI runs.

N/A (cluster state and age only; savings modelled from billed cost less the Autopilot management fee)

GCS Cold Class Transition Operation Storm

Buckets paying more in cold-class transition operations than in storage should be reviewed for a size condition.

A configuration and cost check over complete invoice months, with 90 days used by default — the latest complete month supplies every dollar figure and the earlier months supply the recurrence check. A bucket and cold-class pair is flagged when Class A operation cost is at least 3.0x its storage cost and objects average at most 8.0 MiB per operation (default band 0–8.0), and the class billed operations in at least 2 prior months so a completed one-time bulk migration is not flagged. The ratio band's upper bound is a sentinel assigned when storage cost is zero rather than a detection gate, so the band is effectively open above 3.0. Ships disabled and inert; savings net off the higher Standard at-rest cost of objects that would stop transitioning, and both remediations are advisory.

Cloud Storage bucket is dominated by {cold_class}-class operation churn - {op_count} Class A ops in {invoice_month}, a {ratio}:1 ops-to-storage ratio, {mib_per_op} MiB per op across {gib_month} GiB-month, recurring over {prior_months} prior months.

Add a sizeAboveBytes condition to the {cold_class} lifecycle rule so undersized objects stay in Standard. Lifecycle rule: Add a sizeAboveBytes condition so objects too small to repay their transition fee stay in Standard. Autoclass: Enable Autoclass on the bucket so transitions incur no operation charge and objects under 128 KiB never move.

N/A (billed Class A operation cost vs storage cost ratio, MiB per operation, and prior months with operations)

GCP Rightsize Recommendations

GCP resources that may be oversized, underutilized, on a pricier machine family, disk type, tier or compute class than needed, over-retained, or schedulable — candidates for a more cost-effective configuration rather than removal.

Recommendation

Description

How Recommendation Is Generated

Reason

Action

Metrics

Compute Engine Instance Rightsize

Instances that are in use but oversized should be reviewed for a smaller machine type.

Mavvrik evaluates utilization over a configurable lookback period, with 30 days used by default. A RUNNING instance is flagged when its 95th-percentile Max CPU is within 6%–60% (default) and at least 80% of the window's hours carry telemetry; Compute Engine publishes no memory metric at all, so there is no memory band. Up to three targets are offered — a same-family shape holding memory at or above the source, an N2-to-E2 same-shape swap, or a custom-shape vCPU cut — each keeping projected peak CPU under an 80% headroom guard, respecting a 2 vCPU floor and 8 GiB/vCPU cap, and beating the source by at least 5% of billed cost. GPU/TPU shapes, GKE nodes, local SSD, sole-tenant affinity, nested virtualization, pinned CPU platform and managed-instance-group members are excluded. Savings are an on-demand price ratio applied to actual billed cost, and the change needs a stop/start maintenance window.

Compute Engine instance is underutilized - CPU p95 {cpu_p95}% over {days} days on {vcpu} vCPU / {memory} GiB, {hours} hours sampled ({coverage}% coverage).

Resize from {source_sku} to {target_sku}.

CPU % (95th pct of hourly Max) + hourly coverage count

Compute Engine Custom Machine Shape Rightsize

Custom-shape instances running well below their vCPU count should be reviewed for a narrower shape.

Mavvrik evaluates utilization over a configurable lookback period, with 30 days used by default. A RUNNING custom-shape instance is flagged when its 95th-percentile Max CPU is within 6%–40% (default) with at least 80% hourly coverage. The target holds memory identical and cuts vCPU to the smallest even step that keeps projected peak under an 80% headroom guard and memory density at or below 8 GiB/vCPU, never below 2 vCPU; instances already on the extended-RAM SKU have no memory-preserving downsize and are dropped. Savings come from the instance's own billed custom-core and per-vCPU licence meters rather than a rate times 730, and the resize needs a stop/start maintenance window. Ships inert — Compute Engine rightsizing now has one owner, and custom-shape vCPU cuts are generated there as target options instead.

Compute Engine instance is underutilized for its custom shape - CPU p95 {cpu_p95}% over {days} days on {vcpu} vCPU / {memory_mb} MB; {target_vcpu} vCPU fits the {headroom}% headroom ceiling ({gib_per_vcpu} of {max_gib_per_vcpu} GiB/vCPU).

Resize from {instance_type} to {target_sku}.

CPU % (95th pct of hourly Max) + hourly coverage count

Compute Engine Same-Shape Machine Family Downshift

Lightly-used N2 instances should be reviewed for the same shape on the cheaper E2 family.

A pricing comparison with a light utilization gate; Mavvrik evaluates over a configurable lookback period, with 30 days used by default. A RUNNING N2 predefined-shape instance is flagged when its 95th-percentile Max CPU is above 40% and at or below 60% (default) and the E2 family bills at least 10% below N2 (default band 10%–100%) on the vCPU meter and the RAM meter independently, with rates derived from the tenant's own billed cost over quantity rather than any external price list. The target is the identical shape with the family prefix swapped — same vCPU, same memory — and it must match the published E2 shape set exactly or the row is dropped rather than approximated. Pinned CPU platform, GPUs, local SSD and managed-instance-group members are excluded because E2 cannot support them. Ships inert so the same instance is not surfaced by a second active rightsize rule.

Compute Engine instance is in a suboptimal machine family - CPU p95 {cpu_p95}% over {days} days ({coverage}% coverage); the {target_family} family bills {core_delta}% below {source_family} on vCPU and {ram_delta}% below on memory.

Resize from {instance_type} to {target_type}.

CPU % (95th pct of hourly Max) + billed vCPU/RAM rate deltas

Persistent Disk Over-Provisioned Tier (pd-ssd)

Disks whose IO sits well below their tier should be reviewed for a cheaper tier.

Mavvrik evaluates utilization over a configurable lookback period, with 30 days used by default. An in-use pd-ssd disk is flagged when its p99 IOPS and p99 throughput each sit within 0%–50% (default) of what pd-balanced would provide at the same provisioned capacity, and the window maximum fits under 100% of that envelope so a single month-end batch disqualifies the disk. Savings are the derived 41.2% capacity rate delta, not the whole disk cost. Both tiers are SSD-backed so the latency class is unchanged, but Compute Engine has no in-place disk type change — remediation is snapshot, re-create and re-attach in a maintenance window. Disks the Hyperdisk migration or the idle-disk policy would claim are subtracted, and the absolute figure is an upper bound because the cost basis prices disks at the default effective rate rather than the tenant's contract rate.

In-use pd-ssd disk ({size} GiB) fits pd-balanced over {days} days - IOPS p99/max {iops_p99}/{iops_max}, throughput p99/max {mibps_p99}/{mibps_max} MiB/s, coverage {hours}/{window_hours} hrs ({coverage}%).

Re-create the disk as pd-balanced capacity in a maintenance window.

IOPS + throughput (p99 and max) + hourly coverage count

Persistent Disk Over-Provisioned Tier (pd-balanced)

Non-boot pd-balanced disks with low, latency-tolerant IO should be reviewed for pd-standard.

Mavvrik evaluates utilization over a configurable lookback period, with 30 days used by default. A non-boot pd-balanced disk is flagged when its p99 IOPS and p99 throughput each sit within 0%–50% (default) of the pd-standard envelope at the same capacity and the window maximum fits under 100% of it; because that envelope is eight times tighter than pd-balanced, 50 here is a materially stricter test than the same number on the pd-ssd policy. This change loses the SSD latency class — pd-standard is HDD-backed and per-operation latency is roughly an order of magnitude worse — and the IO-count metrics behind the finding are blind to latency sensitivity, so it is advisory, requires human review, and must never be applied to a database. Only machine families verified to support pd-standard are considered, and any disk whose attachment family cannot be resolved is excluded rather than assumed compatible. Savings are the derived 60.0% capacity rate delta.

Non-boot pd-balanced disk ({size} GiB) fits pd-standard over {days} days - IOPS p99/max {iops_p99}/{iops_max}, throughput p99/max {mibps_p99}/{mibps_max} MiB/s, coverage {hours}/{window_hours} hrs ({coverage}%).

Re-create the disk as pd-standard capacity in a maintenance window.

IOPS + throughput (p99 and max) + hourly coverage count

Persistent Disk Generation Migration (Hyperdisk Balanced)

Non-boot persistent disks fitting the free Hyperdisk baseline should be reviewed for migration.

Mavvrik evaluates utilization over a configurable lookback period, with 30 days used by default. A non-boot persistent disk on an N2 or N2D attachment is flagged when its p99 IOPS and p99 throughput sit within 0%–50% (default) of the free Hyperdisk Balanced baseline and its window maximum fits under 100% of that flat 3,000 IOPS / 140 MiB/s baseline — which does not scale with capacity, so a peak above it after the move throttles unless performance is provisioned separately and billed. Aggregate demand is also checked per owning instance against the vCPU-tiered shared Hyperdisk limit, and every disk on a breaching instance is dropped rather than one arbitrary volume. This is a re-platform, not an in-place change: snapshot, create the replacement at default performance, then detach and attach in a maintenance window. Savings are the derived rate delta — 52.94% from pd-ssd, 20.00% from pd-balanced.

Non-boot {disk_type} disk ({size} GiB) fits the free Hyperdisk Balanced baseline over {days} days - IOPS p99/max {iops_p99}/{iops_max}, throughput p99/max {mibps_p99}/{mibps_max} MiB/s, coverage {hours}/{window_hours} hrs ({coverage}%).

Migrate the disk to Hyperdisk Balanced at default performance.

IOPS + throughput (p99 and max) + hourly coverage count

Oversized Data-Disk Clone Image Retained Past Migration

Oversized non-bootable clone images kept past their migration should be reviewed for deletion.

A configuration and reference-graph check (no utilization — a custom image publishes no telemetry, so age is the observation window). An image is flagged when it carries no OS licence, making it a non-bootable data-disk clone rather than a golden boot image; its compressed archive exceeds 100 GiB (default); it is older than the 90-day migration grace window; and between 1 and 2 (default) live disks descend from it, with every descendant already attached and the most recent attach itself older than 90 days. Widening the lookback widens the staleness gate and the descendant-attachment dwell together, since there is deliberately no separate minimum-age threshold. Images with zero descendants are out of scope because nothing proves their bytes survive elsewhere. Savings are the whole image storage charge, and the finding is advisory — image deletion is permanent.

Custom image is an oversized, non-bootable clone - {archive_gib} compressed GiB from a {disk_size} GB source disk, {age} days old, with {descendants} descendant disk(s) already restored, last attached {attach_age} days ago.

Delete the retained, non-bootable clone image.

N/A (OS-licence array, compressed archive size, creation timestamp, and live disks whose source image resolves to this image)

Cloud SQL Instance Underutilized

Instances that are in use but over-provisioned should be reviewed for a smaller tier.

Mavvrik evaluates utilization over a configurable lookback period, with 30 days used by default. A RUNNABLE instance with an ALWAYS activation policy is flagged when its 95th-percentile CPU is within 0%–40% and 95th-percentile memory within 0%–95% (default), at least 80% of the window's hours carry telemetry, and at least one live client connection is present. Memory is read from total resident usage including page cache rather than the reported utilization metric, which tracks tier-sized pre-allocated quota and would pass or fail every instance of a tier identically. Up to 4 legal smaller tiers are offered, each keeping projected CPU under an 80% headroom guard, holding the observed working set times a 1.3 buffer, and never falling below a read replica's primary. Savings are the compute share of the bill only; shared-core and legacy bundled tiers have no downsize target and are excluded. The gating client-connections metric needs roughly 30 days of history before the gate is meaningful.

Cloud SQL instance is over-provisioned - CPU p95 {cpu_pct} pct, memory p95 {mem_pct} pct ({mem_used}/{mem_total} GiB), {conns} peak conns, disk IOPS p95 {iops}, {hours}/{window_hours} hours over {days} days, on {vcpu} vCPU ({tier} tier, {availability_type}, {db_version}, {sql_instance_type}).

RAM only: Change the Cloud SQL tier from {tier} to {target_tier} (RAM only, {src_gib} to {tgt_gib} GiB). vCPU only: Change the Cloud SQL tier from {tier} to {target_tier} (vCPU only, {src_vcpu} to {tgt_vcpu}). Both: Change the Cloud SQL tier from {tier} to {target_tier} ({src_vcpu} to {tgt_vcpu} vCPU, {src_gib} to {tgt_gib} GiB RAM).

CPU % (95th pct) + memory % from total resident usage (95th pct) + peak connections + disk IOPS (95th pct)

AlloyDB Instance Underutilized

Instances doing real work on an oversized shape should be reviewed for fewer vCPU.

Mavvrik evaluates utilization over a configurable lookback period, with 14 days used by default — set there because the AlloyDB metric backfill is shallow and a 30-day window would drop every instance on the completeness gate. A READY serving instance above 2 vCPU is flagged when its 95th-percentile CPU is within 5%–50% (default) of provisioned vCPU with at least 80% hourly coverage. The memory threshold reads peak memory in use as a share of provisioned RAM, derived from the tightest available-memory moment in the window — a peak, not a p95 — and it ships wide open at 0–100, so it acts as a tunable knob for excluding memory-heavy instances rather than as a detection filter, because PostgreSQL fills RAM with buffer cache and peak use sits high even on instances running very low CPU. Memory safety comes from target selection instead: the recommendation is the smallest position on the 2/4/8/16/32/64 vCPU ladder, at 8 GiB per vCPU, that clears both the CPU p95 and the smaller of observed peak memory or database size plus 4 GiB of overhead, each carried with 30% headroom.

AlloyDB {instance_type} instance is over-provisioned - CPU p95 {cpu_pct} pct over {days} days on {vcpu} vCPU / {ram} GiB, peak memory {peak_gib} GiB ({peak_pct} pct), {db_gib} GiB database, {connections} connections, {transactions} transactions, {observed_hours} observed hours.

Resize the AlloyDB instance from {vcpu} to {target_vcpu} vCPU ({instance_type}).

CPU % (95th pct of hourly Max) + peak memory in use % (from minimum available memory) + database size + peak connections + transaction total

AlloyDB Backup Retention Rightsize

Backups kept past their target retention should be reviewed for a shorter schedule.

A billed-hours retention check (no utilization). Mavvrik reads complete invoice months over a configurable window, with 60 days used by default, which resolves to the last complete month plus the one before it on most days. A backup object is flagged when its realized retention — measured from the earliest hour of its own billing window to the latest metered hour — runs past the 7-day (default) target, and the hours billed beyond that age become the savings share. AlloyDB backups are incremental, so the headline applies a conservative 50% chain-carry-forward haircut while the optimal figure keeps the meter-isolated upper bound; objects whose age origin lands on the first day of the earliest scanned month are dropped as left-censored. Advisory: retention can be raised again immediately, but a backup already expired under the shorter retention cannot be recovered, so the schedule should be changed rather than existing backups bulk-deleted.

AlloyDB backup is over-retained - kept {realized_days} days against a {target_days} day target, {beyond_share} pct of metered hours past the target age.

Lower the AlloyDB backup retention from {realized_days} to {target_days} days.

N/A (billed backup-storage hours from per-object usage start and end times)

AlloyDB Non-Production Cross-Region Secondary Cluster

Non-production cross-region secondary clusters should be reviewed for deletion.

A configuration and cost check (no utilization). Mavvrik reads the latest complete invoice month, which a 30-day window resolves to on every day of the month, and only cluster-grain meters are read so no vCPU or RAM charge can enter the claim. A candidate is corroborated as a real cross-region DR replica only when a same-named non-replica cluster is billing in the same account and a different region — the closest-storage primary is chosen as the match, storage parity must be within 0%–15% (default) of the larger of the two, and an unpaired candidate is dropped as a naming coincidence. Deleting the replica removes the non-production cross-region failover path and breaks anything reading from the replica region; recreation re-seeds a full copy of storage, so it is treated as hard to reverse and ships advisory-only. Non-production-scoped: ships disabled and inert, and the replica classifier must be supplied through advanced settings before the policy selects anything, because which resources are non-production is the customer's definition.

AlloyDB cluster is a cross-region secondary replica of a primary in another region - storage parity {parity} pct, replica holds {storage_tib} TiB of continuous-backup storage in {invoice_month}.

Delete the secondary AlloyDB cluster and rely on automated backups from the primary in the other region instead.

N/A (billed cluster-storage and PITR-log meters plus a cross-region same-name pairing test)

AlloyDB PITR Window Above Free Tier

Clusters billing recovery logs past the free tier should be reviewed for a shorter window.

A configuration and cost check (no utilization). Mavvrik reads complete invoice months over a configurable window, with 30 days used by default, and only cluster-grain meters are read so instance compute cannot enter the claim. A cluster is flagged when its point-in-time-recovery log meter is non-zero and it is billing at both edges of the window — a billable PITR log is itself proof the recovery window runs past AlloyDB's free first 7 days, which is a product fact rather than a tunable. The recommended window is 7 days (default, capped there because a lower value saves nothing extra and only shrinks the recovery surface), and the whole meter is claimed at a savings fraction of 1.0, lowerable to 0.4 for a conservative figure. Shortening the window permanently discards every recovery point in the interim, so the finding is advisory. Non-production-scoped: ships disabled and inert pending an advanced-settings scope.

AlloyDB cluster retains {pitr_gib_hours} GiB-hours of point-in-time-recovery logs past the {free_tier_days}-day free tier in {invoice_month} - {pitr_share} pct of its cluster-grain storage meter.

Set the continuous-backup recovery window on the AlloyDB cluster to {target_days} days.

N/A (billed point-in-time-recovery log cost and GiB-hours against the cluster's own charge)

AlloyDB PITR Window On Write-Churn Production Primaries

Write-churn production primaries should be reviewed for a shorter recovery window.

A configuration and cost check (no utilization). Mavvrik reads complete invoice months over a configurable window, with 30 days used by default, which is also the minimum cluster age. A cluster is flagged when its retained PITR log volume is at least 100% of its stored database volume (the band's high end is an open sentinel, and 100 sits inside an observed empty band, so any value in the 63–116 range selects the same population), it bills at both window edges, and it has live serving instances — requiring real instance compute keeps this disjoint from the orphan-cluster policy. The recommended window is 10 days (default, safe range 7–13), keeping a 3-day chargeable tail above the free 7 days and roughly halving the log meter; the headline claim is that partial reduction while the optimal figure is the whole meter. This shortens a recovery window on production databases and removes recovery capability permanently for the discarded interval, so it is advisory and sign-off-gated. Ships disabled and inert; the cluster's current billable window has no shipped default and must be supplied through advanced settings from the real cluster configuration.

Production AlloyDB primary retains {pitr_gib_hours} GiB-hours of point-in-time-recovery logs against {storage_gib_hours} GiB-hours stored - write-churn ratio {churn_pct} pct over a {window_days}-day window, {billable_days} days past the {free_tier_days}-day free tier.

Set the continuous-backup recovery window on the production AlloyDB cluster to {target_days} days.

N/A (billed PITR-log GiB-hours as a ratio of billed cluster-storage GiB-hours)

Bigtable Non-Production Node Floor

Non-production instances sitting just above the one-node floor should be reviewed for a node cut.

A configuration and billed-quantity check (no utilization — no Bigtable metric is collected and there are no asset rows either, so the evidence is provisioned node-hours against stored bytes). Mavvrik reads complete invoice months over a configurable window, with 90 days used by default, resolving to the last three complete months, and the instance must be present in every month. It is flagged when its minimum monthly node-equivalent is at least 1.9 (default; the billing export lands real two-node instances just under 2.0, so a 2.0 floor would miss all of them), its minimum stored data is above 1 GiB-month while its peak stays at or below 1024 GiB-month, and its node-equivalent spread across the window is at most 0.1 — a varying profile means the cluster is already responding to load. A Bigtable cluster cannot be stopped, paused or scaled below one node, so one node is the hard floor. Cutting to the floor halves read and write throughput but loses no data; deleting a replica cluster permanently destroys that copy and the multi-cluster availability posture. Non-production-scoped: ships disabled and inert.

Bigtable instance holds {node_equiv} node-equivalents (spread {spread}) and {min_stored}-{max_stored} GiB-month stored, steady across {months} complete invoice months above the {floor}-node floor.

Lower the Bigtable instance to {target_nodes} node-equivalent by reducing serveNodes on its cluster. OR Remove the redundant non-production replica cluster from the Bigtable instance.

N/A (billed Server Node node-hours converted to node-equivalents, plus SSD/HDD storage GiB-month)

Cloud Run Service Underutilized

Services running well below their declared CPU and memory limits should be reviewed for smaller limits.

Mavvrik evaluates utilization over a configurable lookback period, with 14 days used by default. A service is flagged when its 95th-percentile CPU is within 0%–40% and 95th-percentile memory within 0%–65% (default) — both as fractions of the configured container limits — at least 80% of the window's hours carry telemetry, and live request traffic is present. Because the metric only exists for hours a container instance was actually running, that completeness gate doubles as an effectively-always-on filter. Targets come from the legal CPU set and binary memory steps, must respect both directions of Cloud Run's CPU-memory coupling, and must absorb projected peak under an 80% headroom guard with an extra 1.3 memory buffer, because an undersized memory limit terminates the container rather than slowing it. Services with more than one container, a traffic split, or traffic pinned to a non-latest revision are excluded. Savings take whichever CPU/memory share endpoint minimises the claim, so the figure is a lower bound.

Cloud Run service is underutilized - CPU p95 {cpu_p95}%, memory p95 {mem_p95}% over {days} days ({hours} hours sampled, {requests} requests).

Resize from cpu={src_cpu}, memory={src_memory} to {target_sku}.

CPU % + memory % (both 95th pct of hourly p95, as a share of the configured limit) + request count

Cloud Run Bursty Service Over-Declared Limits

Bursty services with over-declared limits should be reviewed for smaller limits.

Mavvrik evaluates utilization over a configurable lookback period, with 30 days used by default. A service is flagged when a container instance ran in at least 24 distinct hours (default floor, safe range 8–168) but in fewer than 80% of the window's hours — at or above that coverage the service is effectively always-on and belongs to the Cloud Run underutilized policy instead — traffic was present in at least one of those hours, and its 95th-percentile CPU is within 0%–40% with peak memory within 0%–65% (default). Because coverage is sparse by design, memory is read as the maximum of the hourly 95th percentiles rather than a percentile of them, and the target must absorb that observed peak under an 80% headroom guard with a 1.3 memory buffer, on the same legal lattice and coupling rules as the always-on sibling. The coverage constant is pinned and must equal the sibling's completeness gate, or the two populations overlap or leave a hole between them.

Cloud Run service is bursty - CPU p95 {cpu_p95}%, peak memory {mem_peak}% over {observed_hours} observed hours, traffic in {request_hours} of them ({requests} requests), over {days} days.

Resize from cpu={src_cpu}, memory={src_memory} to {target_sku}.

CPU % (95th pct of hourly p95) + peak memory % (max of hourly p95) + observed hours + request hours

GKE Autopilot Premium Compute Class Rate Premium

GKE Autopilot clusters paying a premium compute-class rate over general-purpose should be reviewed.

Mavvrik averages the per-region, per-dimension effective Autopilot Pod rates over a configurable lookback period, with 30 days used by default — the single last complete invoice month. A cluster is flagged when a premium compute class (Scale-Out, Balanced, Performance or Accelerator) bills at 1.15x or more the general-purpose rate for the same region and billed dimension; general-purpose can never flag itself, since its multiple against itself is exactly 1.0. Only Pod mCPU and Pod Memory request meters are read — the Autopilot management fee and ephemeral storage are excluded because compute class does not reprice them — and Spot/preemptible meters are removed so they cannot drag the general-purpose baseline down. The recoverable premium is always priced on exactly one month of premium-class spend, so widening the window cannot inflate savings; there is no cost floor, and the delta is arithmetic on two rates the tenant already pays rather than a modelled discount.

GKE Autopilot cluster runs Pod mCPU and Pod Memory requests on premium compute class {premium_classes} - {min_rate_multiple}x to {max_rate_multiple}x the general-purpose rate across {premium_group_count} meter groups over {months_observed} months.

Set the compute-class selector to general-purpose where the premium class is not required.

Premium-vs-general-purpose rate multiple (cost/quantity per class), premium-class meter cost

GKE Autopilot Non-Production Spot Pods

Non-production GKE Autopilot clusters running standard rather than Spot Pods should be reviewed.

Mavvrik evaluates non-production Autopilot Pod request spend over a configurable lookback period, with 30 days used by default. There is no threshold block at all — every knob is a constant: 25% of the Pod footprint is modelled as Spot-adoptable (10% as the cautious bound), priced at the documented 60% Spot discount floor with a 91% ceiling. Savings are therefore a modelled factor, not a measurement, and the row carries all three bounds. Environment is the discriminator, but the query applies no environment predicate of its own, so the claim rests entirely on injected scope; the policy ships disabled and inert until the tenant's non-production scope is written into advanced settings. What is given up is the Autopilot SLA: a Spot Pod can be evicted at any time with a 15-second grace period.

Non-production GKE Autopilot cluster runs Pod mCPU, Pod Memory and Pod Ephemeral Storage requests on standard rather than Spot Pods over the last {days} days; {spot_eligible_pct} pct is modelled as Spot-adoptable.

Set the Spot nodeSelector on the eviction-tolerant Deployments and Jobs, and redeploy.

N/A (billed Autopilot Pod request meters only; no cluster or Pod telemetry exists)

Dataproc Serverless Batch Cost Regression Against Its Own Schedule Cohort

Dataproc Serverless batches consuming far more compute than their own schedule cohort should be reviewed.

Mavvrik evaluates the last 30 days — exactly the last complete invoice month, the grain a cohort percentile needs. A cohort is (project/location, logical job, submit hour); a run is flagged when it consumed at least 3.0x its cohort median and at least 0.5 Data Compute Unit hours, inside a cohort of at least 8 runs. Savings are the excess over the cohort's own 75th percentile, with the 90th percentile as the conservative bound and the median as an explicitly non-bookable ceiling. The 0.5 DCU-hour floor doubles as the disjointness boundary against the minimum-charge sibling, which owns the 0.1967 DCU-hour population. Ships disabled and inert: the cohort key uses the full batch resource name and the submit-slot and submit-date fields resolve to nothing, so a tenant batch-naming parser must be supplied through advanced settings before any cohort forms.

Dataproc Serverless batch cohort has {flagged_run_count} of {cohort_runs} runs above {deviation_multiple}x the cohort median - over {distinct_submit_days} submit days, median {p50_dcu_hours} DCU-hours, p{baseline_percentile} {baseline_dcu_hours}, worst {worst_run_dcu_hours} at {worst_deviation_multiple}x.

Profile the flagged runs in the Spark UI, then repartition the skewed key or cap the dynamic-allocation executor ceiling.

Cohort run count, per-run DCU-hours, deviation vs cohort median, p50/p75/p90 cohort DCU-hours

Dataproc Serverless Batch Submissions Billing Only The Minimum Charge

Recurring Dataproc Serverless jobs billing only the one-minute minimum charge should be reviewed.

Mavvrik evaluates the last 30 days — exactly the last complete invoice month, the grain a run-count and run-share gate needs. A batch counts as a no-op when its entire billed footprint falls between 0.19 and 0.25 Data Compute Unit hours; the physical floor is 12 DCUs at the one-minute minimum, or 0.1967 DCU-hours, and the 0.19 lower bound exists to exclude month-boundary fragments that bill below physics. A job family is reported when at least 20 of its runs are no-ops and those are at least 90% of its runs, so the remedy is a scheduler change rather than a per-run action. 90% of the no-op spend is modelled as recoverable (50% as the conservative bound), holding back the one guarded or consolidated run per family expected to survive. Ships disabled and inert: the family key is the full per-run batch resource name, so a tenant batch-naming parser must be supplied through advanced settings before families group.

Dataproc Serverless batch family hits the one-minute minimum charge on {noop_runs} of {family_runs} monthly batches ({noop_run_share_pct} pct) - DCU-hours {min_noop_dcu_hours}-{max_noop_dcu_hours} against the 0.1967 floor.

Add an empty-input guard to the scheduler, or collapse the {noop_runs} per-table submissions into one multi-table batch.

No-op run count and share per family, per-run DCU-hours against the 0.1967 floor

GCS Bucket Cost Dominated By API Operations

Buckets whose Cloud Storage bill is mostly API operations rather than stored bytes should be reviewed.

Mavvrik evaluates the last 30 days — exactly the last complete invoice month, since a part-month operation meter over a part-month bucket total is meaningless. A bucket is flagged when Standard-class API operations are at least 80% of its whole Cloud Storage bill and it runs at least 10,000 Class A write and list calls per GiB-month stored. Nearline, Coldline and Archive transition SKUs are excluded at meter grain, and any bucket the cold-class transition policy would itself claim is left entirely to that policy. Savings are a modelled reduction factor rather than a measurement, because no available table exposes the current poll interval: 50% as the point estimate, 25% conservative, 90% as the ceiling matching a 30-to-300-second interval change. There is no cost floor.

Cloud Storage bucket is operation-dominated - operation share {op_share_pct} pct in {invoice_month} on only {stored_gib_month} GiB-month stored, {class_a_ops} Class A ops = {class_a_ops_per_gib} per GiB.

Reduce the Class A call rate at its source - lengthen the scheduler poll interval, or batch the writes.

Operation cost share of bucket bill, Class A operations per GiB stored, stored GiB-month

GCS Early Delete Penalty From Retention Below The Class Minimum

Buckets billing an early-delete penalty because objects are removed before the class minimum should be reviewed.

Mavvrik evaluates a configurable lookback period, with 90 days used by default — the latest complete invoice month supplies every scored figure and the earlier months answer whether the penalty is a one-off or standing. A configuration and cost check with no thresholds at all: GCP bills the early-delete SKU only when an object was deleted, replaced or rewritten before its class minimum, so its presence is the finding and the whole penalty is recoverable. The breached minimum is read from the class named in the SKU — 30 days Nearline, 90 Coldline, 365 Archive. There is deliberately no materiality or share floor, since low-share/high-dollar and high-share/low-dollar penalties are the same misconfiguration; buckets either cold-class sibling would claim are dropped. The lever is a retention or lifecycle-timing change, and the trade is that the objects must live out the full minimum duration.

Cloud Storage bucket incurred an early-delete meter - {ed_gib_days} GiB-days of {breached_class} storage in billing month {invoice_month} breached the {min_duration_days}-day minimum duration, {ed_share_pct} pct of the bucket total, recurring over {prior_months_with_ed} prior months.

Extend the object deletion age past the {min_duration_days}-day minimum duration of the {breached_class} storage class.

Early-delete meter cost and GiB-days, penalty share of the bucket bill, prior months carrying the penalty

GCS Cold Class Transition Fires Too Late

Buckets whose lifecycle rule transitions objects to a cold class later than needed should be reviewed.

Mavvrik evaluates a configurable lookback period, with 90 days used by default, over which Standard-footprint volatility, cold-class growth, transition counts and early-delete history are all measured. A bucket is flagged when its own SetStorageClass rule already fires at 60 days or later, its Standard footprint swings no more than 25% month over month (the shifted-volume model assumes roughly uniform ingest), it serves no more than 2.0 Class B reads per GiB of Standard storage, and the derived mean transitioned object is at least 1.0 MiB — below that the per-object catch-up charge dominates the recurring saving. The target age is 30 days, and Standard carries no minimum storage duration, so nothing is lost by holding the first 30 days there. Savings are the rate delta on the shifted GiB-month less the one-time catch-up charge amortized over 12 months. Buckets the operation-storm policy claims are removed, because that policy's action is the exact opposite. Ships disabled and inert.

Cloud Storage bucket transitions to {target_class} only at {transition_age} days - {std_gib} GiB-month Standard in {invoice_month}, {b_ops_per_std_gib} Class B reads per GiB, {std_volatility_pct} pct volatility, mean object {derived_obj_mib} MiB; {target_transition_age_days} days would shift {shifted_gib} GiB-month.

Lower the transition age on the existing SetStorageClass rule from {transition_age} days to {target_transition_age_days} days.

Configured transition age, Standard GiB-month volatility, Class B reads per GiB, derived object size, cold-class growth per month

Log Bucket Retention Rightsize

Log buckets retaining logs past the 30-day default should be reviewed for a shorter retention period.

Mavvrik evaluates a configurable lookback period, with 30 days used by default — the single last complete invoice month; raising it to 60 or 90 requires the retention charge to recur in every month. A configuration and cost check: the bucket's own configured retention must be 31 days or more, which is the point above which the Log Retention meter exists at all, since log ingestion already includes the first 30 days of storage. The target is the GCP default of 30 days, where the meter goes to zero and the whole meter is recoverable; an intermediate target prices pro rata against the same meter. The required bucket and retention-locked buckets are excluded because their period cannot be shortened, so the recommendation would be unactionable. The trade is log history: entries older than the new period are dropped.

Cloud Logging bucket retains logs {retention_days} days against the GCP default of {default_retention_days} days; the Log Retention meter billed {retention_gib_month} GiB-months with {ingest_gib} GiB ingested, {recoverable_pct} pct recoverable at a {retention_target} day target.

Reduce the retention period on the Cloud Logging bucket from {retention_days} days to {retention_target} days.

Configured retention days, Log Retention meter GiB-months, ingested GiB, recoverable fraction

Pub/Sub Topic Retention Backlog Rightsize

Pub/Sub topics retaining a message backlog longer than consumers need should be reviewed.

Mavvrik evaluates a configurable lookback period, with 30 days used by default. Retention is not read from configuration — it is derived as backlog GiB-month divided by published GiB, times the days in the month — and a topic is flagged when that derived window sits between 0.007 and 31 days and the proposed target removes at least 20% of the backlog meter. The target is branch-selected: a topic delivering under 5% of project publish volume downstream while carrying at least 95% of that volume is classed unconsumed and targeted at the 1-day floor; every other topic is targeted at 7 days. Savings are the backlog meter scaled by the reduction fraction and capped at the topic's own cost. What is given up is replay depth — a shorter window means fewer messages available to seek back to. Enrolled as active but ships inert.

Pub/Sub topic retains messages {derived_retention_days} days and is {topic_share_pct} pct of project publish volume with {delivery_ratio_pct} pct delivered downstream, classified {consumer_branch}; a {target_retention_days} day window removes {retention_reduction_pct} pct of the meter.

Set messageRetentionDuration to {target_retention_days} days.

Derived retention days (backlog GiB-month / publish GiB), retention reduction pct, delivery ratio, topic share of project publish

Filestore Non-Production HA Tier Downgrade

Non-production Filestore instances on the Regional or Enterprise tier should be reviewed for Basic HDD.

Mavvrik evaluates a configurable lookback period, with 90 days used by default — the last three complete invoice months, and presence in every month is what excludes a freshly created instance still being sized. An instance is flagged when its derived provisioned capacity falls between 1,024 GiB, the Basic HDD tier minimum, and 65,472 GiB, its documented maximum — outside that band the target capacity does not exist on the tier — and when the ratio of its own Regional/Enterprise capacity rate to its own Basic HDD rate lands between 2.0 and 4.0. That ratio is a self-validating arithmetic check around the published list ratio: outside the band the derivation is untrustworthy and the row is suppressed rather than emitted wrong. Both rates are read off the tenant's own bill by SKU, never assumed. This is a migration, not an in-place change, and what is given up is the Regional tier's cross-zone durability and availability guarantee. Environment is the discriminator, with no environment predicate in the query; ships disabled and inert.

Filestore instance is over-provisioned on the Regional and Enterprise capacity tier - {premium_ratio}x the Basic HDD rate on {provisioned_gib} provisioned GiB, billed every month for {months_seen} months.

Migrate Filestore capacity from the Regional and Enterprise tier to Basic HDD ({provisioned_gib} GiB), then delete the source instance.

Provisioned GiB from the Regional/Enterprise capacity SKU, tier premium ratio, per-SKU net rates, months billed

Snapshot Veeam Non Production Retention Rightsize

Non-production Veeam snapshot chains retained longer than needed should be reviewed for shorter retention.

Mavvrik evaluates a configurable lookback period, with 30 days used by default — the single last complete invoice month, which is where every cost figure comes from. Snapshots are grouped by the label Veeam stamps on everything it creates, and a backup policy is reported when at least 25 of its snapshots sit past the retention target, so a handful of stragglers is not read as a retention problem. Two of the three knobs remain pinned and are not tunable as shipped: the retention target is fixed at 14 days and the chain carry-forward at 30%, so only 70% of the released band is modelled as actually freed — a modelled factor, not a measurement, because per-snapshot unique bytes are not exposed. What is given up is restore points: rollback coverage drops from roughly a month to a fortnight. Environment is the discriminator, with no environment predicate in the query; ships disabled and inert.

Veeam-managed snapshot chain is over-retained - {flagged_snapshots} of {policy_snapshots} snapshots exceed the {retention_target}-day retention target ({band_gib_month} GiB-months), age p95 {p95_age_days} days, max {max_age_days} days.

Reduce the Veeam backup retention period from about {p95_age_days} days to {retention_target} days.

Snapshots past the retention target, policy snapshot count, p95 and max snapshot age, released-band GiB-month

Internal Application Load Balancer Low Traffic Consolidation

Co-located low-traffic internal ALBs each holding their own proxy floor should be reviewed for consolidation.

Mavvrik evaluates a configurable lookback period, with 60 days used by default — two complete invoice months, which serve simultaneously as the persistence gate, the averaging window for the traffic signal, and the minimum-age guard against the configuration creation timestamp. An internal managed forwarding rule is flagged when it moves more than 1 and at most 100 GiB of data processing per month and sits in a co-located cluster of at least 3 rules sharing one project, region, VPC and port key. The lower bound is the disjointness boundary: at or below 1 GiB the rule is dead and belongs to the idle proxy-floor policy, which deletes it outright; above 100 GiB the merged load balancer would scale past its own three-proxy floor and the saving erodes. Savings are (n-1)/n of the proxy-instance floor the cluster pays today, since one load balancer survives and keeps its floor. This is a re-platform rather than an in-place change. Enrolled as active but ships inert.

Internal ALB forwarding rule is 1 of {cluster_size} low-traffic ALBs sharing one VPC and port {port_key}, each holding its own 3-proxy floor - {proxy_hours} proxy-hours ({proxy_instances_held} instances) in {invoice_month}, {dp_gib_per_month} of {cluster_dp_gib_per_month} GiB/month cluster-wide.

Consolidate with the other co-located rules behind one internal ALB, routing each service by host rule.

Data-processing GiB per month, co-located cluster size, proxy-instance hours (no forwarding-rule telemetry exists)

Non-Production Project Enrolled in Cloud Armor Enterprise

Non-production projects enrolled in Cloud Armor Enterprise should be reviewed for the Standard tier.

Mavvrik evaluates a configurable lookback period, with 30 days used by default — the latest complete invoice month and never the month in progress, because the subscription credit that nets down the flat protected-resource fee lands after the gross fee and a partial month overstates the base several-fold. There is no threshold block: every knob is a SKU, a GCP published Standard list price, or a naming convention. A project is flagged only when its net protected-resource fee exceeds what the same policy-months, rule-months and request volume would cost repriced at Standard, with the discount factor derived from the two Standard SKUs the tenant already pays. There is deliberately no cost or savings floor — the netting already floors a non-viable project at exactly zero. What is given up is a security control across an entire project: adaptive protection and Enterprise-only rule types go away and advanced rules already configured there freeze. Environment is the discriminator; name-based scoping was removed from the query, so the policy ships disabled and inert until the real non-production project scope is written into advanced settings.

Non-production backend service is metered by Cloud Armor Enterprise per service regardless of traffic - {metered_backend_services} metered services, against {policy_months} policy-months, {rule_months} rule-months and {request_count} requests on Standard.

Unenroll from the Cloud Armor Enterprise subscription so {metered_backend_services} backend services fall back to Cloud Armor Standard.

Net protected-resource fee per project, metered backend services, Standard policy-months / rule-months / request volume, derived Standard discount factor

Non-Production Redundant Dedicated Interconnect Circuit

Non-production Dedicated Interconnect circuits held only for redundancy should be reviewed for decommissioning.

Mavvrik evaluates a configurable lookback period, with 30 days used by default — the latest complete invoice month and never the month in progress, since the circuit meter accrues hourly and a partial month always reads as partial provisioning. A circuit is flagged when it is a surplus member of a group of at least 2 sharing a pair key, every member of that group billed at least 90% of the month's hours, and at least 1 circuit is ranked ahead of it — the survivor gate that structurally guarantees a group never loses its last connection. Only the flat hourly Dedicated circuit meter is read, so VLAN attachment hours and interconnect data transfer are untouched. What is given up is the redundancy itself: the environment drops from a two-circuit availability SLA to a single unprotected connection with no SLA, and the action is a physical decommission rather than a configuration change. Environment is the discriminator and the only available signal is the circuit name, so the policy ships disabled and inert.

Interconnect circuit is a surplus member of a {circuits_in_pair}-circuit non-production group - billed {billed_hours} of {month_hours} hours ({own_billed_hours_pct} pct) in {invoice_month}, group minimum {billed_hours_pct} pct billed.

Decommission the surplus Interconnect circuit, keeping the {circuits_kept_ahead} circuit(s) ranked ahead of it.

Circuits per pair key, billed circuit hours as a share of month hours (group minimum), survivor rank, net circuit cost

Non-Production VPN Connection Redundancy Collapse

Non-production VPN connections built above the redundant tunnel count should be reviewed for collapsing.

Mavvrik evaluates a configurable lookback period, with 30 days used by default — the latest complete invoice month and never the month in progress, since the tunnel meter accrues hourly and a partial month makes the whole fleet look part-time. A tunnel is flagged when its connection carries at least 4 tunnels, which selects the documented four-tunnel topology and structurally cannot touch a plain two-tunnel pair already at the redundancy floor; every tunnel in the connection must have billed at least 90% of the fleet-maximum tunnel-hours; and at least 2 tunnels must rank ahead of the candidate. That 2-tunnel floor must not be lowered, because a single-interface tunnel does not meet the availability SLA. Non-zero internet egress on the account and region is also required, which both proves the peer is outside Google and keeps this policy disjoint from the Google-to-Google peering policy. What is given up is the availability tier. Environment is the discriminator, with no environment predicate in the query; ships disabled and inert.

VPN tunnel is a surplus member of a {tunnels_in_group}-tunnel non-production connection - billed {billed_hours} hours ({own_uptime_pct} pct of the fleet peak {fleet_max_hours}) in {invoice_month}, group minimum {group_min_uptime_pct} pct uptime, {internet_egress_gib} GiB internet egress.

Reduce the VPN connection from {tunnels_in_group} tunnels to {tunnels_kept_ahead} by deleting the surplus tunnel.

Tunnels per connection, billed tunnel hours vs fleet peak (group minimum), survivor rank, internet-egress GiB, net tunnel cost

Google-to-Google VPN Tunnel Replaceable by VPC Peering

VPN tunnels connecting two Google Cloud networks should be reviewed for replacement by VPC Peering.

Mavvrik evaluates a configurable lookback period, with 30 days used by default — the latest complete invoice month and never the month in progress, since a partial month would fail the uptime gate for the entire fleet. A tunnel is flagged when the VPN gateways in its account and region billed exactly 0 GiB of internet data transfer, which is the primary Google-internal signal, while carrying non-zero intra- or inter-region Google-internal transfer, and every tunnel in the connection billed at least 90% of the fleet-maximum tunnel-hours. The egress band ships pinned at 0.0 and is not tunable as delivered. Savings are the net tunnel cost less a residual that reprices the Google-internal GiB at the GCP cross-zone VPC rate, split pro rata across the connection. Connections of 4 or more tunnels are excluded so the non-production HA-collapse policy keeps them. This is a re-platform that gives up IPsec at the tunnel layer, and because peer-gateway config, route tables and subnet ranges do not reach this data plane, subnet-overlap, non-transitivity and encryption-mandate checks must be cleared by a network engineer first. Ships inert.

Cloud VPN tunnel is 1 of {tunnels_in_group} tunnels to another Google Cloud network - VPC Peering candidate, {internet_egress_gib} GiB internet egress, {google_internal_gib} GiB Google-internal in {invoice_month}, {group_min_uptime_pct} pct group uptime, {billed_hours} billed hours.

Replace the VPN tunnel with VPC Network Peering.

Internet-egress GiB and Google-internal GiB per account/region, tunnel hours vs fleet peak (group minimum), tunnels per connection, net tunnel cost

GCP Orphan Recommendations

GCP resources that are not attached to active workloads and may continue to incur unnecessary costs.

Recommendation

Description

How Recommendation Is Generated

Reason

Action

Metrics

Persistent Disk Unattached

Persistent disks attached to no instance bill the full capacity rate while doing no work.

A configuration / state check. A persistent disk is flagged when its attachment list is absent, it is at least 30 days old, and either it was never attached or it was detached at least 30 days ago with the detach as its most recent attachment event — a disk detached yesterday is mid-maintenance, not waste. Disks attached to a stopped instance are excluded automatically and belong to the separate stopped-instance policy; released GKE PersistentVolumes are surfaced in the reason rather than excluded. The whole billed disk cost is quoted as recoverable, so the figure is an upper bound if the capacity is commitment-covered. Deletion is irreversible, so the option ships as snapshot-then-confirm-then-delete and is advisory.

Persistent {disk_type} disk ({size_gb} GB) is unattached - Never attached: never attached since creation Previously attached: detached {n} days ago, past the {days}-day dwell window.

Snapshot then delete the orphaned persistent disk.

N/A (attachment state, last attach / last detach timestamps and creation age)

External IP Address Unattached

Reserved external IP addresses attached to nothing bill the idle-address rate regardless of association.

A configuration / state check. A regional or global address is flagged when it is external, its own status is RESERVED, it has no users, its purpose is not Cloud NAT auto-allocation, and its last observed content change was at least 30 days ago. Internal addresses are excluded outright because they are free, and Cloud NAT auto-allocated addresses are dropped by the status gate. The address has no metric series and exposes no detach timestamp, so the dwell evidence is a point-in-time state plus a last-change age — an address detached minutes ago cannot be distinguished from one idle for years — and release is irreversible, since GCP hands out a different IP on re-reservation, so the recommendation is advisory.

External IP address ({ip_version}, {network_tier} tier) is orphaned - unattached for {n} days, past the {days}-day dwell threshold.

Release the idle reserved external IP address.

N/A (address type, status, users and purpose plus last-state-change age)

Stale Operator-Created Disk Snapshot

Snapshots taken by hand for a one-off change keep billing gibibyte-months long after the change closed.

Mavvrik evaluates each snapshot's age from its first billed usage line over a configurable window, with 90 days used by default; a Compute Engine snapshot has no utilisation telemetry of any kind, so age is the staleness signal itself and the policy defines no thresholds. A snapshot is flagged when it was first billed more than 90 days ago and still carries a positive gibibyte-month charge in the last complete invoice month. No name or label exclusion is applied in the query, so backup-tool-owned, schedule-generated and legal-hold-labelled snapshots are not filtered out. The saving is the billed storage charge less an assumed 30% incremental-chain carry-forward, so it rests on an assumption; deletion is permanent with no rollback, and the option is advisory.

Disk snapshot is unrotated - billed {age_days} days with no backup-tool label or schedule name, {billed_gib} GiB-months on the meter, {chain_carryforward_pct} pct carried forward on deletion.

Delete the snapshot once the originating change, upgrade or test is confirmed closed.

Snapshot age in days since first billed usage line + billed gibibyte-months in the grounding month

Cloud Storage Bucket Dormant And Unmanaged

Buckets with no lifecycle rules and no read or write activity keep paying at-rest storage indefinitely.

Mavvrik evaluates Cloud Storage write and read operations per GiB stored over a configurable lookback period, with 90 days used by default — the last three complete invoice months. A bucket is flagged when it has zero lifecycle rules, no retention lock, and in every one of those months recorded at most 1.0 Class A write per GiB and at most 1.0 Class B read per GiB, having been billed in every month and created before the window opened; a bucket that bills but has no live asset row is never flagged, because absent configuration is unknown configuration. The saving is the bucket's own at-rest storage meter, capped at its whole Cloud Storage bill, with no rate lookup or modelled factor. The expiry is destructive and irreversible once the soft-delete window elapses, and dormancy is inferred from billing meters only — an annual audit extract looks identical to abandoned data — so it is advisory; a non-destructive colder-class alternative ships alongside, deliberately quoted at zero saving.

Cloud Storage bucket is dormant with {lifecycle_rule_count} lifecycle rules - {gib_month} GiB-month in {invoice_month}, peak {max_a_ops} Class A / {max_b_ops} Class B ops ({writes_per_gib} writes, {reads_per_gib} reads per GiB) over {months} months, last write {invoice_month}, created {creation_date}.

Add an Object Lifecycle Management Delete rule to expire its contents. OR Add a lifecycle SetStorageClass rule to move its contents to a colder class.

Class A writes per GiB + Class B reads per GiB + stored GiB-month + dormant months + lifecycle rule count

AlloyDB Restore Leftover Cluster

A restored AlloyDB cluster with no serving instances still bills regional storage and point-in-time-recovery logs.

Mavvrik evaluates each cluster's compute-lane share of its AlloyDB spend over a configurable lookback period, with 60 days used by default. A cluster is flagged when that share is 0% — no vCPU or RAM meter anywhere in the window, since low compute share alone does not prove abandonment — while its cluster-lane meters (database storage plus PITR logs) still bill, and it was billed in both the earliest and the latest complete invoice month of the window so an in-flight restore or migration cutover is not caught. The whole cluster lane is quoted as recoverable; AlloyDB committed-use discounts cover compute only, so no commitment absorbs it. Deleting a cluster removes its automated backups permanently and an export is the only rollback, so it is advisory, and the policy ships disabled and inert.

AlloyDB cluster retains its full regional storage and point-in-time-recovery logs with {compute_ratio_pct} pct compute share over {days} days, a pattern held across {months_seen} months.

Delete the orphaned AlloyDB cluster after exporting any data still needed from it.

Compute-lane cost as a percentage of cluster-lane cost + months seen

BigQuery Frozen Dataset Reclaim

Datasets billing only long-term storage have gone unwritten for months and may be dead weight.

Mavvrik evaluates BigQuery active and long-term storage cost per dataset over a configurable lookback period, with 90 days used by default and at least 3 complete invoice months required. A dataset is flagged when its active storage cost is zero in every complete month of the window, its long-term storage cost is positive throughout, and it bills in exactly one region — multi-region datasets are partitioned off to the cross-region replica policy so the same bytes are never reclaimed twice. Frozen means unwritten, not unread, and BigQuery gives no dataset-grain read attribution, so the saving rests on the owner confirming no consumer reads it and the recommendation is advisory. Ships disabled and inert: a final disposability classifier gates the query, and it must be supplied through advanced settings before the policy selects anything.

BigQuery dataset has zero active storage and positive long-term storage, unchanged across {months_observed} complete invoice months, holding {gib} GiB.

Delete the frozen dataset to stop its long-term storage billing. OR Export the dataset to a Cloud Storage Archive bucket and then drop it from BigQuery.

Active vs long-term storage cost per complete invoice month + frozen months + long-term storage GiB

GCP License Recommendations

GCP resources that may be using a more expensive licensing model than necessary and could benefit from a no-cost equivalent image or a cheaper licence band. These recommendations help reduce licensing costs without changing or removing the active workload.

Recommendation

Description

How Recommendation Is Generated

Reason

Action

Metrics

Compute Engine Premium Licence vCPU Band Downgrade

Premium OS licences bill by vCPU band, so an oversized instance pays a higher licence tier than its load needs.

Mavvrik flags running Compute Engine instances (at least 30 days old) whose premium OS licence bills in a vCPU band the workload no longer needs: CPU p95 must fit a smaller shape at or below the 80% headroom ceiling with a floor of 2 target vCPU, network p95 must fall between 0 and 100 Mbps, and at least 80% of the 30-day hourly window must carry samples. Both the source and target band rates are derived from the tenant's own billed rows rather than a price list, a same-product peer instance must already be billing the target band, and the licence meter must be proven per-instance rather than per-vCPU; custom machine types and targets that would land on the extended-RAM SKU are excluded. Only the licence step is quoted as saving — the vCPU and RAM reduction is excluded because the tenant's Compute Flexible commitment absorbs those meters — so the figure rests on that commitment assumption. The resize needs a stop/start maintenance window and vendor confirmation that the smaller size is supported; ships inert.

Compute Engine instance is over-licensed for its vCPU band - CPU p95 {cpu_p95}%, network p95 {net_mbps_p95} Mbps over the last {days} days on {src_vcpu} vCPU under the {lic_product} licence ({band_lo}-{band_hi} vCPU band).

Resize from {instance_type} to {target_sku}.

CPU utilization (95th pct) + network throughput (95th pct, receive plus transmit)

Compute Engine Non-Production Premium Licence Replacement

Non-production instances paying a Red Hat licence fee can run a no-cost equivalent image already in the fleet.

Mavvrik flags running Compute Engine instances (at least 30 days old) that billed a paid Red Hat Enterprise Linux licence meter in the last complete invoice month, where a no-cost Rocky Linux equivalent is observably already running licence-free on between 25 and 100,000 live instances in the same fleet — equivalence is evidenced by the customer's own production use, and this same gate is what keeps Windows Server out of scope by rule rather than by hand. Instances sharing a cost-correlation identifier are excluded so one licence meter is never attributed twice. The saving is the licence meter alone and never the instance, verified to carry no commitment row; the swap is an operating-system replatform that recreates the root disk and forfeits the Red Hat support entitlement, so it is advisory. Non-production-scoped: ships disabled and inert, and the non-production scope must be supplied through advanced settings before the policy selects anything.

Compute Engine instance ({instance_type}) is non-production and runs the {lic_product} licence - {replacement_instances} other running instances in this fleet already run {replacement_license_name} licence-free.

Rebuild the instance from the {replacement_license_name} image to drop the {lic_product} licence meter.

N/A (paid premium licence meter cost for the last complete invoice month + count of live running instances already at zero licence cost on the replacement image)

Recommendations are advisory and support review and decision-making. Thresholds and lookback periods shown are defaults and are configurable per policy. Mavvrik never changes customer cloud resources.