This page explains how Mavvrik savings recommendations are generated for Amazon Web Services (AWS). 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.
A small number of library policies ship disabled by default and must be enabled per tenant before they generate recommendations; these are marked (ships disabled by default) in the tables below.
|
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, or on a pricier tier / plan / architecture than needed. Candidates for resizing, configuration 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 EBS volumes, unused Elastic IPs, snapshots whose source volume is gone, or load balancers with no registered backends. These typically serve no function on their own and are strong candidates for deletion. |
|
License |
Resources that may be using a more expensive licensing or support model than necessary — such as paying an extended-support surcharge to keep running a past-end-of-life engine, cluster, or version, or a pay-as-you-go license where an existing entitlement could be applied. Candidates for upgrading to a supported version, applying bring-your-own-license benefits, or switching to a more cost-effective licensing option without changing or removing the workload. |
AWS Idle Recommendations
AWS resources that may be unused, inactive, or no longer required based on utilization, traffic, connection activity, age, or attachment status.
|
Recommendation |
Description |
How Recommendation Is Generated |
Reason |
Action |
Metrics |
|---|---|---|---|---|---|
|
EC2 Instance Idle |
Running EC2 instances serving no measurable workload should be reviewed for stop or termination rather than a resize. |
Mavvrik evaluates CPU and network telemetry over a configurable lookback period, with 14 days used by default. A running instance is flagged when the p95 of hourly p95 CPU is within 0%–5% (default) AND the p95 of hourly peak network traffic is within 0–1,000,000 bytes, about 1 MB (default) — the network signal is a required corroborator, so a low-CPU box that is really a busy proxy, NAT host or file server is never called idle, and an instance carrying no network samples at all fails a blocker eval rather than passing on a vacuous check. Requires at least 80% CPU telemetry coverage and an instance at least 30 days old by its LaunchTime; instances averaging more than 10% CPU across the window are excluded so a spiky-but-working host is not caught on a percentile artifact. Production is excluded both by environment tag (PROD|LIVE|GLOBAL on stst:environment, environment, env, stage, stst:ms:environment) and by Name tag (PROD|PRD). Only instances with a positive attributable amortized compute cost emit a row. The CPU band is GOLDEN-MANAGED and not safe to retune per tenant: the three EC2 rightsize policies copy this value into an idle_cpu_floor_pct constant, and constants are not tenant-overridable, so widening it here alone double-counts the 5–10 band while narrowing it opens a coverage hole. Savings cover the instance compute meter only — attached EBS bills as its own resource and is reported separately. |
Running {instance type} instance {instance id} served no measurable workload over the last {N} days: p95 of hourly p95 CPU was {cpu} percent and p95 of hourly peak network traffic was {bytes} bytes. It bills full instance-hours while doing no work. |
Spot: Review, then terminate the idle Spot instance. Otherwise: Review, then stop the idle instance (terminate if it is genuinely disposable). A Spot instance is offered the terminate option only, because stop is unavailable for a one-time Spot request and only conditionally available for a persistent one; on-demand instances are offered stop (data and configuration preserved) alongside terminate. |
CPU % (p95 of hourly p95) + peak network bytes (p95 of hourly max in + out) |
|
Application Load Balancer Idle |
Application Load Balancers that carried no client traffic of any kind should be reviewed for deletion. |
Mavvrik evaluates load balancer request count over a configurable lookback period, with 14 days used by default, and cross-checks billed Load Balancer Capacity Units per load-balancer-hour across the last complete invoice month. Flagged when requests are 0 (default) and LCU intensity is within 0–0.0005 capacity units per load-balancer-hour (default). Requires at least 50% telemetry coverage, at least 25 billed load-balancer-days in the month, and a monthly cost of at least 1. Only load balancers whose configuration Type is application are considered. |
Mavvrik cost engineers found this {scheme} Application Load Balancer served {N} requests over the last {days} days and billed {LCU} Capacity Units per load-balancer-hour across the last complete invoice month, so no client traffic of any kind crossed it - not requests, not redirects, not fixed responses. It continues to pay the fixed ELB hourly meter regardless. |
Delete the idle Application Load Balancer to stop both the fixed hourly meter and the LCU meter. |
RequestCount (sum) + LCU per load-balancer-hour (LCUUsage / LoadBalancerUsage meters) |
|
Network Load Balancer Idle |
Network Load Balancers carrying no client flows should be reviewed for deletion. |
Mavvrik evaluates billed Load Balancer Capacity Units per load-balancer-hour over the last complete invoice month, against a declared 30-day window; NLB publishes to the AWS/NetworkELB namespace and emits no RequestCount into this data plane, so the billing meter is the only utilisation signal. Flagged when LCU intensity is within 0–0.001 capacity units per load-balancer-hour (default). Requires the load balancer to be at least 45 days old, at least 25 billed load-balancer-days in the month, and a monthly cost of at least 1. Load balancers whose name or tags suggest they back a VPC endpoint service are surfaced with a PrivateLink reviewer note rather than excluded. |
Mavvrik cost engineers found this {scheme} Network Load Balancer billed {LCU} Capacity Units per load-balancer-hour across {hours} hours of the last complete invoice month, so no client flows crossed it, yet it continues to pay the fixed ELB hourly meter. The NLCU is billed as the maximum of new flows, active flows and processed bytes, and a zero reading means none of the three occurred. |
Delete the idle Network Load Balancer to stop both the fixed hourly meter and the LCU meter. |
LCU per load-balancer-hour (LCUUsage / LoadBalancerUsage meters, invoice month) - no CloudWatch request metric exists |
|
Classic Load Balancer Idle with Healthy Backends |
Fully functional Classic Load Balancers with healthy backends that nobody calls should be reviewed for deletion. |
Mavvrik evaluates request count and backend health over a configurable lookback period, with 14 days used by default, and cross-checks the classic per-GB data-processing meter across the last complete invoice month. Flagged when requests are 0 (default) and processed data is within 0–0.01 GB (default) while at least one and every registered backend instance reported healthy throughout the window. Requires at least 50% telemetry coverage, at least 25 billed load-balancer-days, and a monthly cost of at least 1. Load balancers whose backends are never healthy or only partly healthy are excluded - that shape is a drained or half-drained blue-green standby rather than abandonment, and deleting it destroys the failover path. |
Mavvrik cost engineers found this {scheme} Classic Load Balancer is fully functional but unused: {healthy} of its {registered} registered backend instances reported healthy to CloudWatch during the window, and it still served {N} requests over the last {days} days. Nothing is broken here - nobody is calling it - and it keeps paying the fixed classic per-load-balancer-hour meter regardless. |
Delete the idle Classic Load Balancer to stop both the fixed hourly meter and the data-processing meter. |
RequestCount (sum) + HealthyHostCount (Max) + DataProcessing GB meter (invoice month) |
|
NAT Gateway Idle |
NAT gateways billing a full month of hourly charge while carrying no traffic should be reviewed for deletion. |
Mavvrik evaluates the 95th percentile of hourly concurrent connections over a configurable lookback period, with 14 days used by default, alongside billed data-processing volume across the last complete invoice month. Flagged when billed data is within 0–1 GB (default) and the p95 connection count is 0 (default). Requires at least 75% telemetry coverage and a gateway at least 30 days old. A NAT gateway has no stop or pause operation, so deletion is the only way to end the charge. |
This NAT gateway processed {GB} GB over the last complete invoice month and reported a p95 of {N} concurrent connections across {hours} observed hours, so it is billing a full hourly charge for capacity nothing is using. A NAT gateway cannot be stopped - deleting it ends both the hourly and the data-processing charge and recovers {currency} {amount} per month. |
Delete this NAT gateway after confirming no route table still targets it. |
Concurrent connections (95th pct of hourly max) + NatGateway-Bytes meter (invoice month) |
|
EBS Volume Attached to Long-Stopped EC2 Instance |
Volumes still billing full provisioned capacity on instances stopped for an extended period should be reviewed for snapshot-and-release. |
A configuration / state check (no lookback). Stop duration is parsed from the EC2 StateTransitionReason string and must be at least 30 days; an instance whose reason string carries no timestamp is excluded rather than assumed old. Instances and volumes tagged disaster-recovery, dr-, standby, failover, golden or template are excluded. Two savings figures are computed rather than one - archive-then-release, net of the ongoing snapshot storage cost at 0.05 per GB-month, and release-without-snapshot at full volume cost; the primary row reports the recommended lower-risk archive-then-release figure and both are carried in the target options. |
This {volume type} volume of {N} GiB is attached as {device} to instance {instance id}, which has been stopped for {N} days. The stopped instance incurs no compute charge, but the volume keeps billing its full provisioned capacity. |
Take a snapshot, then release the volume from the long-stopped instance, or release the volume without taking a snapshot (higher risk, permanent data loss). |
N/A (volume attachment + parent instance stop duration from StateTransitionReason) |
|
RDS Instance Idle |
DB instances serving no workload while billing instance-hours, storage and backup should be reviewed for snapshot-and-delete. |
Mavvrik evaluates CPU utilization and client connections over a configurable lookback period, with 14 days used by default. Flagged when the 95th percentile of hourly-max CPU is within 0%–5% (default) AND peak database connections is 0 (default) - both signals must agree, because database_connections_max reads 0 rather than NULL when the metric is not collected, so a connections-only rule flags busy databases as idle. Requires at least 70% telemetry coverage and an instance at least 30 days old. Read replicas are excluded entirely, and anything that looks production by environment tag (PROD|LIVE|GLOBAL) or by DB instance identifier (PROD|PRD) is excluded. Aurora cluster members are scored on instance compute only, never cluster storage. |
Standalone: {engine} instance on class {class} served no measurable workload over the last {days} days: p95 of hourly-max CPU was {cpu} percent and the peak client connection count was {N}. It continues to bill instance-hours, storage and backup while doing no work. Aurora last member: ... is currently the only live member of Aurora cluster {cluster}, and removing it deletes the entire cluster, including its shared storage. Aurora member: ... its instance-hours bill separately from the cluster shared storage, which continues regardless. |
Standalone: Review, take a final snapshot, then delete the idle DB instance. Aurora last member: Review, take a cluster-level final snapshot, then delete the Aurora cluster (this is its last remaining instance). Aurora member: Review, then remove the idle Aurora cluster member instance (cluster storage and snapshots are unaffected). |
CPU % (95th pct of hourly Max) + peak database connections |
|
Redshift Cluster Idle (ships disabled by default) |
Non-production Redshift clusters billing compute node-hours around the clock while serving no queries should be reviewed for indefinite pause or deletion. |
Mavvrik evaluates database connections and CPU over a configurable lookback period, with 30 days used by default. Flagged when at least 95% of observed hours recorded zero database connections (default), the last database session ended at least 14 days ago (default), the 95th percentile of hourly-average CPU is within 0%–20% (default), and on-demand compute spend for the last complete invoice month is at least 25. The recency gate is load-bearing - an aggregate share of idle hours only proves the cluster is quiet, never that it is abandoned. Requires at least 80% telemetry coverage, and the cluster must carry a non-production environment tag (dev, preprod, uat, test, qa, staging, stage, int). Savings are the unblended on-demand compute meter only, because managed storage keeps billing through both a pause and a snapshot-preserving delete and Reserved Instances are payable regardless. |
This {env} Redshift cluster recorded no database connection in {pct} percent of the {hours} observed hours, its last database session ended {N} days ago, and p95 of hourly average CPU was {cpu} percent - the leader-node housekeeping baseline for a cluster serving no queries. It held {N} tables and billed {amount} {currency} of on-demand compute last month while doing no work. |
Review with the owning team, then pause the cluster indefinitely, or delete it after taking a final snapshot. |
Database connections (zero-hour share + days since last session) + CPU % (95th pct of hourly avg) + table count |
|
Redshift Non-Production Pause Schedule (ships disabled by default) |
Non-production Redshift clusters idle on a recurring weekly pattern should be reviewed for paired pause and resume scheduled actions. |
Mavvrik evaluates per-hour-of-week connection activity across the 168 weekly slots over a configurable lookback period, with 30 days used by default. A slot counts as schedulable only if it was idle in at least 90% of the weeks it was observed, because scattered one-off quiet hours cannot be turned into a pause window; flagged when at least 25% of observed slots are schedulable (default) and the recoverable on-demand compute spend is at least 50 (default). Clusters idle in 95% or more of all observed hours are excluded and left to the always-idle lever. Requires at least 80% telemetry coverage and a non-production environment tag (dev, preprod, uat, test, qa, staging, stage, int). Savings cover the recurrently-idle share of the on-demand compute meter only - managed storage bills through a pause and Reserved Instances are payable regardless. |
This {env} Redshift cluster is reliably idle in {N} of the {total} observed hour-of-week slots, or {pct} percent of the week, with no database connection in {pct} percent of all {hours} observed hours. Those idle hours recur in the same slots week after week, so they can be turned into a pause schedule, while on-demand compute billed {amount} {currency} last month and runs around the clock today. |
Create paired pause and resume scheduled actions across the recurring idle window, after confirming it with the owning team. |
Database connections per hour-of-week slot (168 slots, recurrence-weighted) + on-demand compute spend |
|
EMR Cluster Idle Without Auto-Termination |
EMR clusters sitting in the WAITING state with auto-termination disabled while running no work should be reviewed for an auto-termination policy. |
Mavvrik evaluates the CloudWatch IsIdle gauge over a configurable lookback period, with 30 days used by default, grouping contiguous idle hours into episodes by gaps-and-islands. Flagged when the cluster is in the WAITING state with auto-termination disabled and was fully idle (IsIdle at or above 0.999) for at least 50% of measured hours (default), with cluster spend of at least 25 (default). Requires at least 80% telemetry coverage. Liveness is read from configuration Status.State rather than asset_termination_time, which on the grounding tenant passed 210 clusters of which only 7 were actually running; clusters running Presto, Trino or HBase are excluded. Savings charge each idle episode the 1-hour idle-timeout grace period the policy must wait out and cover the Amazon EMR surcharge only, so every figure is a conservative floor - the underlying EC2 and EBS spend is attributed to the instance ids. |
EMR cluster {name} is in the WAITING state with auto-termination disabled and was fully idle for {N} of {total} measured hours over the last {days} days ({pct} percent), across {N} idle episodes with a longest unbroken idle run of {N} hours. An auto-termination policy with a {N} minute idle timeout would reclaim about {N} of those hours. The cluster bills the Amazon EMR per-instance-hour surcharge for every node while doing no work. |
Termination protection on: Step 1 - remove termination protection with aws emr modify-cluster-attributes --no-termination-protected, because while protection is on an auto-termination policy attached on its own fires nothing; Step 2 - attach an auto-termination policy with IdleTimeout={N} seconds. Otherwise: attach an auto-termination policy with IdleTimeout={N} seconds, so the cluster shuts itself down once it has been idle that long. |
IsIdle gauge (hourly avg; idle-hour share + contiguous idle episodes) |
|
Kinesis Data Stream Dead |
Kinesis data streams that recorded no ingest at all while billing declared capacity should be reviewed for deletion. |
Mavvrik evaluates both mode-dependent ingest meters over the last 3 complete invoice months, all of which must show zero ingest - there is no CloudWatch telemetry for this resource type on this data plane, so no metric window is read. Flagged when the stream carries no registered enhanced fan-out consumer, has no extended retention enabled, is at least 90 days old, and costs at least 5 per month (default). Savings are computed from cost_monthly at meter grain rather than asset_monthly, which over-reports some streams 2x and omits the enhanced fan-out meter entirely. On-demand streams are additionally proven unread via the OnDemand-BilledOutgoingBytes meter; for provisioned streams reads are unobservable and the emitted finding says so rather than claiming a proof it does not have. |
Mavvrik cost engineers found this Kinesis data stream ({name}, {mode} mode, created {N} days ago) recorded zero ingest across all {N} complete invoice months of the window - zero PUT payload units on the provisioned meter AND zero on-demand incoming bytes - and carries no registered enhanced fan-out consumer. Kinesis bills declared capacity continuously, with no idle discount and no scale-to-zero, so the stream pays full price to hold nothing. On-demand: Reads are proven absent as well - on-demand mode meters outgoing bytes, and this stream billed zero across every month of the window. Provisioned: Reads could NOT be checked, because provisioned mode emits no egress meter and shared-throughput GetRecords polling is unbilled, so an application-level check is required before deletion. |
Delete this Kinesis data stream to stop every meter it carries, after confirming with a named owner that no client still holds a reference to it. |
PutRequestPayloadUnits + OnDemand-BilledIncoming/OutgoingBytes + EnhancedFanoutHour meters (3 invoice months) |
|
Kinesis Enhanced Fan-Out Consumer Idle |
Streams paying the enhanced fan-out consumer-shard-hour meter while ingesting a negligible fraction of a shard should be reviewed for a move to shared throughput. |
Mavvrik evaluates the EnhancedFanoutHour meter and estimated ingest over the last 3 complete invoice months - there is no telemetry for this resource type on this data plane, so no metric window is read. Flagged when estimated monthly ingest is within 0%–5% of one shard's write capacity (default), where one shard is taken as 2,566.4 GB per month and PUT payload units are converted at 25 KB each, which rounds up per record and therefore overstates real volume. The meter bills against the consumer ARN, which never appears in asset_view, so consumer ARNs are rolled back to their parent stream by regex before joining. Savings isolate the EnhancedFanoutHour meter recovered in full; the underlying shard-hour charge is untouched. |
Mavvrik cost engineers found this Kinesis data stream ({name}, {mode} mode) billing {N} enhanced fan-out consumer-shard-hours over {N} complete invoice months while ingesting an estimated {pct} percent of one shard monthly write capacity. Enhanced fan-out bills continuously from the moment a consumer registers, not when it reads, and it exists only to give that consumer a dedicated 2 MB/s pipe per shard. On this stream it costs {currency} {amount} per month on top of the shard itself and buys nothing a free shared-throughput consumer does not already provide. |
Move the consumer on this stream from enhanced fan-out to the free shared-throughput read path and deregister the fan-out consumer to stop the EnhancedFanoutHour meter. |
EnhancedFanoutHour meter + ingest estimated from PutRequestPayloadUnits and OnDemand-BilledIncomingBytes (3 invoice months) |
|
Secrets Manager Secret Unused |
Stored secrets that nothing has retrieved for an extended period should be reviewed for scheduled deletion. |
A configuration and billing-meter check (no telemetry). Flagged when the configuration snapshot shows the secret has not been retrieved for at least 180 days (default), or has never been retrieved in its Region at all, AND the billing meter independently recorded zero GetSecretValue calls in the last complete invoice month; the secret must be at least 30 days old. Because the action is a deletion, two independent sources must agree. Four exclusions are applied to every row rather than sampled: secrets owned by another AWS service, secrets under automatic rotation, secrets whose name states retention intent (DoNotDelete, break-glass, emergency, disaster/dr, backup, failover, recovery), and manually-created database credentials under the rds-db-credentials/ prefix which OwningService does not mark. A secret scheduled for deletion keeps billing for the whole recovery window, so month-1 realized savings are approximately zero and the full amount arrives from month 2. |
Idle: Mavvrik cost engineers validated on your live data that secret {name} has not been retrieved for {N} days, yet it is still billed the AWS Secrets Manager storage charge of {amount} {currency} per month. Never retrieved: ... secret {name} has never been retrieved in its Region since it was created, yet it is still billed the same monthly charge. Either way a second and independent source agrees: the billing meter recorded zero GetSecretValue retrievals in the last complete invoice month, alongside {N} metadata List calls which never retrieve a value and correctly never move the last-accessed date. |
Confirm ownership with the owning team, then schedule deletion with the maximum 30 day recovery window so the secret can be restored if a consumer still needs it. |
N/A (LastAccessedDate + zero GetSecretValue meter, invoice month) |
AWS Rightsize Recommendations
AWS resources that may be oversized, underutilized, on a pricier tier / plan / architecture than needed, or schedulable — candidates for a more cost-effective configuration or commitment rather than removal.
|
Recommendation |
Description |
How Recommendation Is Generated |
Reason |
Action |
Metrics |
|---|---|---|---|---|---|
|
EC2 Same-Family Downsize |
Running EC2 instances whose sustained CPU leaves enough headroom to fit the next size down should be reviewed for a same-family downsize. |
Mavvrik evaluates p95 CPU over a configurable lookback period, with 14 days used by default, then projects that load onto the next smaller size in the same family taken from the published EC2 specification catalog rather than a hand-written size ladder; flagged when the projected post-resize CPU is within 0%–50% (default). Requires at least 80% telemetry coverage and at least 25 per month in modelled savings. Memory-optimized families are excluded outright, along with burstable, bare-metal and local-NVMe families, because a one-step downsize also halves memory and local instance storage. Auto Scaling group members are collapsed into one group-level finding, and a group whose live members do not all independently clear the gates is scored zero. Instances that EC2 Instance Idle claims are yielded to it rather than offered a resize, because stopping strictly dominates resizing. The hand-off is the full intersection of that policy's gate — running, at least 30 days old, p95 CPU at or below 5%, p95 peak network at or below 1,000,000 bytes over a 14-day window at 80% coverage, and not production by either environment tag or Name tag — deliberately not a bare CPU floor: a low-CPU instance that is production-tagged, too young, or network-active is not claimed by the idle policy, so yielding it on CPU alone would leave it with no recommendation at all. Two blocker evals enforce this: disjoint_from_ec2_instance_idle, and exclusion_matches_idle_policy, a drift guard on the duplicated gate. Before these gates, 28 instances on the grounding tenant were being offered both "stop it" and a resize. |
Standalone instance: Mavvrik cost engineers measured sustained p95 CPU at {cpu} percent over the last {N} days on this {instance type} instance. The next size down inside the same instance family is {target type}, which halves vCPU from {src vCPU} to {tgt vCPU}, so the workload would sit at about {post-resize cpu} percent CPU after the resize - still at or below the {ceiling} percent post-resize ceiling. Auto Scaling group, all members qualify: Mavvrik cost engineers measured average sustained p95 CPU at {cpu} percent over the last {N} days across all {N} live {instance type} instances in Auto Scaling group {group}, every one of which independently clears the post-resize CPU, memory, EBS-only and burstable safety gates. Auto Scaling group, partial: Mavvrik cost engineers validated {M} of the {N} live instances in Auto Scaling group {group} against the safety gates - the remaining {K} live instances have not been proven safe for this resize, and a launch-template change applies to every member, so no priced action is recommended for this group today. |
Stop the instance, change the instance type from {instance type} to {target type}, and start it again during a maintenance window. Or update the launch template for Auto Scaling group {group} from {instance type} to {target type} and run an instance refresh - this applies to all {N} instances currently qualifying in the group. |
CPU % (95th pct) + projected post-resize CPU (p95 scaled by source/target vCPU ratio) |
|
EC2 Cross-Family Spec-Identical Swap |
Running EC2 instances that can move to a different instance family selling the identical specification at a lower rate should be reviewed for a like-for-like type swap. |
Mavvrik evaluates CPU over a configurable lookback period, with 14 days used by default, alongside published on-demand rates for the last complete invoice month; flagged when observed p95 CPU is within 0%–95% (default) and the target family's rate is 5%–30% (default) below the current type. Requires at least 80% telemetry coverage and at least 10 per month in savings. A target qualifies only on identical vCPU, memory, processor architecture and local-storage class with equal or better network bandwidth, and is refused if it belongs to an older hardware generation or a Flex family. Instances covered by a Reserved Instance are excluded outright, because Size Flexibility does not cross families and the move would strand the reservation. Instances that EC2 Instance Idle claims are yielded to it rather than offered a resize, because stopping strictly dominates resizing. The hand-off is the full intersection of that policy's gate — running, at least 30 days old, p95 CPU at or below 5%, p95 peak network at or below 1,000,000 bytes over a 14-day window at 80% coverage, and not production by either environment tag or Name tag — deliberately not a bare CPU floor: a low-CPU instance that is production-tagged, too young, or network-active is not claimed by the idle policy, so yielding it on CPU alone would leave it with no recommendation at all. Two blocker evals enforce this: disjoint_from_ec2_instance_idle, and exclusion_matches_idle_policy, a drift guard on the duplicated gate. Before these gates, 28 instances on the grounding tenant were being offered both "stop it" and a resize. |
Standalone instance: Mavvrik cost engineers matched this {instance type} instance to {target type}, a different instance family selling the identical specification: {vCPU} vCPU, {memory} GiB of memory, the same processor architecture, the same local-storage class and equal or better network bandwidth. Published on-demand rates put the target about {pct} percent below the current type, so the swap lowers the hourly rate without reducing any capacity. This instance carries no Reserved Instance coverage in the scored invoice month, so no reservation is stranded by the move. Auto Scaling group, all members qualify: Every live instance in this group independently cleared every safety check, so the figures scale the representative per-instance economics to the full group of {N} instances. Auto Scaling group, partial: Only {M} of the {N} live instances in this group independently cleared every safety check for this specific target, so the figures scale to those instances only. |
Stop the instance, change the instance type to {target type}, and start it again. Or update the launch template for Auto Scaling group {group} to {target type} and refresh the group, which applies to all {N} instances in the group. |
CPU % (95th pct) + published on-demand rate delta % |
|
EC2 Graviton Migration (ARM-Proven Accounts) |
Running x86 EC2 instances in accounts that already run the equivalent Graviton family should be reviewed for ARM64 compatibility and migration. |
A configuration and pricing comparison (no utilization), priced over the last complete invoice month. Mavvrik matches each x86 instance to a spec-equivalent Graviton type from the EC2 specs catalog - identical family category, vCPU, memory and burstable class - and computes the delta from published on-demand rates as a discount-neutral rate ratio applied to billed compute cost. Flagged when the Graviton rate is 2%–40% (default) below the current type, the same AWS account already runs at least 3 instances of the exact Graviton family being proposed, and the implied delta is worth at least 25 per month. Emitted as a zero-scored architecture-assessment opportunity - score and every monthly savings field are 0 - because account-level ARM presence does not prove the AMI, application binaries and host agents on this specific instance have a validated ARM64 build. Instances that EC2 Instance Idle claims are yielded to it rather than offered a resize, because stopping strictly dominates resizing. The hand-off is the full intersection of that policy's gate — running, at least 30 days old, p95 CPU at or below 5%, p95 peak network at or below 1,000,000 bytes over a 14-day window at 80% coverage, and not production by either environment tag or Name tag — deliberately not a bare CPU floor: a low-CPU instance that is production-tagged, too young, or network-active is not claimed by the idle policy, so yielding it on CPU alone would leave it with no recommendation at all. Two blocker evals enforce this: disjoint_from_ec2_instance_idle, and exclusion_matches_idle_policy, a drift guard on the duplicated gate. Before these gates, 28 instances on the grounding tenant were being offered both "stop it" and a resize. Here the gate is advice coherence only, since this policy emits zero-scored assessment rows and so carries no dollar double-count. |
Mavvrik cost engineers matched this {instance type} instance to {target type}, an AWS Graviton type with identical vCPU, memory and family category, and the same AWS account already runs {N} instances of the {target family} family. This is an architecture-assessment opportunity, not a priced recommendation: account-level Graviton presence does not confirm that the AMI, application binaries and host agents on THIS instance have a validated ARM64 build. Published on-demand rates put the Graviton target about {pct} percent below the current type; if compatibility is confirmed, the list-price delta implies roughly {savings} {currency} per month, but that figure is not counted toward score or savings until the workload owner confirms it. |
Confirm AMI, binary and host-agent ARM64 compatibility for this instance, then relaunch on {target type} using an ARM64 machine image once validated. |
N/A (instance spec match + published rate delta + count of arm64 siblings in the account) |
|
EC2 Compute Savings Plan Coverage Gap |
EC2 instances that ran continuously at On-Demand rates with neither a Savings Plan nor a Reserved Instance attached should be reviewed for a Compute Savings Plan commitment. |
Mavvrik evaluates billed EC2 compute over the last complete invoice month; an instance qualifies when it ran 90%–100% (default) of the month's hours uncovered, and the published 1-year No Upfront Compute Savings Plan discount for its configuration falls in the 4%–35% (default) band, above a 10 per month floor. Because a Compute Savings Plan is a single commitment purchased once per billing scope rather than a per-instance action, one advisory row is emitted per (billing account, usage account) scope, naming the qualifying instances and aggregate uncovered spend as evidence. Savings are emitted as zero by design: the commitment is hourly and this data plane carries only daily-grain billing, which cannot prove a commitment stays consumed through the lowest-traffic hour of the day. |
Mavvrik cost engineers identified {N} uncovered On-Demand EC2 instances in this account that ran steadily through the last complete invoice month with neither a Savings Plan nor a Reserved Instance attached, totaling {cost} {currency} per month in uncovered spend. Published rates indicate a {term} year {purchase option} Compute Savings Plan would typically discount this configuration by about {discount} percent. Mavvrik's data plane carries only daily-grain billing data for this tenant, one level too coarse to size the hourly spend commitment a Compute Savings Plan actually requires. |
Review this account's uncovered On-Demand EC2 spend in AWS Cost Explorer and size a Compute Savings Plan commitment using its own hourly-usage-based recommendation before purchasing. |
Run-hours % of the invoice month (cost-weighted average) + effective-vs-on-demand discount % |
|
EBS gp2 to gp3 Migration |
Attached gp2 EBS volumes should be reviewed for migration to the cheaper gp3 volume type. |
A configuration and pricing comparison (no utilization). Mavvrik prices the tenant's actual billed gp2 capacity meter at the 20% gp3 per-GB discount, then nets off the gp3 provisioned-IOPS top-up (0.0055 per IOPS above the 3,000 IOPS gp3 includes free) needed to preserve the gp2 capacity-derived baseline of 3 IOPS/GB, so a volume above roughly 1,000 GiB is priced with its top-up charge included rather than ignored. Any volume with a positive net saving is flagged. Unattached gp2 volumes are deliberately excluded and left to the unattached-volume policy, because deleting beats migrating. |
This attached gp2 volume of {N} GiB bills capacity at the legacy gp2 rate. gp3 delivers the same general-purpose SSD storage for {pct} percent less per GB, and the volume-type change is online with no downtime and no data movement. The gp2 baseline of {baseline} IOPS requires a gp3 top-up of {topup} provisioned IOPS to preserve performance, which is already netted off the saving shown. |
Change the volume type from gp2 to gp3. |
N/A (volume type + attachment state + size + billed gp2 capacity meter) |
|
EBS gp3 Over-Provisioned IOPS |
gp3 volumes provisioned above the free IOPS baseline whose measured demand sits far below the provisioned level should be reviewed for a lower IOPS setting. |
Mavvrik evaluates observed IOPS - read plus write ops per hour divided by 3600 - over a configurable lookback period, with 14 days used by default, and flags any gp3 volume provisioned above the 3,000 free IOPS baseline with excess IOPS to reclaim. The recommended target is the greatest of the 3,000 free floor, p95 plus 50% headroom, and the observed peak plus a 10% peak guard, so a bursty database is never sized below a burst it actually performed. Requires at least 50% metric coverage over the window. Savings are fractional on the EBS:VolumeP-IOPS meter only; capacity cost is untouched, and the change is online and fully reversible. |
This gp3 volume is provisioned at {IOPS} IOPS but observed demand over the last {N} days peaks at {peak} IOPS with a p95 of {p95} IOPS. Mavvrik cost engineers validated a target of {target} IOPS, which keeps the observed peak plus a safety margin above the gp3 baseline included at no charge. |
Reduce provisioned gp3 IOPS from {IOPS} to {target}. |
IOPS (95th pct, Max) from read + write ops |
|
EBS gp3 Over-Provisioned Throughput |
gp3 volumes provisioned above the free throughput baseline whose measured bandwidth sits far below the provisioned level should be reviewed for a lower throughput setting. |
Mavvrik evaluates observed bandwidth - read plus write bytes per hour converted to MiB/s - over a configurable lookback period, with 14 days used by default, and flags any gp3 volume provisioned above the 125 MiB/s free baseline with excess throughput to reclaim. The recommended target is the greatest of the 125 MiB/s free floor, p95 plus 50% headroom, and the observed peak plus a 10% peak guard, so a nightly backup burst is never sized away. Requires at least 50% metric coverage over the window. Savings are fractional on the EBS:VolumeP-Throughput meter only; capacity cost is untouched, and the change is online and fully reversible. |
This gp3 volume is provisioned at {MiB/s} MiB/s but observed bandwidth over the last {N} days peaks at {peak} MiB/s with a p95 of {p95} MiB/s. Mavvrik cost engineers validated a target of {target} MiB/s, which keeps the observed peak plus a safety margin above the gp3 baseline included at no charge. |
Reduce provisioned gp3 throughput from {MiB/s} to {target} MiB/s. |
Throughput MiB/s (95th pct, Max) from read + write bandwidth |
|
Lambda arm64 Migration (ARM-Proven Accounts) |
Live x86_64 Zip-packaged Lambda functions in accounts that already run arm64 functions should be reviewed for an arm64 rebuild. |
A configuration and pricing comparison (no utilization), read over the last complete invoice month. Flagged when the function's own AWS account already runs at least 3 arm64 Lambda functions - direct evidence an arm64 build path exists for that team - and the measured rate delta falls in the 10%–25% (default) band, above a 1 per month savings floor. The delta comes from the tenant's own billed arm64 and x86 GB-second rates per region, used only where the region carries at least 1,000,000 GB-seconds, falling back to the published 20% Graviton discount otherwise and clamped at 25% so a free-tier-distorted region can never produce negative savings. The saving applies to the duration meter only, because requests price identically on both architectures. Container-image functions are excluded outright. |
Mavvrik cost engineers found this function running on x86_64 with a Zip deployment package while its own AWS account already runs {N} Lambda functions on arm64, which is direct evidence that an arm64 build path exists for this team. AWS prices arm64 duration below x86 for the same GB-second, and the delta measured from this tenant's own billed rates in {region} is about {pct} percent. The saving applies to the {cost} {currency} of duration spend only, because requests are priced identically on both architectures. |
Rebuild this function's deployment package and every layer it attaches for arm64, then set the function architecture to arm64. Validate one function in a non-production account before rolling the set. Current runtime: {runtime}. |
N/A (architecture + package type + tenant's own arm64 vs x86 GB-second rate ratio) |
|
Lambda Provisioned Concurrency Rightsize |
Lambda functions reserving far more provisioned concurrency than is actually consumed should be reviewed for a smaller allocation. |
Mavvrik compares the provisioned-concurrency allocation meter against the duration meter actually executed on it for the last complete invoice month - a gap-free billing-derived utilisation with none of CloudWatch's coverage gaps - and corroborates with invocation counts over a configurable lookback period, with 30 days used by default. Flagged when utilisation is within 0%–5% (default) and at least 1 per month is recoverable. The recommended target retains a 20% headroom slice above observed demand, so this targets over-allocation rather than the existence of provisioned concurrency, which is a legitimate cold-start latency investment. Savings are capped at the function's own allocation meter, never its total Lambda cost, and a missing invocation hour is read as zero invocations rather than missing telemetry. |
Mavvrik cost engineers found this function reserving {alloc} GB-seconds of provisioned concurrency last complete invoice month while only {used} GB-seconds were actually executed on it - a utilisation of {pct} percent. Provisioned concurrency bills for the capacity you configure, for the whole period you configure it, whether or not any invocation lands on it. No invocations: This function also recorded no invocations at all over the lookback window, so the reserved capacity is serving nobody - this is the highest-confidence subset of the finding. Some invocations: The function recorded {invocations} invocations over the lookback window. |
Reduce the provisioned concurrency allocation on this function toward observed peak concurrency, retaining about {pct} percent headroom above measured use rather than removing provisioned concurrency entirely. |
Provisioned-concurrency allocated vs executed GB-seconds (billing meters) + invocation count (Sum) |
|
Lambda Kafka ESM Poller Group Consolidation |
Provisioned-mode Lambda Kafka event source mappings that never rise above their one Event Poller Unit floor should be reviewed for sharing EPU capacity through a common poller group. |
Mavvrik evaluates the Event Poller Unit meter over the last complete invoice month; flagged when EPU averaged across every hour of the month is within 0–1.05 (default), proving the mapping never autoscaled above its configured minimum. Qualifying mappings are grouped per AWS account and region and require at least 2 members; each group retains its highest-cost member as the EPU payer and every other member emits a row, with a 10% shared-capacity reserve withheld from the claim and a 10 per month savings floor. A maturity predicate requires the meter to have billed in at least 2 of the last 3 months, so partial-month newcomers whose low EPU-hours reflect a mid-month creation rather than a genuine at-the-floor pattern are excluded. |
Mavvrik cost engineers found this Kafka event source mapping billing {N} Event Poller Unit hours in the last complete invoice month, which is {avg} EPU averaged over every hour of the month - it has never autoscaled above its configured minimum, and it has billed this meter in {M} separate months. AWS ESM grouping lets event source mappings in the same VPC share EPU capacity through a common PollerGroupName instead of each reserving a dedicated unit. Grouping this mapping with the {K} other at-the-floor mappings in the same account and region leaves one retained EPU payer for the group. |
Set PollerGroupName to {group} on this event source mapping so it shares Event Poller Unit capacity with the other at-the-floor mappings in this account and region, after confirming they all sit in the same Amazon VPC. |
EPU-hours meter (invoice months), averaged over every hour of the month |
|
Low-Traffic Application Load Balancer Consolidation |
Clusters of live Application Load Balancers each carrying real but negligible traffic should be reviewed for consolidation onto one shared load balancer. |
Mavvrik evaluates hourly request counts over a configurable lookback period, with 14 days used by default; flagged when the busiest hour's request count is within 0–60 requests (default). Candidates are grouped by account, region, scheme and VPC, requiring at least 2 members, at least 1 per month of fixed hourly cost and at least 50% telemetry coverage. Exactly one load balancer per group is retained and credited zero, so only the load balancers actually being retired emit a row. Only the fixed per-load-balancer ELB hourly meter is counted as savings - the LCU meter follows the traffic onto the shared load balancer and is explicitly excluded. Ships as a design proposal for engineering review, not a mechanical change. |
Mavvrik cost engineers found this Application Load Balancer peaked at {requests} requests in its busiest hour over the last {N} days, too little to justify a dedicated load balancer, yet it pays a fixed ELB hourly charge of {cost} per month that is billed per load balancer regardless of traffic. It shares account, region, scheme ({scheme}) and VPC ({vpc}) with {K} other equally quiet load balancers, so the group can be collapsed onto {survivor} using host-based and path-based listener rules with SNI for multiple certificates. |
Consolidate this low-traffic Application Load Balancer onto {survivor} and retire it once DNS is repointed. |
Request count per hour (95th pct, Sum) + fixed LoadBalancerUsage meter |
|
NAT Gateway Redundant AZ Consolidation |
VPCs running one NAT gateway per Availability Zone while carrying traffic a single gateway could serve should be reviewed for consolidation onto the busiest gateway. |
Mavvrik evaluates billed NAT data processing for the last complete invoice month together with peak simultaneous concurrent connections over a configurable lookback period, with 14 days used by default. Flagged when a VPC with at least 2 active gateways processed within 0–500 GB (default) for the month and its busiest simultaneous hour carried within 0–5,000 concurrent connections (default), with at least 75% telemetry coverage. The busiest gateway in each VPC survives; every other gateway is emitted as a candidate. Savings are the recovered per-gateway NatGateway-Hours charge less the inter-Availability-Zone transfer the rerouted traffic will now incur at 0.02 per GB - the data-processing charge is never claimed, because that traffic keeps flowing through the survivor. Gateways already idle (under 1 GB and no connections) are excluded and left to the idle-NAT policy. |
VPC {vpc} runs {N} active NAT gateways that together processed only {GB} GB over the last complete invoice month and, at their busiest simultaneous hour, together carried {conns} concurrent connections - far inside what a single NAT gateway carries. Removing this gateway and routing its subnets to the busiest gateway in the VPC recovers {currency} {amount} per month of hourly charge, already net of the inter-Availability-Zone transfer its {GB} GB will incur. The data-processing charge is not claimed as saved - that traffic keeps flowing through the surviving gateway. |
Consolidate this VPC onto its busiest NAT gateway and delete this one after repointing the route tables. |
NatGateway-Bytes GB (invoice month) + simultaneous active connections (Max of the VPC-wide hourly sum) |
|
NAT Gateway S3 or DynamoDB Endpoint Route Gap |
NAT gateways that are the default route for route tables missing the S3 or DynamoDB gateway-endpoint route should be reviewed for adding the free endpoint route. |
A live route-table configuration check paired with the billed NAT data-processing meter for the last complete invoice month; connection telemetry over a configurable lookback period, with 14 days used by default, is used only to exclude already-idle gateways at 75% coverage. Flagged when a gateway is the active default route for at least one route table missing the S3 or the DynamoDB prefix-list route and it processed at least 100 GB. Each service is checked separately per route table, because a route table can carry the S3 endpoint route while still lacking the DynamoDB one, and the account must already show usage of the missing service. The share of NAT bytes actually bound for S3 or DynamoDB cannot be measured without VPC Flow Logs, so the divertible-share knob defaults to 0% and the row reports the measured data-processing charge as a bounded upper bound rather than a fabricated point saving. |
This NAT gateway is the active default route for {N} route table(s) that lack a gateway-endpoint route for {service(s)}, so the {M} subnet association(s) behind them reach {service(s)} across the metered NAT path instead of a free gateway endpoint. The gateway processed {GB} GB in the last complete invoice month, {currency} {cost} of data-processing charge - the measured exposure and the upper bound of what adding the missing endpoint route(s) could recover. The modelled saving is that exposure scaled by the divertible-share setting, which defaults to zero because the S3 and DynamoDB share of NAT traffic cannot be measured without VPC Flow Logs. |
Create an S3 gateway VPC endpoint and associate the affected route table(s), or associate the affected route table(s) with the existing S3 gateway VPC endpoint; a DynamoDB gateway VPC endpoint is named as well when that route is also missing. |
NatGateway-Bytes GB and charge (invoice month) + route-table endpoint-route configuration (per service) |
|
RDS Instance Downsize |
Provisioned RDS DB instances sustaining low CPU while still holding more than half their class memory free should be reviewed for one size step down within the same instance family. |
Mavvrik evaluates CPU and freeable memory over a configurable lookback period, with 14 days used by default; flagged when the 95th percentile of max CPU is within 0%–40% (default) and free memory stays within 50%–100% (default) of class memory at the worst moment of the window. Requires at least 70% telemetry coverage. Memory is used as a safety floor, never as the waste signal, because database engines deliberately fill RAM with buffer cache. The target class must be proven orderable in this tenant - AWS has already billed that exact class in the same account, region and engine major version under the same engine and license terms - and the current class must carry a normalization size factor of at least 8 (at least 2 for burstable classes). Serverless instances and classes with no smaller sibling are excluded. |
Provisioned {engine} instance on class {class} sustained a p95 CPU of {cpu} percent over the last {N} days while still holding {mem} percent of its class memory free at the worst moment of the window. AWS already bills class {target class} in this exact account, region and engine major version under the same {engine} engine and license terms, which proves that class is orderable here. The published on-demand rate ratio between the two classes projects about {pct} percent off the isolated instance-hours charge. |
Downsize {class} to {target class} within the same instance family. |
CPU % (95th pct of Max) + freeable memory as % of class memory (Min) |
|
RDS Graviton Migration |
RDS DB instances on burstable x86 classes with a proven-orderable Graviton equivalent should be reviewed for a like-for-like class swap. |
A configuration and pricing comparison (no utilization), read over the last complete invoice month. Flagged when a db.t2 or db.t3 instance billed at least 5 on its instance-hour meters and the equivalent db.t4g class has already been billed in this exact account, region and engine major version under the same engine and license terms, with a published on-demand rate delta of 2%–40% (default). Scope is deliberately restricted to the burstable t-family, the only family whose x86-to-Graviton price delta is directly measurable on billed rates; m- and r-family x86 classes are excluded pending list-price data, as are engines with no Graviton offering (SQL Server, Oracle) and local-NVMe families (m5d, r5d, m6id, r6id). |
{engine} instance runs the x86 burstable class {class}, for which the Graviton class {target class} has already been billed in this exact account, region and engine major version under the same engine and license terms, at the same vCPU and memory. Graviton classes bill a lower published on-demand rate, so the swap cuts the instance-hour charge with no reduction in capacity, no storage change and no availability change. |
Change the instance class from {class} to {target class}. |
N/A (instance class + engine/license terms + published on-demand rate delta on the proven-orderable meter pair) |
|
RDS Multi-AZ Non-Production (ships disabled by default) |
RDS DB instances running Multi-AZ while tagged as a non-production environment should be reviewed for conversion to single-AZ. |
A configuration and tag check paired with the billed Multi-AZ meters for the last complete invoice month (no utilization). Flagged when an instance carries an environment label matching DEV, TEST, UAT, STAG, SANDBOX or QA on one of the recognised tag keys and billed at least 5 across the isolated Multi-AZUsage, MirrorUsage and Multi-AZ/Mirror storage meters, with 50% of those meters modelled as the saving. An environment label is REQUIRED - an untagged instance is never flagged, since absence of a tag is not evidence of non-production - and labels matching PROD, LIVE or GLOBAL are excluded, which also excludes PREPROD deliberately because it often mirrors production. |
{engine} instance on class {class} is tagged as environment {env} yet runs Multi-AZ, which provisions a synchronous standby replica in a second availability zone and roughly doubles both the instance-hour and the storage charge. It billed {cost} {currency} of Multi-AZ premium in the last complete invoice month. Development and test databases generally do not require that availability guarantee. |
Convert the non-production DB instance from Multi-AZ to single-AZ. |
N/A (Multi-AZ configuration + environment tag + isolated Multi-AZ/Mirror meter cost) |
|
ElastiCache Redis OSS to Valkey Engine Migration |
ElastiCache cache clusters still running the Redis OSS engine should be reviewed for an in-place upgrade to the cheaper Valkey engine. |
A configuration and pricing comparison (no utilization). Mavvrik reads the isolated NodeUsage meter cost over the last complete invoice month and applies the 20 percent rate discount AWS prices Valkey below Redis OSS for the identical node type in the identical region, capped at the resource amortized cost. Flagged when the cluster engine is redis at version 5.0.6 or later (a full-semver source gate) and isolated node-hour spend is at least 5 per month (default). The ExtendedSupport surcharge meter is excluded so the two ElastiCache levers stay disjoint, and commitment coverage is computed and emitted so a reserved or savings-plan-covered node is reviewed rather than silently re-engined. |
Cache cluster runs the Redis OSS engine (version {version}) on {node type} at a billed rate of {rate} per node hour. Isolated node-hour spend is {cost} {currency} per month, so moving this node to Valkey {target version} recovers {savings} {currency} per month with no change in capacity, node type or connection string. Cluster shape: {shape}. |
Upgrade this cache cluster in place from Redis OSS {version} to Valkey {target version} on the same {node type} node type. Replication group member: upgrade the whole replication group in one modify call; reads stay available and writes pause only for the few-second failover. Standalone cache: add it to a replication group first, then schedule the upgrade as a planned outage window. |
N/A (engine version semver + node type + isolated NodeUsage meter cost and billed rate) |
|
OpenSearch Oversized Dedicated Master Tier |
OpenSearch domains whose dedicated master instance type is larger than the minimum AWS recommends for the cluster node count should be reviewed for a smaller master tier. |
A configuration and pricing comparison (no utilization). Mavvrik compares the domain's dedicated master type against the AWS-recommended minimum for its node count (the m5.large.search class for 1–10 instances, the c5.2xlarge.search class for 11–30) and multiplies master node-hours by the hourly rate delta down to the recommended-minimum class (m6g.large), using billed rates from the last complete invoice month. Flagged when estimated savings reach at least 25 per month (default), and only for domains with three or more dedicated master nodes. Commitment-covered node-hours (rate 0 / cost 0) are excluded, and the delta is measured INCREMENTALLY net of any Graviton generation swap the previous-generation lever already recommends for the same domain, so the two never double-count. |
This OpenSearch domain coordinates {N} data nodes with {M} dedicated master nodes of type {master type}, which is above the class AWS recommends as the minimum for a cluster of this size. Master nodes bill at the full per-node rate, so {hours} master node-hours run at {rate} per hour where the recommended-minimum class costs {target rate}. Resizing the master tier recovers {savings} {currency} per month, measured net of any generation swap already recommended for this domain. |
Resize the dedicated master tier from {master type} to {target type} using a managed blue/green deployment. |
Master node-hours (sum) + hourly rate delta (avg billed rate, paid hours only) |
|
OpenSearch Hot EBS Over-Allocation |
OpenSearch domains provisioning far more hot EBS storage than the data they actually hold should be reviewed for a smaller per-node volume. |
Mavvrik evaluates per-node FreeStorageSpace over a configurable lookback period, with 14 days used by default, deriving peak used storage from the fullest data node at its fullest observed moment against the AWS-documented usable capacity (volume size less the 20-percent-up-to-20-GiB instance reserve). Flagged when peak used storage is 0–40 percent of usable capacity (default) and at least 24 hours of storage telemetry exist in the window. The target is peak usage plus 100 percent headroom rounded to 10 GB, and savings are the domain ES:GP2-Storage / ES:GP3-Storage meter from the last complete invoice month scaled by the proportional volume reduction, requiring at least 5 per month. Nearly-full domains, domains without a full day of storage telemetry, and any target below the AWS 10 GB minimum volume size are excluded. |
This OpenSearch domain provisions {GB} GB of EBS per data node across {N} nodes, but over the last {days} days the fullest node at its fullest moment still had {free GB} GB free. Peak used storage is {used GB} GB against {usable GB} GB of usable capacity, or {pct} percent. The domain EBS storage meter is {cost} {currency} per month and bills provisioned capacity rather than used capacity, so the shrink recovers {savings} {currency} per month. |
Reduce the OpenSearch domain EBS volume size from {current GB} GB to {target GB} GB per data node using a managed blue/green deployment, after an UpdateDomainConfig dry run. |
FreeStorageSpace (per-node min) + hours with data; ES:GP2/GP3-Storage meter (last invoice month) |
|
OpenSearch gp2 Storage Conversion |
OpenSearch domains still backing their EBS volumes with gp2 should be reviewed for conversion to the cheaper, higher-baseline gp3 volume type. |
A configuration and pricing comparison (no utilization). Mavvrik reads the domain ES:GP2-Storage meter over the last complete invoice month and multiplies it by the tenant's own observed per-region gp3-versus-gp2 rate discount, never an estimate from provisioned GB. Flagged when the observed discount is 5–20 percent (default) and estimated savings are at least 2 per month. Volumes above 1,000 GB are excluded, because there the gp2 capacity-linked IOPS baseline (3 IOPS per GB) exceeds the gp3 free 3,000 and matching it would require paid provisioned IOPS. |
This OpenSearch domain still backs its {GB} GB EBS volumes with gp2. On your own bill in this region gp3 is {pct} percent cheaper per GB, and it also raises the performance floor to a flat 3000 IOPS and 125 MB/s at any volume size, versus the gp2 3 IOPS per GB. The domain gp2 storage meter is {cost} {currency} per month, so the conversion recovers {savings} {currency} per month with no capacity change. |
Change the OpenSearch domain EBS volume type from gp2 to gp3 using a managed blue/green deployment. |
N/A (EBS volume type + volume size); ES:GP2-Storage meter and observed per-region gp3-vs-gp2 rate delta |
|
OpenSearch Previous-Generation Node Migration |
OpenSearch domains still billing node-hours on previous-generation Intel instance families should be reviewed for their same-size Graviton equivalents. |
A configuration and pricing comparison (no utilization). Mavvrik multiplies observed PAID ESInstance node-hours from the last complete invoice month by the hourly rate delta between each previous-generation Intel family (m3/m4/m5, c4/c5, r4/r5, i2/i3) and the cheapest same-size Graviton part (m6g/m7g/m8g, c6g/c7g/c8g, r6g/r7g/r8g) billed in the same region on the tenant's own meter, summed over every qualifying instance type so data nodes and dedicated master nodes are both swept by the one meter. Flagged when estimated savings are at least 10 per month (default). Commitment-covered node-hours (rate 0 / cost 0) are excluded, the swap holds vCPU and memory constant so no capacity is removed, and it is gated on the domain engine version actually supporting the target Graviton generation. |
This OpenSearch domain bills {N} node-hours on previous-generation instance types ({migration map}), with a configured data-node type of {data node type}. The same-size Graviton parts are cheaper on your own billed rate in this region, and the domain engine version is confirmed to support them, so the swap recovers {savings} {currency} per month with no capacity change. |
Change the OpenSearch domain instance types to their Graviton equivalents ({migration map}) using a managed blue/green deployment. |
Paid node-hours (ESInstance meter, sum) + hourly rate delta (avg billed rate) |
|
Kinesis On-Demand to Provisioned Conversion |
On-demand Kinesis data streams ingesting a negligible fraction of a single shard should be reviewed for conversion to provisioned mode with a small fixed shard count. |
Mavvrik evaluates estimated ingest against one shard's write capacity over the last 3 complete invoice months, because no CloudWatch telemetry exists for this resource type; ingest is derived from PutRequestPayloadUnits at the documented 25 KB payload unit plus OnDemand-BilledIncomingBytes, against 2,566.4 GB per shard-month. Flagged when estimated monthly ingest is 0–5 percent of one shard (default), the stream is at least 60 days old, and it was billed in every month of the window. The break-even is proved per region from the tenant's own billed Storage-ShardHour rate rather than a list price, targeting 1 provisioned shard; a region with no observed shard-hour rate is excluded outright. The action is explicitly two-step because a mode switch alone inherits the on-demand four-shard count and would raise cost. |
Mavvrik cost engineers found this Kinesis data stream ({stream}) running in ON_DEMAND capacity mode in {region} while ingesting an estimated {pct} percent of one shard monthly write capacity over {months} complete invoice months. On-demand bills a flat {od rate} {currency} per stream-hour for AWS to manage shards automatically, which is worth paying for spiky traffic; this tenant's own billing in the same region shows provisioned shard-hours at {shard rate}, so {N} provisioned shard(s) cost {target rate} per hour and the break-even sits well above the throughput this stream carries. |
Convert this stream to provisioned capacity mode in two steps: call UpdateStreamMode to switch to PROVISIONED, then call UpdateShardCount down to {N} shard(s). Both steps are required - a mode switch alone keeps the inherited shard count and costs more than on-demand. |
Estimated ingest vs one shard (PutRequestPayloadUnits + OnDemand-BilledIncomingBytes meters) + OnDemand-StreamHour and Storage-ShardHour rates (invoice months) |
|
Kinesis Extended Retention on Idle Stream |
Kinesis data streams paying the extended-retention premium while ingesting almost nothing should be reviewed for a return to the default 24-hour retention window. |
Mavvrik evaluates the Extended-ShardHour meter and estimated ingest over the last 3 complete invoice months, because no CloudWatch telemetry exists for this resource type; ingest is derived from PutRequestPayloadUnits at the documented 25 KB payload unit plus OnDemand-BilledIncomingBytes, against 2,566.4 GB per shard-month. Flagged when estimated monthly ingest is 0–5 percent of one shard (default) and extended-retention shard-hours are billed. Retention period is not exposed in configuration_json on this tenant, so the billed meter is the only enablement signal. Savings are the isolated Extended-ShardHour meter only - the base shard-hour charge and the separate LongTermRetention-ByteHrs meter are both deliberately excluded. |
Mavvrik cost engineers found this Kinesis data stream ({stream}, {mode} mode) billing {hours} extended-retention shard-hours over {months} complete invoice months while ingesting an estimated {pct} percent of one shard monthly write capacity. Extended retention raises the record window from the default 24 hours to 7 days and bills a second, higher per-shard-hour meter for every shard, continuously - the charge is per shard-hour, not per byte, so a near-empty stream pays the full premium to retain almost nothing. Here the premium is {premium} {currency} per month against a base shard charge of {base} {currency}. |
Reduce this stream's retention period back to the default 24 hours with DecreaseStreamRetentionPeriod to stop the extended-retention shard-hour meter. |
N/A (Extended-ShardHour and Storage-ShardHour meters + estimated ingest from PutRequestPayloadUnits / OnDemand-BilledIncomingBytes, invoice months) |
|
S3 Bucket Intelligent-Tiering Adoption |
S3 buckets paying STANDARD-class storage-time with no enabled lifecycle transition rule should be reviewed for a Days-0 Intelligent-Tiering transition. |
Mavvrik evaluates bucket size, object count and Requests-Tier2 (GET/SELECT) volume over a configurable lookback period, with 30 days used by default. Flagged when the bucket carries no enabled lifecycle transition rule, average object size is at least 1,024 KB (default, below which the monitoring-and-automation fee outweighs the tiering saving), STANDARD storage-time is at least 10 per month, measured read activity is 0–1 GETs per object per month (default), and the bucket is at least 90 days old. No Storage Class Analysis, Storage Lens or object-level access log exists in this data plane, so no per-bucket cold-byte share is measured: the SCORED savings use a disclosed conservative 25 percent cold share, while a second tenant-measured high-bound share - the real cold byte-hour share this tenant achieves on buckets that already adopted Intelligent-Tiering, capped at 90 percent and falling back to 75 percent when no such bucket exists - is reported alongside as a range and never folded into the scored number. Savings are fractional on the STANDARD storage-time meter only, net of the Intelligent-Tiering Frequent Access premium (3.5 percent) on the hot share and the projected per-object monitoring fee, floored at zero and capped at the meter. |
Mavvrik cost engineers validated on your live data that S3 bucket {bucket} pays {cost} {currency} per month of STANDARD storage-time with no enabled lifecycle transition rule, holding about {N} objects at an average size of {KB} KB and a measured read rate of {gets} GET or SELECT requests per object per month. A conservative assumption that {low pct} percent of bytes are cold recovers about {savings} {currency} per month, the scored estimate. This tenant's own measured Intelligent-Tiering outcome elsewhere in the fleet - buckets that already use it end up with about {high pct} percent of bytes in the cold tier - would recover about {high savings} {currency} per month if this bucket behaves similarly. |
Add an S3 Lifecycle rule with a Days-0 transition to INTELLIGENT_TIERING on this bucket. Do not enable the opt-in Archive Access or Deep Archive Access tiers - those add asynchronous restore latency and are out of scope. |
Average object size + object count (bucket size / NumberOfObjects) + GETs per object per month (Requests-Tier2 meter) + STANDARD storage-time meter |
|
S3 Bucket Recurring Early-Delete Penalty |
S3 buckets repeatedly charged the AWS early-delete fee should be reviewed for a mis-sequenced lifecycle transition into a minimum-duration storage class. |
Mavvrik evaluates the billed EarlyDelete usage-type over the last 6 complete invoice months. Flagged when the penalty is charged in at least 2 of those months (default) and averages at least 1 per month. The class-minimum fact is read from the billed meter (SIA / ZIA / GIR / GDA / bare Glacier) rather than assumed from the lifecycle config, which can be stale or already corrected, and detection is per lifecycle RULE and per storage-class TRANSITION so a transition day is paired only with that same rule's own expiration day. Because AWS bills the fee as exactly the prorated remainder of the class minimum at the ordinary storage rate, savings are capped at both the bucket's mean penalty and its total cost, and a bucket whose visible rule shows no provable cheaper alternative emits 0 rather than the full penalty. |
Mavvrik cost engineers validated on your live data that S3 bucket {bucket} was charged the AWS {class} early-delete penalty in {N} of the last {M} complete invoice months, averaging {cost} {currency} per month. AWS defines that fee as the exact prorated remainder of the {min} day minimum billable duration for {class}, billed at the same rate as ongoing storage - waiting out the remaining days reclassifies the identical charge as ordinary storage cost rather than lowering the bill. Mis-sequenced rule: lifecycle rule {rule} transitions objects to {class} at day {T} and expires them at day {E} - {shortfall} days short of the {min} day minimum. Rule already correct: the current rule already sequences transition and expiration past the minimum, so the charge is not explained by the visible rule; no dollar figure is claimed. No matching rule: no enabled lifecycle rule currently transitions objects into {class} on this bucket, so review whether objects are uploaded directly into it or a noncurrent-version rule applies; no dollar figure is claimed. |
Review the AWS early-delete penalty on this bucket. Extending retention past the storage class minimum does not lower the total bill - it only changes whether the charge is billed as a lump-sum penalty or as ordinary storage cost. The total cost only goes down by skipping the transition into that class, or moving to Intelligent-Tiering instead, which has no minimum billable duration. |
N/A (EarlyDelete usage-type meter over 6 invoice months + per-rule lifecycle transition/expiration days) |
|
SNS High-Volume Publish Batching |
High-volume SNS topics publishing single messages through the plain Publish API should be reviewed for adoption of PublishBatch. |
Mavvrik evaluates the SNS Publish request meter over the last complete invoice month. Flagged when the topic bills at least 10,000,000 publish requests (default), never calls the PublishBatch API, and the measured ratio of billable requests per delivered message is 0–1.5 (default) - the ratio guard drops topics where 64 KB payload chunking rather than message count is driving the request meter. A positive-delivery gate keeps the candidate set provably disjoint from the SNS publish-to-nowhere lever. Savings are a fraction of the Publish request meter only, at a configured batch factor of 3 messages per call; delivery, payload-GB and data-transfer meters are byte-proportional and are excluded. |
Mavvrik cost engineers validated on your live data that SNS topic {topic} published {N} messages in the last complete invoice month, billing {cost} {currency} on the request meter, and never called the PublishBatch API. The measured ratio of {ratio} billable requests per delivered message confirms each publish is billed as a single request rather than being inflated by 64 KB payload chunking, so messages on this topic can be packed into batches. At the configured factor of {factor} messages per call, {savings} {currency} per month of request spend is recoverable. |
Switch the producing application to the SNS PublishBatch API and buffer messages so each call carries a full batch of up to 10. No SNS topic, subscription or delivery configuration is changed. |
Publish requests (invoice month) + billable requests per delivered message ratio |
|
Secrets Manager API Call Storm |
Secrets Manager secrets whose monthly metadata List API volume is a statistical outlier should be reviewed for a client-side caching fix. |
A billing-meter plus configuration check with no telemetry (metrics_hourly carries no rows for this resource type); Mavvrik evaluates the per-secret List meter over the last complete invoice month, the in-progress month excluded because it would understate a storm. Flagged when metadata List calls reach at least 100,000 in the month (default) - a floor set above this tenant's p99 of per-secret List volume, so only true outliers survive. Savings are the volume ABOVE a retained allowance of 100,000 calls per month, priced at the AWS list rate of USD 0.05 per 10,000 requests and capped at the secret's own List meter, so a properly cached client is still budgeted for. Account-level ListSecrets lines carry no resource id and are quarantined outright, never attributed to a secret. |
Mavvrik cost engineers validated on your live data that secret {secret} was billed for {N} Secrets Manager metadata List API calls in the last complete invoice month, against {M} GetSecretValue retrievals, costing {cost} {currency} on the {operation} meter alone. Metadata List calls return only metadata and never the secret value, so this traffic pays to ask about the secret rather than to read it. No retrievals: no value was retrieved from this secret at all in that month, which is the signature of an SDK retry loop or a health check pointed at the wrong API. Negligible retrievals: retrieval volume is negligible next to the metadata polling, so the calls are not serving the application. |
Enable the AWS Secrets Manager client-side caching component in the calling application, or hoist the secret lookup out of the request path so it happens once at startup, so metadata List calls are no longer made per request. |
N/A (List vs GetSecretValue API call meters, last complete invoice month) |
|
Bedrock Global Cross-Region Inference Migration |
Bedrock inference profiles billing Claude consumption on geographic cross-Region inference meters should be reviewed for the cheaper global cross-Region routing mode. |
A configuration and pricing comparison (no utilization). Mavvrik isolates geographic-CRIS meter spend over the last complete invoice month and applies the rate delta measured on this tenant's own rate card, where every matched Global-versus-Regional meter pair sits at exactly 9.0909 percent (Global = Regional divided by 1.1), capped at the profile cost. Flagged when geographic-CRIS spend is at least 25 per month (default) and the model matches the Claude families that support global routing. Nothing about the prompt, the model tier or the response quality changes - only the routing mode. |
Mavvrik cost engineers found this Amazon Bedrock inference profile billing {cost} {currency} of GEOGRAPHIC cross-Region inference on {model} in the last complete invoice month, across {qty} metered token units. On this tenant's own rate card the differential is not an estimate: the matched Global-versus-Regional meter pairs all sit at exactly 9.0909 percent, so Global is Regional divided by 1.1. Applying {pct} percent to the geographic-CRIS base recovers {savings} {currency} per month. |
Swap the model identifier to its global cross-Region inference profile equivalent so the same tokens bill on the cheaper global routing rate. |
Geographic-CRIS meter spend (amortized) + metered token units (last complete invoice month) |
|
Bedrock Prompt Cache Adoption on Zero-Hit Profiles |
Bedrock inference profiles consuming large volumes of uncached input tokens while recording no cache-read meter at all should be reviewed for prompt cache checkpoints. |
Mavvrik evaluates uncached input-token spend and the cache-read meter over the last complete invoice month. Flagged when uncached input spend is at least 100 (default), the measured cache-hit ratio is 0–5 percent (default), and at least 1 unit of input-token quantity is billed. Bedrock prices cache-write input tokens at about 1.25x and cache-read at about 0.1x the standard input rate, so savings assume a stable cacheable-prefix share of 30 percent reused 4 times per write. That share cannot be proven from billing data, so this is an L3 unit-economics lever emitted as a BOUNDED RANGE (15 percent at reuse 2 through 50 percent at reuse 10) with the optimistic end carried separately, and it requires engineer sign-off before it reaches the customer. |
Mavvrik cost engineers found this Amazon Bedrock inference profile buying {cost} {currency} of UNCACHED input-token spend on {model} in the last complete invoice month while recording a cache-hit ratio of {pct} percent, so it is claiming none of the prompt-caching discount. Assuming {share} percent of the input is a stable, cacheable prefix, the recoverable amount is {savings} {currency} per month, within a bounded range of {low} to {high} {currency}. The cacheable-prefix fraction is an explicit assumption and not a measurement, which is why a range is reported. |
Add prompt cache checkpoints to the stable prefix of the requests sent through this Bedrock inference profile. |
Uncached input-token spend and quantity + cache-read token quantity + cache-hit ratio (last complete invoice month) |
|
Bedrock Non-Production Inference Profile Overspend (ships disabled by default) |
Non-production Bedrock inference profiles whose token spend approaches or exceeds that of their matching production peer should be reviewed for throttling, sampling or scheduling down. |
A cost comparison over the last complete invoice month against the production profile for the same workload. Environment tier and workload identity are parsed from the profile name suffix (dev / preprod / staging / test / uat / sandbox / qa versus prod / production), because tags are empty on every profile on this estate. Flagged when the non-production spend above a budget of 25 percent of its production peer (default) is at least 25 (default), and only for profiles at least 30 days old. Bedrock on-demand inference is billed purely per token with no standing charge, so the excess is recoverable in full with no production impact. |
Mavvrik cost engineers compared this non-production Amazon Bedrock inference profile against its production peer. The {tier} profile {profile} billed {cost} {currency} in the last complete invoice month, which is {pct} percent of the {peer cost} {currency} billed by the matching production workload {base name}. A non-production environment budgeted at {budget pct} percent of production would spend {budget} {currency}, leaving {savings} {currency} of recoverable excess. |
Throttle, sample or schedule down this non-production Bedrock inference profile so its token spend stays within the configured share of its production peer. |
Non-production vs production profile amortized token spend and their ratio (last complete invoice month) |
|
Glue Non-Production FLEX Execution Class (ships disabled by default) |
Non-production Glue Spark ETL jobs still running on the STANDARD execution class should be reviewed for the cheaper FLEX execution class. |
A configuration and pricing comparison (no utilization). Mavvrik applies the published FLEX rate delta of 34 percent (0.29 versus 0.44 USD per DPU-hour) to the job's ETL DPU-Hour meter spend from the last complete invoice month. Flagged when the job meets every AWS-documented FLEX eligibility condition - command type glueetl, Glue version 3.0 or later, and worker type G.1X or G.2X - and its monthly spend is at least 5 (default). Non-production is classified from the usage account name (dev, test, stg, staging, sandbox, nonprod, qa, uat, preprod, sit) because environment tags are effectively absent on this estate, so production jobs are excluded unless the tenant widens that pattern and nominates which production jobs tolerate a variable start. |
Mavvrik cost engineers validated this AWS Glue Spark job as FLEX-eligible: command glueetl, Glue version {version}, worker type {worker type} with {N} configured workers, currently billed on the STANDARD execution class in non-production account {account}. It billed {cost} {currency} of ETL DPU-Hour spend across {DPU hours} DPU-hours in the last complete invoice month. AWS bills FLEX at 0.29 USD per DPU-hour versus 0.44 USD on STANDARD, a {pct} percent reduction on identical work, with no change to the script, worker count, bookmarks or auto scaling. |
Switch the job execution class from STANDARD to FLEX, or set ExecutionClass to FLEX on this non-production Glue Spark job. |
N/A (command type + Glue version + worker type); ETL DPU-Hour meter spend and DPU-hours (last complete invoice month) |
AWS Orphan Recommendations
AWS resources that are not attached to active workloads and may continue to incur unnecessary costs.
|
Recommendation |
Description |
How Recommendation Is Generated |
Reason |
Action |
Metrics |
|---|---|---|---|---|---|
|
EBS Volume Unattached |
Volumes sitting in the available state with no instance attached should be reviewed for snapshot-then-delete or outright deletion. |
A configuration and cost check (no lookback). Two independent configuration fields must agree before a volume is flagged - State = available AND an empty Attachments array - and a maturity gate requires the volume to be at least 7 days old by its provider CreateTime, with an unparseable CreateTime excluded rather than assumed old. Orchestrator-managed volumes (Kubernetes, Karpenter, EKS) and DR/standby-tagged volumes are excluded because they are detached by design. Two savings figures are carried rather than one: the primary row reports archive-then-delete, net of the ongoing snapshot storage it creates at 0.05 per GB-month, and the target options carry the higher-risk delete-without-snapshot figure at the full volume cost. |
This {volume type} volume of {N} GiB has been in the available state with no instance attached for {N} days and keeps billing its full provisioned capacity. Restore path: {restore path}. |
Take a snapshot, then delete the unattached volume, or delete the unattached volume without taking a snapshot (higher risk, permanent data loss). |
N/A (volume state + attachments array + provisioned size) |
|
Classic Load Balancer Orphaned |
Classic Load Balancers with no registered backend instances should be reviewed for deletion, since they cannot route to anything yet keep paying the fixed hourly meter. |
Mavvrik evaluates registered backends, CloudWatch host counts and the AWSELB data-processing meter over a configurable lookback period, with 14 days used by default. Flagged when the configuration records zero registered instances, CloudWatch publishes a maximum healthy host count of 0 and a maximum unhealthy host count of 0 across the whole window - AWS emits those metrics only when instances are registered, so their absence corroborates the configuration rather than a stale snapshot - request count is 0 (default) and data processed is at most 0.01 GB. Requires at least 50% telemetry coverage of the window, at least 25 billed load-balancer-days, at least 1 monthly cost, and a load balancer created at least 14 days ago. Load balancers tagged as managed by Kubernetes, CloudFormation or Elastic Beanstalk are annotated with a reviewer note rather than excluded, since an orphan under infrastructure-as-code is still a valid finding. |
Mavvrik cost engineers found this {scheme} Classic Load Balancer has no registered backend instances in its configuration, so it cannot route to anything, yet it keeps paying the fixed classic per-load-balancer-hour meter. Across the last {N} days CloudWatch published no healthy host count and no unhealthy host count for it - AWS reports those metrics only when instances ARE registered, so their absence confirms the configuration rather than a stale snapshot - and it served {requests} requests. |
Delete the orphaned Classic Load Balancer to stop both the fixed hourly meter and the data-processing meter. |
Healthy/unhealthy host count (Max) + request count (Sum) + AWSELB DataProcessing GB meter |
|
EBS Orphaned Snapshot Chain |
Snapshot chains whose source volume no longer exists, with no protected member surviving, should be reviewed for deletion of the whole chain in one action. |
A configuration and cost check with no telemetry; volume absence is corroborated over invoice months rather than a metric lookback. Volume absence must pass TWO independent tests - missing from the live volume inventory AND no billing row for at least 3 months - because the volume inventory on this data plane covers only a fraction of billed volume ids. The newest snapshot in the chain must be at least 90 days old, and every surviving member must be unprotected: not an AWS Backup recovery point, not under a Data Lifecycle Manager policy (markers aws:backup:, aws:dlm:, dlm:managed) and not backing an AMI (descriptions matching CreateImage, DestinationAmi, SourceAmi). Snapshots carrying a placeholder vol-ffff volume id are excluded, and members must carry a nonzero billed snapshot cost. Because the entire chain goes at once, no later snapshot survives to inherit the shared blocks, so the full billed cost of every member is genuinely recovered. |
This {N} GiB snapshot was taken {N} days ago from volume {volume}, which is absent from the live volume inventory and {billing history}. It is one of {N} live snapshots of that gone volume, none of which is an AWS Backup recovery point, under a Data Lifecycle Manager policy or backing an AMI, so the entire chain can be deleted in one action and the full billed cost recovered - with no surviving member, no shared block can be re-attributed. |
Sole member: Delete this sole remaining snapshot of retired volume {volume}. Chain: Delete all {N} snapshots of retired volume {volume} together, including this one. |
N/A (snapshot age + source-volume existence + protection markers + billed snapshot cost) |
|
EBS Stale Sole Ad-hoc Snapshot |
One-off manual snapshots that are the only live snapshot of a volume still in service should be reviewed for deletion once they have outlived their use as a rollback point. |
A configuration and cost check (no telemetry, no metric lookback). Fires only when the source volume IS present in the live volume inventory, has exactly one live completed snapshot, is under no automated backup regime at all - no AWS Backup recovery point, no Data Lifecycle Manager policy (markers aws:backup:, aws:dlm:, dlm:managed), no AMI linkage (descriptions matching CreateImage, DestinationAmi, SourceAmi) - and that snapshot is more than 180 days old. A chain depth of exactly one is what makes the full-recovery claim safe: with no sibling there is no later snapshot for the incremental blocks to be re-attributed to. Snapshots carrying a placeholder vol-ffff volume id are excluded, and the lever is mutually exclusive with EBS Orphaned Snapshot Chain by an opposite source-volume-existence test. |
This {N} GiB snapshot is the ONLY live snapshot of volume {volume}, which is still in service, and it was taken {N} days ago. The volume is under no automated backup regime at all - no AWS Backup recovery point, no Data Lifecycle Manager policy, no AMI linkage - so this is a one-off manual copy that outlived its purpose rather than part of a retention scheme. |
Confirm the one-off snapshot is no longer needed as a rollback point, then delete it. |
N/A (snapshot age + chain depth + backup-regime markers) |
|
SNS Topic With No Subscribers |
Topics billing material Publish request spend while delivering nothing should be reviewed for a dead producer or a subscription removed by mistake. |
Mavvrik evaluates the publish and delivery meters over the last 6 complete invoice months, with publish requests and publish cost taken from the last complete month. Flagged when the topic emits no delivery meter row of any protocol across the whole 6-month window while billing at least 1,000,000 publish requests and at least 5 of publish cost (defaults). A publish-history gate requires the topic to have been publishing in at least 2 of those months, ruling out an integration wired up mid-window whose deliveries have simply not started. The signal is valid on this tenant because the free delivery protocols demonstrably do emit metered rows at zero cost, so an absent delivery row means nothing was delivered rather than that delivery went unmetered; asset inventory carries no subscription data, so zero subscriptions must be confirmed against the live SNS API before any change. |
Mavvrik cost engineers validated on your live data that SNS topic {topic} billed {N} publish requests in the last complete invoice month, costing {cost} {currency}, while emitting no delivery meter row of any protocol across the last {N} complete invoice months. The topic has been publishing in {N} of those months, so this is not a newly wired integration, and SNS bills the publish request whether or not the topic has a subscription - so every one of these messages was discarded and paid for. |
Confirm with the owning team whether these messages were supposed to reach a subscriber; if the producer is dead, switch it off, and if the subscription was removed by mistake, restore it. Do NOT delete the topic: the producer's publish calls would then fail with NotFound. |
Publish request meter + per-protocol delivery meters (invoice months) |
AWS License Recommendations
AWS resources that may be paying a more expensive licensing or support model than necessary — typically an extended-support surcharge for running a past-end-of-life engine or cluster version. These recommendations help reduce licensing costs without changing or removing the active workload.
|
Recommendation |
Description |
How Recommendation Is Generated |
Reason |
Action |
Metrics |
|---|---|---|---|---|---|
|
RDS Engine on Extended Support |
DB instances paying the RDS Extended Support surcharge for a past-EOL major engine version are on a more expensive support model than necessary and should be reviewed for an in-place engine upgrade. |
Mavvrik evaluates the RDS ExtendedSupport usage-type meter over the last complete invoice month. Flagged when the billed surcharge is at least 10 (default) AND the current configuration snapshot still shows an end-of-support engine version - the default pattern covers versions beginning 5.7, 8., 9. or 10 through 13 - so an instance upgraded mid-month, whose surcharge has already stopped, is never flagged. Only live, non-terminated DB instances with a nonzero amortized cost qualify. Savings are the isolated per-vCPU-hour (provisioned) or per-ACU-hour (Serverless v2) surcharge meter only, recoverable with no change to instance size, storage or availability. |
Engine {engine} version {version} is past its end of standard support, so AWS is levying an RDS Extended Support surcharge of {cost} {currency} per month on top of the normal instance charge. The surcharge buys only continued patching of an obsolete version and stops the moment the engine is upgraded to a version under standard support. |
Upgrade {engine} from version {version} to a major version under standard support. |
N/A (ExtendedSupport usage-type meter + configured engine version) |
|
EKS Cluster on Extended Support |
Clusters billing the EKS extended-support surcharge for a past-EOL Kubernetes minor version are paying a premium support rate for the same control plane and should be reviewed for an upgrade. |
Mavvrik evaluates the EKS extendedSupport usage-type meter over the last complete invoice month and maps the cluster's current Kubernetes minor version through a shared end-of-standard-support calendar (1.30 = 2025-07-23, 1.31 = 2025-11-26, 1.32 = 2026-03-23, 1.33 = 2026-07-29, 1.34 = 2026-12-02, 1.35 = 2027-03-27, 1.36 = 2027-08-02). Flagged when the billed surcharge is at least 5 (default) AND the version maps to an end-of-support date on or before today, so a cluster upgraded mid-month is never flagged for a charge it has already stopped paying. Savings are the isolated extendedSupport meter ONLY - the base 0.10 USD per-cluster-hour perCluster meter survives the upgrade and is never recoverable, and claiming the whole cluster cost would overstate a partial month roughly threefold; the full-month run rate is projected over 730 hours at the observed surcharge rate. The recommended target is the lowest version holding at least 180 days of remaining standard support, never merely the next minor, and clusters with no such target are dropped. |
EKS cluster {cluster} runs Kubernetes {version}, which reached end of standard support on {date}, so AWS is billing a separate extended-support surcharge meter of {cost} {currency} for the last complete invoice month on top of the unchanged base control-plane charge. Upgrading the control plane to Kubernetes {target version}, which holds standard support until {date}, stops the surcharge and returns the cluster to the base rate; the base perCluster charge itself continues and is not recoverable. |
Upgrade the EKS control plane of cluster {cluster} from Kubernetes {version} to {target version}, which is {N} sequential minor upgrades. |
extendedSupport meter cost + surcharge hours (invoice month) + perCluster base hours |
|
EKS Version Approaching End of Standard Support |
Clusters weeks away from automatically starting to pay the EKS extended-support surcharge should be reviewed for a control-plane upgrade while the more expensive support rate can still be avoided outright rather than recovered. |
A configuration and calendar check against the last complete invoice month's perCluster meter (no utilization lookback). The cluster's current Kubernetes minor version is mapped through the shared end-of-standard-support calendar (1.30 = 2025-07-23 through 1.36 = 2027-08-02); flagged when remaining runway is 1–180 days (default) and the cluster billed at least 100 base cluster-hours in the last complete invoice month. Clusters already billing a surcharge meter are excluded - those belong to EKS Cluster on Extended Support - and clusters with no target holding at least 180 days of remaining standard support are dropped. Reports ZERO recoverable savings by design, since no surcharge meter exists on these clusters yet; the avoidance figure is projected at 0.50 USD per cluster-hour over 730 hours and carried in the prose, and rows are ranked by deadline urgency rather than dollar size. |
EKS cluster {cluster} runs Kubernetes {version}, which reaches end of standard support on {date}, {N} days from now. On that morning AWS begins billing the extended-support surcharge automatically and without opt-in, taking the control-plane rate from 0.10 to 0.60 USD per cluster-hour and adding about {cost} {currency} per month for this cluster alone. This finding reports zero recoverable savings by design: the figure is spend that starts on a named future date, not spend already incurred. |
Upgrade the EKS control plane of cluster {cluster} from Kubernetes {version} to {target version} before {date}, which is {N} sequential minor upgrades. |
N/A (configured Kubernetes version vs end-of-support calendar + perCluster base hours) |
|
ElastiCache Redis OSS Extended Support Surcharge |
Cache clusters on a Redis OSS major version past end of standard support are billing a per-node-hour Extended Support surcharge that buys no extra capacity, and should be reviewed for an upgrade to a supported version or to Valkey. |
Mavvrik evaluates the ElastiCache ExtendedSupport usage-type meter over the last complete invoice month for live redis-engine cache clusters. Flagged when the cluster's major engine version is at or below 5 - the end-of-standard-support major - and the billed surcharge is at least 1 (default). Savings are the isolated ExtendedSupport meter, disjoint from the NodeUsage base used by the Redis-to-Valkey migration lever, and capped at the resource amortized cost. A second, zero-savings advisory branch fires for clusters on major version 6, which enters paid Extended Support on 2027-02-01, whenever that date falls inside the 6-month planning horizon. Valkey 7.2 is the recommended target because it carries no Redis OSS extended-support schedule at all. |
Surcharge billed: Cache cluster runs Redis OSS {version} on {node type}, a major version past its end of standard support, so AWS bills the Extended Support surcharge meter {usage type} at {rate} per node hour on top of normal node usage. That surcharge is {cost} {currency} per month, buys no extra capacity or capability, and escalates each year through Extended Support Years 1, 2 and 3. Planning advisory: Cache cluster runs Redis OSS {version} on {node type}. Redis OSS major version {N} enters paid Extended Support on {date}; no surcharge is billed on this node today and no savings are claimed. |
Surcharge billed: Upgrade this cache cluster from Redis OSS {version} to a supported version, preferably {target}, to remove the Extended Support surcharge permanently. Planning advisory: Plan an engine-version upgrade before {date}, when Redis OSS major version {N} starts accruing the surcharge. |
N/A (ExtendedSupport usage-type meter + configured Redis OSS major version) |
|
OpenSearch Extended Support Surcharge |
Domains billing the OpenSearchExtendedSupport meter are paying a flat surcharge to stay on an out-of-support engine version and should be reviewed for a major-version upgrade. |
Mavvrik evaluates the OpenSearchExtendedSupport usage-type meter over the last complete invoice month; the BILLED meter is the sole trigger, never the engine version string. Flagged when the surcharge totals at least 25 (default) with a metered quantity above zero, the domain is live with a nonzero amortized cost, and the domain reports itself upgrade-eligible (service software UpdateStatus = ELIGIBLE or UpdateAvailable = true). Savings are the full Extended Support meter and nothing else - no change to instance size, storage or capacity. The savings floor is deliberately high because the action is a one-way, potentially multi-hop major-version migration with no rollback short of restoring a snapshot into a new domain, so the lever stays silent until the surcharge is genuinely material. |
This OpenSearch domain runs engine version {version}, which has fallen out of standard support, so AWS is billing a flat Extended Support surcharge of {currency} {cost} per month across {N} Normalized Instance Hours on top of normal instance and storage pricing. The surcharge buys nothing but more time on the old version and stops entirely once the domain runs a version under standard support. |
Plan and rehearse an upgrade of this domain from {version} to an engine version under standard support to end the Extended Support surcharge. |
OpenSearchExtendedSupport meter cost + Normalized Instance Hours (invoice month) |
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.