Skip to main content

Waste Detection Guide

CloudWise Waste Detection automatically identifies AWS resources that are costing you money without providing value. This guide explains how each detector works, the confidence levels behind our recommendations, and the AWS pricing references that back up our calculations.

Try It Without Connecting Your AWS Account!

You can run Waste Detection in Air-Gapped Mode without granting CloudWise any access to your AWS account. Run our export script in AWS CloudShell, upload the results, and get an analysis of your unused resources (up to 158 of the 186 checks).

Learn about Air-Gapped Mode โ†’

๐ŸŽฏ Our Philosophy: Accuracy Over Everythingโ€‹

We believe in transparent, verifiable savings. Every waste item we flag includes:

  • Confidence Level: How certain we are about the finding
  • Savings Reconciled Against Your Bill: We start from published AWS pricing, then check that figure against what AWS actually charged you for that resource โ€” and cap it accordingly. Where we can't check, we say so.
  • Explanation: A plain-language description of what was detected and why it is waste
  • Action Command: For most findings, an AWS CLI command you can review and run; recommendation-only checks (for example Savings Plans and Reserved Instance recommendations) give guidance instead of a command

Official AWS pricing links are listed on this page, not on each finding.

Most savings figures are computed from your resource's real configuration and AWS list prices. A few checks use a stated estimate instead: halving Lambda memory, a fixed share of cost for Redshift DC2 to RA3 migration and for Redshift concurrency scaling, and a fixed share of log-group storage for a missing retention policy.

See How savings are calculated below for what "reconciled" means and when it doesn't apply.


๐Ÿ’ต How Savings Are Calculatedโ€‹

Every savings figure starts as list-price arithmetic: we take the resource's real configuration (size, capacity, hours) and price it at AWS's published on-demand rates. That answers "what does this resource cost at rack rate?"

It does not answer "what is AWS actually charging you for it?" Those two numbers diverge whenever:

SituationEffect on a list-price figure
Always-free tier (e.g. DynamoDB's free tier of read and write capacity)The resource costs $0. The whole claimed saving is phantom.
Reserved Instances / Savings PlansAlready discounted
EDP or private pricing agreementsBlanket discount
CreditsThe resource costs $0 to you

So CloudWise takes a second step: it looks up what your Cost & Usage Report actually recorded for that specific resource over the trailing 30 days, and caps the claimed saving at the amount you were really billed. You can't save money AWS never charged you.

We cap rather than replace: a right-sizing recommendation claims the difference between two instance sizes, which is legitimately less than the resource's full cost. Reconciliation only ever moves a number down, never up.

When we can't reconcile โ€” and how you'll knowโ€‹

Resource-level detail in the CUR is an opt-in AWS setting (Include resource IDs). Without it โ€” or when the resource is covered by a Reserved Instance or Savings Plan, whose real cost sits on a separate line item โ€” we cannot check a figure against your bill.

When that happens we do not quietly fall back to list price and present it as fact. The finding keeps its list-price estimate and is labelled with why it couldn't be reconciled:

LabelMeaning
Checked against your AWS billReconciled. The figure is capped at real billed cost.
AWS bills $0 for this resourceConfirmed free-tier or fully credited. Nothing to recover.
Estimated at list price โ€” enable resource IDsYour CUR has no per-resource detail yet.
Estimated at list price โ€” covered by an RI/Savings Plan/creditYour effective rate is lower than list, by an amount we can't see.
Estimated at list price โ€” not enough billing historyThe resource is too new to extrapolate from safely.
Estimated at list price โ€” no line in your Cost & Usage Report yetNew resources take a day or two to appear in the report.
Estimated at list price โ€” Container Insights metrics are billed for your whole accountThe Container Insights metric charges this estimate counts are billed for your whole account, not per cluster, so your Cost & Usage Report can't confirm this cluster's share. Shown only for a cluster that is running tasks; a cluster with no running tasks bills $0.

A resource with no line in your Cost & Usage Report is treated as costing $0 only when it is old enough for the report to have billed it; a resource newer than the report's latest day, or an Elastic IP (which AWS bills whenever it is allocated), keeps its estimate, labelled, until the report shows its line.

This mirrors how CloudWise treats a detector that fails: it reports a warning rather than a clean-looking result.

What this changedโ€‹

Reconciliation can make reported savings go down for some accounts โ€” as it should. A small DynamoDB table can sit inside DynamoDB's perpetual free allowance. AWS bills $0 for that capacity, so the true saving is nothing. Findings whose reconciled value falls below your minimum waste threshold now drop out of results, except governance and security checks (for example empty buckets, empty log groups and encryption findings), which are always kept.

Fewer, smaller, true numbers beat more, larger, wrong ones.


๐Ÿ“Š Confidence Levels Explainedโ€‹

CloudWise uses three confidence levels to help you prioritize actions:

HIGH Confidence โœ…โ€‹

These findings involve no guesswork about whether the waste is real:

  • Uses actual resource sizes/counts from AWS APIs
  • Uses fixed AWS pricing (published rates)
  • Clear binary detection (e.g., "attached or not")
  • The best place to start. Still give each one a quick review: confidence is not yet backed by a passing precision measurement on labelled production findings

Confidence describes the detection, not the dollar figure. How much the finding is worth is a separate question, answered by reconciliation below.

Examples: Unattached EBS volumes, Unattached Elastic IPs, Orphaned snapshots (source volume deleted)

MEDIUM Confidence โš ๏ธโ€‹

These findings are accurate with caveats:

  • Uses real CloudWatch metrics
  • Thresholds are configurable (e.g., "idle" means < 5% CPU)
  • May have legitimate exceptions (scheduled jobs, DR resources)
  • Review before taking action

Examples: Idle EC2 instances, Idle OpenSearch domains, Log groups without retention

LOW Confidence โšกโ€‹

These are informational only or for cleanup purposes:

  • Minimal actual cost savings
  • Detection is for housekeeping, not cost reduction
  • Consider during maintenance windows

Examples: ARM64 migration opportunities, outdated Lambda runtimes, Classic Load Balancer migration. (Unused Lambda functions are HIGH confidence; the saving is only the small code-storage charge.)


๐Ÿ“Š Complete Detector Inventoryโ€‹

CloudWise includes 186 waste checks* (from 46 detectors) spanning 40+ AWS services, organized into 10 service categories, each designed to identify a specific cost optimization opportunity.

* All but 43 of the 186 are standard checks. The 43 are advisory checks: they are estimated from configuration and have not been validated against live waste, so their savings are shown separately and never counted in your headline waste total.

Waste Types by Categoryโ€‹

Every waste type belongs to exactly one category, and the counts below add up to the 186 waste types in the catalog. All are active except unused_reserved_instance, which currently doesn't run. Commitment checks (Reserved Instance and Savings Plans purchase, utilization and expiry) and AWS Compute Optimizer rightsizing are grouped with the service they cover, not in separate categories.

CategoryWaste typesDescription
Compute Optimization35EC2 (idle, stopped with attached EBS, Compute Optimizer rightsizing), Lambda cost tuning (idle Provisioned Concurrency, excessive timeout, ARM64 migration), EKS Extended Support, WorkSpaces, Lightsail, Elastic Beanstalk, and compute commitments: EC2 RI and Compute/EC2 Savings Plans recommendations, unused and expiring RIs and Savings Plans, Savings Plans coverage gaps, convertible RI exchanges, CUR-derived unused reservations and Savings Plans waste
Storage Optimization27EBS (unattached, gp2โ†’gp3, over-provisioned IOPS, Compute Optimizer rightsizing), EBS and AMI snapshots, S3 (lifecycle, growth, storage class, empty buckets, incomplete multipart uploads, request and transfer cost), EFS, FSx, ECR, AWS Backup
Database Optimization41RDS (including Extended Support and Compute Optimizer rightsizing), Aurora, DynamoDB, ElastiCache (including Extended Support), Redshift, OpenSearch (including Extended Support), Neptune, DocumentDB (including Extended Support), and Reserved Instance recommendations for RDS, ElastiCache, OpenSearch and Redshift
Network Optimization19Elastic IPs (unattached, on stopped instances, multiple per instance), NAT Gateway, Load Balancers (ALB/NLB/CLB), CloudFront, Route 53, Global Accelerator, Transfer Family, VPC Endpoints
Serverless Optimization12Lambda (unused, over-provisioned memory, outdated runtimes, Compute Optimizer rightsizing), API Gateway, AppSync, Step Functions
Container Optimization5ECS (idle services, oversized tasks and memory, missing auto scaling, Container Insights overhead)
Analytics Optimization22EMR, Kinesis Data Streams, Kinesis Data Firehose, MSK, Glue, Amazon MQ
ML/AI Optimization6SageMaker Notebooks, Endpoints, Oversized Endpoints, Stopped Notebook Storage, Previous-Gen Instances, SageMaker Savings Plans recommendations
Management & Operations7CloudWatch Logs, CloudTrail, CloudWatch Dashboards
Security & Compliance12Secrets Manager, KMS, Encryption-at-rest (EBS, RDS, EFS, S3, OpenSearch, DocumentDB), Deletion Protection (RDS, DynamoDB), Public Access (RDS), Backup Coverage

Note: 158 waste checks are available in Air-Gapped Mode with full export. 28 require Online Mode (AWS Compute Optimizer, RI/SP purchase recommendations, the live-lookup commitment detectors, unused API Gateway APIs, unused KMS keys, CloudTrail checks, unused CloudFront distributions, unused Route 53 hosted zones, orphaned DNS record cross-referencing, the Elastic Beanstalk orphaned-RDS check, the never-subscribed Kinesis enhanced fan-out consumer check, and the idle OpenSearch domain check) โ€” each needs a live AWS API call the offline export can't provide. See the Coverage Matrix for details.

Advisory checks (43)โ€‹

We validate a check by making it fire on a real resource built for the purpose, and not fire on a matching resource that isn't waste. The checks below can't be validated that way, so they are advisory: each finding is estimated from your configuration and has not been validated against live waste.

In the workspace an advisory finding carries an Advisory label, and its detail says so. Its savings are shown on a separate "advisory" line and are not included in your headline waste total. The same applies wherever CloudWise shows a savings total: waste emails and Slack messages, PDF and CSV report summaries (labelled "excl. advisory"), per-account totals, the Air-Gapped results summary and your shared score card.

Waste TypeWhy it can't be validated
appsync_idle_cacheThe smallest resource that triggers it costs more to run for the check's window than our test budget allows
aurora_io_optimization_opportunityThe smallest resource that triggers it costs more to run for the check's window than our test budget allows
aurora_serverless_opportunityThe smallest resource that triggers it costs more to run for the check's window than our test budget allows
convertible_ri_exchange_opportunityNeeds a Reserved Instance or Savings Plan commitment, or AWS's own purchase recommendation
cur_savings_plan_wasteNeeds a Reserved Instance or Savings Plan commitment, or AWS's own purchase recommendation
cur_unused_reservationNeeds a Reserved Instance or Savings Plan commitment, or AWS's own purchase recommendation
documentdb_extended_support_costAWS no longer allows the configuration it looks for
emr_over_provisionedThe smallest resource that triggers it costs more to run for the check's window than our test budget allows
expiring_reserved_instanceNeeds a Reserved Instance or Savings Plan commitment, or AWS's own purchase recommendation
expiring_savings_planNeeds a Reserved Instance or Savings Plan commitment, or AWS's own purchase recommendation
glue_catalog_bloatThe smallest resource that triggers it costs more to run for the check's window than our test budget allows
high_lcu_cost_albThe smallest resource that triggers it costs more to run for the check's window than our test budget allows
idle_global_acceleratorThe smallest resource that triggers it costs more to run for the check's window than our test budget allows
idle_redshiftThe smallest resource that triggers it costs more to run for the check's window than our test budget allows
idle_transfer_no_activityThe smallest resource that triggers it costs more to run for the check's window than our test budget allows
idle_transfer_serverThe smallest resource that triggers it costs more to run for the check's window than our test budget allows
idle_transfer_web_appThe smallest resource that triggers it costs more to run for the check's window than our test budget allows
idle_workspaceThe smallest resource that triggers it costs more to run for the check's window than our test budget allows
kinesis_on_demand_downgradeThe smallest resource that triggers it costs more to run for the check's window than our test budget allows
long_running_emrThe smallest resource that triggers it costs more to run for the check's window than our test budget allows
oversized_redshiftThe smallest resource that triggers it costs more to run for the check's window than our test budget allows
redshift_concurrency_scaling_wasteThe smallest resource that triggers it costs more to run for the check's window than our test budget allows
redshift_legacy_dc2AWS no longer allows the configuration it looks for
redshift_no_pauseThe smallest resource that triggers it costs more to run for the check's window than our test budget allows
redshift_wlm_over_provisionedThe smallest resource that triggers it costs more to run for the check's window than our test budget allows
ri_opportunity_ec2Needs a Reserved Instance or Savings Plan commitment, or AWS's own purchase recommendation
ri_opportunity_elasticacheNeeds a Reserved Instance or Savings Plan commitment, or AWS's own purchase recommendation
ri_opportunity_opensearchNeeds a Reserved Instance or Savings Plan commitment, or AWS's own purchase recommendation
ri_opportunity_rdsNeeds a Reserved Instance or Savings Plan commitment, or AWS's own purchase recommendation
ri_opportunity_redshiftNeeds a Reserved Instance or Savings Plan commitment, or AWS's own purchase recommendation
s3_high_request_and_transfer_costThe smallest resource that triggers it costs more to run for the check's window than our test budget allows
s3_no_default_encryptionAWS no longer allows the configuration it looks for
savings_plan_coverage_gapNeeds a Reserved Instance or Savings Plan commitment, or AWS's own purchase recommendation
sp_opportunity_computeNeeds a Reserved Instance or Savings Plan commitment, or AWS's own purchase recommendation
sp_opportunity_ec2Needs a Reserved Instance or Savings Plan commitment, or AWS's own purchase recommendation
sp_opportunity_sagemakerNeeds a Reserved Instance or Savings Plan commitment, or AWS's own purchase recommendation
underutilized_redshiftThe smallest resource that triggers it costs more to run for the check's window than our test budget allows
unused_acceleratorThe smallest resource that triggers it costs more to run for the check's window than our test budget allows
unused_reserved_instanceNeeds a Reserved Instance or Savings Plan commitment, and currently doesn't run. For uploaded CUR files, see cur_unused_reservation
unused_savings_planNeeds a Reserved Instance or Savings Plan commitment, or AWS's own purchase recommendation
unused_transfer_protocolThe smallest resource that triggers it costs more to run for the check's window than our test budget allows
workspaces_pool_overprovisioned_capacityThe smallest resource that triggers it costs more to run for the check's window than our test budget allows
workspaces_windows_license_optimizationThe smallest resource that triggers it costs more to run for the check's window than our test budget allows

๐Ÿ“ด Air-Gapped Mode vs Online Modeโ€‹

FeatureAir-Gapped ModeOnline Mode
AWS ConnectionNone requiredIAM role required
Detectors Available158 of 186All 186
Daily scans and alertsโŒโœ…
Compute OptimizerโŒโœ…
RI/SP RecommendationsโŒโœ…
Ideal ForEvaluation, complianceProduction monitoring

Air-Gapped Mode is perfect for:

  • Companies with strict security/compliance requirements
  • Evaluating CloudWise before full integration
  • One-time cost audits
  • Teams that need approval before granting external access

Get started with Air-Gapped Mode โ†’

๐Ÿ”’ Subscription Tier & Detector Coverageโ€‹

Some detectors are gated by your subscription tier rather than by Air-Gapped vs. Online mode. The Free tier runs a smaller, high-signal set of detectors; paid tiers unlock the full registry (Glue, Step Functions, CloudFront, Aurora, KMS, Secrets Manager, and more).

If your account is downgraded (for example, a cancelled subscription falls back to Free), the detectors outside your tier stop running. This is never silent: the workspace Findings view shows a quiet notice โ€” "N detectors aren't included in your plan" โ€” whenever a scan skipped detectors for tier reasons, so a smaller findings list is never mistaken for "the waste got fixed" or "CloudWise stopped working." The notice states only the number of detectors withheld; it never estimates hidden findings or savings, since we don't run those detectors and won't guess.

This mirrors the guarantee CloudWise already applies to detector failures: a detector that times out, hits a circuit breaker, or throws an exception is not swallowed into a clean-looking result; the scan reports a warning. Tier-based skips use a separate, non-error signal (a skip is a plan limit, not a fault).

Detectors skipped because your Cost & Usage Report shows no active usage for a service are a separate, data-driven condition โ€” not a plan limit โ€” and are tracked internally the same way, but don't surface in this plan-upgrade notice.

Complete Detector Reference Tableโ€‹

The table below lists all implemented waste types with their detection criteria and status. A few waste types appear under two headings.

Compute Optimization (34 Waste Types)โ€‹

Waste TypeConfidenceDetection
idle_ec2MEDIUMHourly CPU < 5% average for 14 days, with no sustained or heavy peaks; ASG, EKS, Beanstalk, EMR, Spot Fleet and EC2 Fleet instances excluded
stopped_ec2_with_ebsMEDIUMStopped instance with EBS volumes, priced per attached volume
unused_lambdaHIGHZero invocations for 30 days, on a function last changed at least 30 days ago
over_provisioned_lambdaMEDIUMMemory of 512 MB or more with average duration under 500 ms over 30 days
lambda_provisioned_concurrency_idleHIGHProvisioned Concurrency utilization < 10% for 14 days
lambda_excessive_timeoutMEDIUMTimeout โ‰ฅ 10ร— average duration and above 3ร— the longest run
lambda_arm64_migrationLOWx86_64 function with ARM64-compatible runtime
oversized_ecs_taskMEDIUMFargate service whose busiest task stayed under 20% CPU for 7 days
idle_ecs_serviceHIGHTasks desired but none running, no rollout in progress ($0: hygiene)
ecs_no_autoscalingMEDIUMFargate service with 2+ running tasks and no Application Auto Scaling
ecs_container_insights_wasteHIGHContainer Insights on a dev/test cluster, a cluster with fewer than 3 active services, or one with no running tasks
oversized_ecs_memoryMEDIUMPeak memory under 40% of the task memory for 7 days
idle_sagemaker_notebookMEDIUMInService with no start, change or Jupyter log activity in the window
idle_sagemaker_endpointHIGHZero invocations for 7 days
stopped_sagemaker_notebook_storageHIGHStopped notebook with an ML storage volume
previous_gen_sagemaker_instanceHIGHPrevious-gen instance type (ml.m4/c4)
idle_workspaceHIGHNo user connection for more than 30 days
oversized_workspaceMEDIUMPeak CPU and memory fit the next smaller bundle, and the price difference is at least 15 USD a month
workspaces_autostop_opportunityHIGHAlwaysOn WorkSpace with low usage โ€” switch to AutoStop billing, with an estimated saving of at least 10 USD a month
workspaces_pool_overprovisioned_capacityMEDIUMWorkSpaces Pool whose peak active sessions over 14 days are at most 75% of its desired sessions, with at least 2 idle sessions costing at least 25 USD a month
workspaces_windows_license_optimizationMEDIUMWindows license-included desktops eligible for BYOL or Linux alternatives
idle_lightsailHIGHStopped, or CPU < 5% for 14 days (75% coverage)
lightsail_unattached_static_ipHIGHAllocated but not attached to any instance
lightsail_unattached_diskHIGHBlock storage disk not attached to any instance
lightsail_old_snapshotMEDIUMManual snapshot older than 90 days
lightsail_idle_load_balancerHIGHLoad balancer with zero healthy instances
lightsail_idle_databaseHIGHDatabase with CPU < 5% and fewer than 1 connection on average over 14 days
idle_beanstalkHIGHUnhealthy environment (Red health)
beanstalk_idle_trafficHIGHZero load-balancer (or enhanced-health) requests for 14 days
beanstalk_unnecessary_albHIGHLoad-balanced with single instance (min=max=1)
beanstalk_previous_gen_instancesHIGHUsing previous-generation instance types (t2, m4, c4, etc.)
beanstalk_over_provisionedMEDIUMInstance CPU <25% for 14 days across 2+ instances
beanstalk_orphaned_rdsHIGHRDS left behind by a terminated Beanstalk environment, with zero connections for 14 days
eks_extended_support_costHIGHEKS cluster version within 90 days of Extended Support or already in it

Storage Optimization (26 Waste Types)โ€‹

Waste TypeConfidenceDetection
unattached_ebsHIGHVolume status = available
old_ebs_snapshotMEDIUMSnapshot at least 90 days old; never the newest snapshot of a volume that still exists
orphaned_ebs_snapshotHIGHSource volume deleted โ€” snapshot serves no recovery purpose
ami_orphaned_snapshotHIGHAMI deregistered โ€” backing snapshot no longer needed
gp2_migrationHIGHVolume type = gp2
over_provisioned_iopsMEDIUMProvisioned IOPS underutilized
no_lifecycle_policyMEDIUMS3 bucket without lifecycle rules
incomplete_multipartHIGHMultipart uploads abandoned for more than 7 days
s3_rapid_growthLOWBucket growth of 100% or more against the newest daily size from 28 to 32 days earlier, size at least 1 GB, absolute growth at least 10 GB, no expiration rules
s3_wrong_storage_classLOWS3 Standard of at least 50 GB and at least 90% of total storage, no Intelligent-Tiering or lifecycle transitions
s3_empty_bucketLOWEmpty bucket (0 objects, 0 bytes) at least 30 days old, excluding infra buckets
s3_high_request_and_transfer_costLOWNon-storage costs (transfer + API requests) exceed storage costs (CUR-based)
idle_efsHIGHFile system with no mount targets
no_lifecycle_efsMEDIUMEFS filesystem without lifecycle policy
idle_fsxHIGHZero read and write bytes for 7 days
oversized_fsxMEDIUMStorage utilization below 40% of provisioned capacity
fsx_throughput_overprovisionedMEDIUMBusiest hour below 30% of provisioned throughput (Windows/ONTAP/OpenZFS)
old_fsx_backupMEDIUMManual FSx backup at least 90 days old
ecr_no_lifecycle_policyMEDIUMECR repo without lifecycle policy
old_ecr_imagesMEDIUMImages pushed more than 90 days ago, when old plus untagged images exceed 0.5 GB
untagged_ecr_imagesHIGHUntagged ECR images above 0.1 GB (when Old ECR Images does not already report them)
old_backupMEDIUMRecovery point at least 180 days old, without a compliance hold
redundant_backupMEDIUMTwo backup plans backed up the same resource within 1 hour; the point that expires sooner is flagged
backup_no_lifecycle_tieringLOWBackup plan rule that keeps backups 90+ days with no cold storage transition
stale_backup_plan_assignmentHIGHBackup plan with explicit resource selections that has not run for 30+ days
backup_copy_policy_overreachLOWMore than 10 copy jobs and 100 GB to one destination vault in 90 days

Database Optimization (37 Waste Types)โ€‹

Waste TypeConfidenceDetection
idle_rdsHIGHZero connections for 14 days
old_rds_snapshotMEDIUMManual snapshot at least 90 days old
aurora_io_optimization_opportunityHIGHAurora Standard cluster where I/O charges >25% of total spend over 30 days
aurora_extended_support_costHIGHAurora cluster running EOL engine version under Extended Support
aurora_serverless_opportunityMEDIUMProvisioned Aurora cluster whose writer averages at most 15% CPU with a peak of at most 40% over 14 days, suitable for Serverless v2
aurora_to_rds_downgrade_opportunityMEDIUMAurora Standard cluster with at most 1 reader, writer CPU at most 20%, at most 100 average connections and I/O charges at least 25% of spend, where RDS with gp3 storage would be cheaper
rds_extended_support_costHIGHNon-Aurora RDS MySQL/PostgreSQL instance in warning window or active Extended Support surcharge state
idle_dynamodbHIGHZero consumed capacity for 14 days, on a table at least 14 days old
dynamodb_no_autoscalingMEDIUMProvisioned mode without auto-scaling
over_provisioned_dynamodbMEDIUMRead and write capacity utilization both < 20% over 14 days, on a table at least 14 days old
idle_elasticacheHIGHZero connections for 7 days
oversized_elasticacheMEDIUMElastiCache node over-provisioned relative to usage
elasticache_extended_support_costHIGHElastiCache Redis OSS 4/5/6 cluster in warning window or active Extended Support surcharge state (Valkey and Memcached have no Extended Support)
elasticache_replication_wasteHIGHNon-production cluster with unnecessary replicas
elasticache_engine_migrationHIGHRedis OSS / Memcached cluster eligible for lower-priced Valkey
elasticache_serverless_optimizationMEDIUMNode-based cluster with spiky traffic better suited for Serverless
elasticache_data_tiering_opportunityMEDIUMMemory-only R5/R6g/R7g cluster eligible for R6gd data tiering
idle_redshiftHIGHZero connections for 7 days on a cluster at least that old, costing at least $0.01 a month
underutilized_redshiftMEDIUMAverage CPU above zero and below 10%, non-zero connections, costing at least $0.01 a month
oversized_redshiftMEDIUMAverage CPU below 15% on an RA3 or DC2 cluster with a smaller node type, saving at least $0.01 a month
redshift_no_pauseMEDIUMNo pause schedule, at least 40% zero-connection hours, costing at least $0.01 a month
redshift_legacy_dc2LOWDC2 node type, costing at least $0.01 a month, recommend RA3 migration
redshift_wlm_over_provisionedMEDIUMWLM queue near-empty, under 50% of 5 concurrency slots used
redshift_concurrency_scaling_wasteMEDIUMConcurrency scaling exceeds the free 3,600 seconds a day, costing at least $0.01 a month
idle_opensearchMEDIUMOver the last 7 days, no client requests (OpenSearchRequests minus InvalidHostHeaderRequests) and no indexing on any day, at most 100 searches per data node per day (background noise), cluster never red, at most 10 MiB of index data and no disk use beyond AWS's reserved space, UltraWarm/cold storage known to be off, no cross-cluster connection, on a domain costing at least $0.01 a month. Plan only: no Fix This
opensearch_extended_support_costHIGHOpenSearch/legacy Elasticsearch domain in warning window or active legacy support surcharge state
oversized_opensearchMEDIUMHourly CPU: avg < 20%, 95% of hours averaging < 40%, 95% of hourly peaks < 50%, over at least 75% of the 7-day window, with active search traffic
opensearch_ebs_overprovisionedMEDIUMFree storage > 60% with flat growth (< 0.1 GB/day)
ri_opportunity_opensearchHIGHOn-demand domain eligible for Reserved Instance savings
idle_neptuneHIGHZero requests for 7 days
neptune_serverless_opportunityHIGHProvisioned cluster with hourly CPU avg < 20%, 95% of hours averaging < 40%, 95% of hourly peaks < 50%, over at least 75% of 14 days, on a cluster at least 14 days old, and cost above the Serverless estimate
oversized_neptuneMEDIUMHourly cluster CPU: avg < 20%, 95% of hours averaging < 40%, 95% of hourly peaks < 50%, over at least 75% of 14 days, on a cluster at least 14 days old, with downsize path available
neptune_old_snapshotHIGHManual snapshot at least 90 days old
idle_documentdbHIGHZero connections and combined read+write IOPS under 10/s for 7 days
old_documentdb_snapshotMEDIUMManual snapshot at least 90 days old
overprovisioned_documentdbMEDIUMHourly CPU: avg < 20%, 95% of hours averaging < 40%, 95% of hourly peaks < 50%, over at least 75% of 7 days; connections < 50, read+write IOPS < 100/s
documentdb_extended_support_costHIGHDocumentDB cluster in warning window or active Extended Support surcharge state

Network Optimization (19 Waste Types)โ€‹

Waste TypeConfidenceDetection
unattached_eipHIGHNo instance or ENI attached
eip_on_stopped_instanceHIGHEIP attached to stopped instance
multiple_eips_per_instanceHIGHRunning instance with > 1 EIP (on a stopped instance each EIP is its own eip_on_stopped_instance finding instead, so no address is counted twice)
idle_nat_gatewayHIGHZero bytes transferred for 7 days
idle_load_balancerHIGHZero healthy targets or zero requests with healthy targets
low_traffic_albMEDIUMALB with <100 requests in 14 days but healthy targets
high_lcu_cost_albLOWALB where LCU cost exceeds 2ร— base fee
classic_lb_migrationLOWClassic Load Balancer โ€” migrate to ALB/NLB
unused_distributionLOWCloudFront with zero requests for 30 days
unused_hosted_zoneMEDIUMRoute53 zone with only NS/SOA records (2 records or fewer)
unused_acceleratorHIGHGlobal Accelerator with no endpoints configured
idle_global_acceleratorHIGHGlobal Accelerator deployed with endpoints but zero processed bytes for 30 days (or, when CloudWatch publishes no traffic metric, at least 14 days old)
disabled_global_acceleratorMEDIUMGlobal Accelerator disabled but still incurring fixed hourly charges
idle_transfer_serverHIGHOnline Transfer Family server with a service-managed identity provider and no configured users (it cannot receive any transfer). A server known to run AS2 only is not judged on users
idle_transfer_no_activityHIGHTransfer Family server with zero file operations for 30 days: a server with service-managed users, or one judged on traffic only because its users live in an external identity provider (API Gateway, Directory Service or Lambda) or it runs AS2 only
idle_transfer_web_appHIGHTransfer Family web app with zero active sessions for 30 days
unused_transfer_protocolMEDIUMTransfer server with an enabled protocol that had zero transfers for 30 days while another protocol was active
unused_vpc_endpointHIGHVPC endpoint with zero traffic for 14 days
orphaned_dns_recordMEDIUMDNS record pointing to non-existent resource

Serverless Optimization (10 Waste Types)โ€‹

Waste TypeConfidenceDetection
unused_lambdaHIGHZero invocations for 30 days, on a function last changed at least 30 days ago
lambda_old_runtimeLOWFunction on deprecated/EOL runtime
unused_api_gatewayHIGHZero requests for 30 days (API at least 30 days old)
unused_appsyncLOWZero requests for 30 days (API at least 30 days old)
appsync_idle_cacheMEDIUMWithheld: AWS publishes no cache hit or miss metric under the standard AppSync dimensions, so this check does not fire yet
appsync_idle_subscriptionsMEDIUMActive WebSocket connections with < 100 requests in 14 days (API at least 14 days old)
idle_state_machineLOWZero executions for 14 days
step_functions_retry_stormHIGHRetry ratio โ‰ฅ 25% AND failure rate โ‰ฅ 20% over 14 days
step_functions_high_transition_densityMEDIUMAvg transitions per success > 50 (configurable)
step_functions_express_duration_wasteMEDIUMExpress p95 duration > 30,000 ms with at least 10,000 executions a month

Analytics Optimization (22 Waste Types)โ€‹

Waste TypeConfidenceDetection
idle_emr_clusterHIGHCluster in WAITING state now and running for more than 24 hours
long_running_emrMEDIUMRUNNING for more than 720 hours with no running step and no step finished in the last 7 days
emr_over_provisionedMEDIUMAverage YARN memory available above 60% over 14 days with fewer than 2 running applications, on a cluster running more than 168 hours
emr_missing_auto_terminationHIGHKeep-alive cluster running more than 24 hours without an auto-termination policy, on an EMR release that supports one
emr_previous_gen_instancesHIGHCluster running more than 24 hours on previous-generation instance types (m3โ†’m5, c3โ†’c5, etc.)
emr_spot_opportunityMEDIUMTask instance group of 2 or more nodes on On-Demand instead of Spot, on a cluster running more than 48 hours
idle_kinesis_streamHIGHZero records for 14 days, on a stream at least that old
over_provisioned_kinesisMEDIUMProvisioned stream with more than 1 shard and per-shard utilization below 20% over 14 days
kinesis_on_demand_downgradeMEDIUMOn-Demand stream at least 30 days old with stable throughput (CV < 0.3) โ€” switch to Provisioned
kinesis_extended_retention_wasteHIGHExtended retention (more than 24 hours) with zero GetRecords in 14 days
kinesis_enhanced_fan_out_wasteHIGHEnhanced fan-out consumer, at least 14 days old, that never subscribed in that time
kinesis_firehose_idleHIGHFirehose delivery stream with zero records for 14 days
idle_msk_clusterHIGHNo user-owned partition on any broker for 7 days, with only AWS canary-level traffic (cluster at least 7 days old)
oversized_msk_clusterMEDIUMBusiest-broker CPU < 20% AND network < 50% for 7 days
old_glue_jobLOWJob not run for 90 days (with no run history: not modified for that long)
idle_glue_crawlerMEDIUMCrawler not run for 90 days (never run: created more than that long ago)
oversized_glue_jobLOWJVM heap < 30% avg OR short duration with large DPU allocation; at least $5 a month excess
glue_job_missing_timeoutMEDIUMTimeout โ‰ฅ 10ร— average execution duration and at least 60 minutes
failed_glue_job_retryHIGHโ‰ฅ 50% failure rate across at least 3 recent runs, wasting at least $5 a month
glue_catalog_bloatLOWData Catalog objects exceed the 1,000,000-object free tier, or table versions exceed 500,000
idle_mq_brokerHIGHZero stored messages, consumers and producers for 14 days (broker at least 14 days old)
oversized_mq_brokerMEDIUMAverage CPU at or below 15% over 14 days โ€” downsize to smaller instance type

ML/AI Optimization (6 Waste Types)โ€‹

Waste TypeConfidenceDetection
idle_sagemaker_notebookMEDIUMInService with no start, change or Jupyter log activity in the window
idle_sagemaker_endpointHIGHZero invocations for 7 days
oversized_sagemaker_endpointMEDIUMLow CPU/memory utilization
stopped_sagemaker_notebook_storageHIGHStopped notebook with an ML storage volume
previous_gen_sagemaker_instanceHIGHPrevious-gen instance type (ml.m4/c4)
sp_opportunity_sagemakerHIGHAWS Cost Explorer SageMaker SP recommendation, reported only when the estimated saving is at least 10 USD a month

Management & Operations (7 Waste Types)โ€‹

Waste TypeConfidenceDetection
no_retention_log_groupMEDIUM/LOWLog group without retention policy
old_log_groupMEDIUMLog group with no recent logs
excessive_retention_log_groupMEDIUMLog group with retention exceeding recommended baseline
empty_log_groupLOWLog group with zero log streams
duplicate_cloudtrailHIGHMultiple trails logging same events
cloudtrail_s3_no_lifecycleMEDIUMCloudTrail bucket without lifecycle that holds logs over 365 days old
unused_dashboardMEDIUMCloudWatch dashboard with no recent views

Security & Compliance (12 Waste Types)โ€‹

Waste TypeConfidenceDetection
unused_secretMEDIUM/LOWSecret not accessed for 90 days
unused_kms_keyLOWCustomer-managed KMS key not referenced by any CloudTrail trail (usage cannot be verified)
unencrypted_ebs_volumeHIGHEBS volume without encryption enabled
unencrypted_rds_instanceHIGHRDS instance without encryption at rest
unencrypted_efs_filesystemHIGHEFS filesystem without encryption at rest
s3_no_default_encryptionMEDIUMS3 bucket without default encryption configuration
opensearch_no_encryption_at_restHIGHOpenSearch domain without encryption at rest
unencrypted_documentdb_clusterHIGHDocumentDB cluster without encryption at rest
rds_no_deletion_protectionHIGHRDS instance without deletion protection enabled
dynamodb_no_deletion_protectionHIGHDynamoDB table without deletion protection enabled
rds_publicly_accessibleHIGHRDS instance with public accessibility enabled
resource_without_backup_coverageMEDIUMCritical resource not covered by any AWS Backup plan

AWS Compute Optimizer Integration (4 Waste Types)โ€‹

Note: These detectors require Online Mode with live AWS API access to Compute Optimizer.

Waste TypeConfidenceDetection
oversized_ec2_optimizerHIGHAWS ML-based rightsizing recommendation, reported only when the estimated saving is at least 5 USD a month
oversized_ebs_optimizerHIGHAWS ML-based IOPS/throughput analysis, reported only when the estimated saving is at least 5 USD a month
oversized_lambda_optimizerHIGHAWS ML-based memory optimization, reported only when the estimated saving is at least 5 USD a month
oversized_rds_optimizerHIGHAWS ML-based RDS rightsizing recommendation, reported only when the estimated saving is at least 5 USD a month

Reserved Instance & Savings Plans (16 Waste Types)โ€‹

Note: Purchase recommendation and live-lookup commitment detectors require Online Mode with live AWS API access to Cost Explorer, EC2, or Savings Plans APIs. CUR-based commitment detectors (cur_unused_reservation, cur_savings_plan_waste) are available in all modes including Air-Gapped.

Waste TypeConfidenceDetection
ri_opportunity_ec2HIGHAWS Cost Explorer EC2 RI recommendation, reported only when the estimated saving is at least 10 USD a month
ri_opportunity_rdsHIGHAWS Cost Explorer RDS RI recommendation, reported only when the estimated saving is at least 10 USD a month
ri_opportunity_elasticacheHIGHAWS Cost Explorer ElastiCache Reserved Node recommendation, reported only when the estimated saving is at least 10 USD a month
ri_opportunity_opensearchHIGHAWS Cost Explorer OpenSearch RI recommendation, reported only when the estimated saving is at least 10 USD a month
ri_opportunity_redshiftHIGHAWS Cost Explorer Redshift Reserved Node recommendation, reported only when the estimated saving is at least 10 USD a month
sp_opportunity_computeHIGHAWS Cost Explorer Compute SP recommendation, reported only when the estimated saving is at least 10 USD a month
sp_opportunity_ec2HIGHAWS Cost Explorer EC2 Instance SP recommendation, reported only when the estimated saving is at least 10 USD a month
sp_opportunity_sagemakerHIGHAWS Cost Explorer SageMaker SP recommendation, reported only when the estimated saving is at least 10 USD a month
unused_reserved_instanceHIGHCurrently doesn't run. For uploaded CUR files, see cur_unused_reservation. Planned: RI utilization < 20% over 30 days and unused fee of at least $10/month
unused_savings_planHIGHSP utilization < 20% over 30 days and unused commitment of at least $10/month, via Cost Explorer
expiring_reserved_instanceHIGHActive RI expiring within 90 days (URGENT/WARNING/NOTICE tiers)
expiring_savings_planHIGHActive SP expiring within 90 days (URGENT/WARNING/NOTICE tiers)
convertible_ri_exchange_opportunityMEDIUMConvertible RI costing at least $10/month on previous-gen instance type eligible for free exchange
savings_plan_coverage_gapMEDIUMSP coverage < 50% with at least $100/mo uncovered on-demand spend
cur_unused_reservationMEDIUMUnused RI hours costing at least $10 in the billing period, detected in CUR billing data (all modes)
cur_savings_plan_wasteMEDIUMUnused SP commitment of at least $10 in the billing period, detected in CUR billing data (all modes)

Complete Waste Type Coverage Matrix (186 Types)โ€‹

This section provides the complete coverage matrix for the current 186 waste types, showing availability across different detection modes. This is particularly useful for understanding what waste detection capabilities are available in Air-Gapped Mode vs Online Mode.

Coverage Summaryโ€‹

ModeWaste Types AvailableCoverage
Online Mode186 (all types)100%
Offline + Full Export158 waste types85%

Complete Coverage Matrixโ€‹

Per-type availability below is tracked as a single Air-Gapped-vs-Online split.

158 of 186 waste types are available in Air-Gapped Mode; 28 require Online Mode (a live AWS API call the offline export cannot provide). See the Online-Only Waste Types table below.

Legend:

  • โœ… Available in Air-Gapped Mode (offline export) and Online Mode
  • ๐Ÿ”Œ Online Mode only

Compute Optimization (35 Types)โ€‹

#Waste TypeRiskModeRemediation
1beanstalk_idle_traffichighโœ…Plan only
2beanstalk_orphaned_rdshigh๐Ÿ”Œ Online onlyPlan only
3beanstalk_over_provisionedmediumโœ…Plan only
4beanstalk_previous_gen_instanceslowโœ…Plan only
5beanstalk_unnecessary_alblowโœ…Plan only
6convertible_ri_exchange_opportunitylow๐Ÿ”Œ Online onlyRecommendation only
7cur_savings_plan_wastehighโœ…Recommendation only
8cur_unused_reservationhighโœ…Recommendation only
9eks_extended_support_costlowโœ…Recommendation only
10expiring_reserved_instancemedium๐Ÿ”Œ Online onlyRecommendation only
11expiring_savings_planmedium๐Ÿ”Œ Online onlyRecommendation only
12idle_beanstalkhighโœ…Plan only
13idle_ec2highโœ…Plan only
14idle_lightsailhighโœ…Recommendation only
15idle_workspacehighโœ…Plan only
16lambda_arm64_migrationlowโœ…Plan only
17lambda_excessive_timeoutlowโœ…Fix This
18lambda_provisioned_concurrency_idlemediumโœ…Fix This
19lightsail_idle_databasehighโœ…Plan only
20lightsail_idle_load_balancermediumโœ…Plan only
21lightsail_old_snapshotmediumโœ…Plan only
22lightsail_unattached_diskmediumโœ…Recommendation only
23lightsail_unattached_static_iplowโœ…Plan only
24oversized_ec2_optimizerlow๐Ÿ”Œ Online onlyRecommendation only
25oversized_workspacemediumโœ…Plan only
26ri_opportunity_ec2low๐Ÿ”Œ Online onlyRecommendation only
27savings_plan_coverage_gaplow๐Ÿ”Œ Online onlyRecommendation only
28sp_opportunity_computelow๐Ÿ”Œ Online onlyRecommendation only
29sp_opportunity_ec2low๐Ÿ”Œ Online onlyRecommendation only
30stopped_ec2_with_ebsmediumโœ…Plan only
31unused_reserved_instancehigh๐Ÿ”Œ Online onlyRecommendation only
32unused_savings_planhigh๐Ÿ”Œ Online onlyRecommendation only
33workspaces_autostop_opportunitymediumโœ…Plan only
34workspaces_pool_overprovisioned_capacitymediumโœ…Plan only
35workspaces_windows_license_optimizationlowโœ…Plan only

Storage Optimization (27 Types)โ€‹

#Waste TypeRiskModeRemediation
36ami_orphaned_snapshotlowโœ…Fix This
37backup_copy_policy_overreachlowโœ…Plan only
38backup_no_lifecycle_tieringlowโœ…Fix This
39ecr_no_lifecycle_policylowโœ…Recommendation only
40fsx_throughput_overprovisionedmediumโœ…Plan only
41gp2_migrationmediumโœ…Fix This
42idle_efshighโœ…Recommendation only
43idle_fsxhighโœ…Fix This
44incomplete_multipartlowโœ…Plan only
45no_lifecycle_efslowโœ…Recommendation only
46no_lifecycle_policylowโœ…Fix This
47old_backupmediumโœ…Fix This
48old_ebs_snapshotlowโœ…Fix This
49old_ecr_imageslowโœ…Recommendation only
50old_fsx_backupmediumโœ…Fix This
51orphaned_ebs_snapshotlowโœ…Fix This
52over_provisioned_iopsmediumโœ…Fix This
53oversized_ebs_optimizerlow๐Ÿ”Œ Online onlyPlan only
54oversized_fsxmediumโœ…Plan only
55redundant_backupmediumโœ…Plan only
56s3_empty_bucketlowโœ…Recommendation only
57s3_high_request_and_transfer_costlowโœ…Plan only
58s3_rapid_growthlowโœ…Plan only
59s3_wrong_storage_classlowโœ…Plan only
60stale_backup_plan_assignmentlowโœ…Recommendation only
61unattached_ebslowโœ…Fix This
62untagged_ecr_imageslowโœ…Recommendation only

Database Optimization (41 Types)โ€‹

#Waste TypeRiskModeRemediation
63aurora_extended_support_costlowโœ…Plan only
64aurora_io_optimization_opportunitylowโœ…Fix This
65aurora_serverless_opportunitylowโœ…Plan only
66aurora_to_rds_downgrade_opportunitymediumโœ…Plan only
67documentdb_extended_support_costlowโœ…Plan only
68dynamodb_no_autoscalingmediumโœ…Fix This
69elasticache_data_tiering_opportunitylowโœ…Plan only
70elasticache_engine_migrationlowโœ…Plan only
71elasticache_extended_support_costlowโœ…Plan only
72elasticache_replication_wastemediumโœ…Plan only
73elasticache_serverless_optimizationlowโœ…Plan only
74idle_documentdbhighโœ…Recommendation only
75idle_dynamodbhighโœ…Fix This
76idle_elasticachehighโœ…Plan only
77idle_neptunehighโœ…Recommendation only
78idle_opensearchhigh๐Ÿ”Œ Online onlyPlan only
79idle_rdshighโœ…Plan only
80idle_redshifthighโœ…Fix This
81neptune_old_snapshotmediumโœ…Fix This
82neptune_serverless_opportunitylowโœ…Plan only
83old_documentdb_snapshotmediumโœ…Fix This
84old_rds_snapshotmediumโœ…Fix This
85opensearch_ebs_overprovisionedmediumโœ…Plan only
86opensearch_extended_support_costlowโœ…Plan only
87over_provisioned_dynamodbmediumโœ…Fix This
88overprovisioned_documentdbmediumโœ…Plan only
89oversized_elasticachemediumโœ…Plan only
90oversized_neptunemediumโœ…Fix This
91oversized_opensearchmediumโœ…Plan only
92oversized_rds_optimizerlow๐Ÿ”Œ Online onlyPlan only
93oversized_redshiftmediumโœ…Plan only
94rds_extended_support_costlowโœ…Plan only
95redshift_concurrency_scaling_wastelowโœ…Plan only
96redshift_legacy_dc2lowโœ…Plan only
97redshift_no_pausemediumโœ…Fix This
98redshift_wlm_over_provisionedmediumโœ…Plan only
99ri_opportunity_elasticachelow๐Ÿ”Œ Online onlyRecommendation only
100ri_opportunity_opensearchlow๐Ÿ”Œ Online onlyRecommendation only
101ri_opportunity_rdslow๐Ÿ”Œ Online onlyRecommendation only
102ri_opportunity_redshiftlow๐Ÿ”Œ Online onlyRecommendation only
103underutilized_redshiftmediumโœ…Plan only

Network Optimization (19 Types)โ€‹

#Waste TypeRiskModeRemediation
104classic_lb_migrationlowโœ…Plan only
105disabled_global_acceleratormediumโœ…Fix This
106eip_on_stopped_instancelowโœ…Fix This
107high_lcu_cost_alblowโœ…Plan only
108idle_global_acceleratorhighโœ…Plan only
109idle_load_balancermediumโœ…Fix This
110idle_nat_gatewaymediumโœ…Fix This
111idle_transfer_no_activityhighโœ…Plan only
112idle_transfer_serverhighโœ…Plan only
113idle_transfer_web_apphighโœ…Plan only
114low_traffic_albmediumโœ…Fix This
115multiple_eips_per_instancemediumโœ…Recommendation only
116orphaned_dns_recordlow๐Ÿ”Œ Online onlyPlan only
117unattached_eiplowโœ…Fix This
118unused_acceleratorhighโœ…Plan only
119unused_distributionmedium๐Ÿ”Œ Online onlyFix This
120unused_hosted_zonemedium๐Ÿ”Œ Online onlyFix This
121unused_transfer_protocolmediumโœ…Plan only
122unused_vpc_endpointmediumโœ…Plan only

Analytics Optimization (22 Types)โ€‹

#Waste TypeRiskModeRemediation
123emr_missing_auto_terminationlowโœ…Plan only
124emr_over_provisionedmediumโœ…Plan only
125emr_previous_gen_instanceslowโœ…Plan only
126emr_spot_opportunitylowโœ…Plan only
127failed_glue_job_retrylowโœ…Fix This
128glue_catalog_bloatlowโœ…Plan only
129glue_job_missing_timeoutlowโœ…Fix This
130idle_emr_clusterhighโœ…Plan only
131idle_glue_crawlermediumโœ…Fix This
132idle_kinesis_streammediumโœ…Fix This
133idle_mq_brokerhighโœ…Fix This
134idle_msk_clusterhighโœ…Plan only
135kinesis_enhanced_fan_out_wastemedium๐Ÿ”Œ Online onlyFix This
136kinesis_extended_retention_wastelowโœ…Fix This
137kinesis_firehose_idlemediumโœ…Plan only
138kinesis_on_demand_downgradelowโœ…Plan only
139long_running_emrhighโœ…Plan only
140old_glue_jobmediumโœ…Fix This
141over_provisioned_kinesismediumโœ…Fix This
142oversized_glue_joblowโœ…Plan only
143oversized_mq_brokerlowโœ…Plan only
144oversized_msk_clustermediumโœ…Plan only

ML/AI Optimization (6 Types)โ€‹

#Waste TypeRiskModeRemediation
145idle_sagemaker_endpointhighโœ…Fix This
146idle_sagemaker_notebookmediumโœ…Fix This
147oversized_sagemaker_endpointmediumโœ…Plan only
148previous_gen_sagemaker_instancelowโœ…Fix This
149sp_opportunity_sagemakerlow๐Ÿ”Œ Online onlyRecommendation only
150stopped_sagemaker_notebook_storagelowโœ…Plan only

Management & Operations (7 Types)โ€‹

#Waste TypeRiskModeRemediation
151cloudtrail_s3_no_lifecyclelow๐Ÿ”Œ Online onlyPlan only
152duplicate_cloudtrailhigh๐Ÿ”Œ Online onlyRecommendation only
153empty_log_grouplowโœ…Fix This
154excessive_retention_log_grouplowโœ…Fix This
155no_retention_log_grouplowโœ…Fix This
156old_log_grouplowโœ…Plan only
157unused_dashboardlowโœ…Fix This

Security & Compliance (12 Types)โ€‹

#Waste TypeRiskModeRemediation
158dynamodb_no_deletion_protectionmediumโœ…Recommendation only
159opensearch_no_encryption_at_restmediumโœ…Recommendation only
160rds_no_deletion_protectionmediumโœ…Recommendation only
161rds_publicly_accessiblehighโœ…Recommendation only
162resource_without_backup_coveragemediumโœ…Recommendation only
163s3_no_default_encryptionlowโœ…Recommendation only
164unencrypted_documentdb_clustermediumโœ…Recommendation only
165unencrypted_ebs_volumemediumโœ…Recommendation only
166unencrypted_efs_filesystemmediumโœ…Recommendation only
167unencrypted_rds_instancemediumโœ…Recommendation only
168unused_kms_keymedium๐Ÿ”Œ Online onlyFix This
169unused_secretmediumโœ…Fix This

Serverless Optimization (12 Types)โ€‹

#Waste TypeRiskModeRemediation
170appsync_idle_cachemediumโœ…Fix This
171appsync_idle_subscriptionslowโœ…Plan only
172idle_state_machinemediumโœ…Fix This
173lambda_old_runtimelowโœ…Plan only
174over_provisioned_lambdamediumโœ…Fix This
175oversized_lambda_optimizerlow๐Ÿ”Œ Online onlyPlan only
176step_functions_express_duration_wastemediumโœ…Plan only
177step_functions_high_transition_densitymediumโœ…Plan only
178step_functions_retry_stormmediumโœ…Plan only
179unused_api_gatewaymedium๐Ÿ”Œ Online onlyFix This
180unused_appsyncmediumโœ…Fix This
181unused_lambdahighโœ…Fix This

Container Optimization (5 Types)โ€‹

#Waste TypeRiskModeRemediation
182ecs_container_insights_wastelowโœ…Plan only
183ecs_no_autoscalingmediumโœ…Plan only
184idle_ecs_servicehighโœ…Plan only
185oversized_ecs_memorymediumโœ…Plan only
186oversized_ecs_taskmediumโœ…Plan only

Online-Only Waste Types (28)โ€‹

The following waste types require live AWS API access and are not available in Air-Gapped Mode.

Waste TypeReason
beanstalk_orphaned_rdsCross-service EB/RDS correlation (RDS + CloudWatch API)
cloudtrail_s3_no_lifecycleLive CloudTrail + S3 API
convertible_ri_exchange_opportunityRI inventory + pricing (EC2 API + Pricing API)
duplicate_cloudtrailLive CloudTrail API
expiring_reserved_instanceReal-time RI expiry tracking (EC2 API)
expiring_savings_planReal-time SP expiry tracking (Savings Plans API)
idle_opensearchLive OpenSearch cross-cluster connection check
kinesis_enhanced_fan_out_wasteLive CloudWatch ListMetrics (subscription check)
orphaned_dns_recordLive Route 53 record cross-reference
oversized_ebs_optimizerAWS ML analysis (Compute Optimizer API)
oversized_ec2_optimizerAWS ML analysis (Compute Optimizer API)
oversized_lambda_optimizerAWS ML analysis (Compute Optimizer API)
oversized_rds_optimizerAWS ML analysis (Compute Optimizer API)
ri_opportunity_ec2Purchase recommendation (Cost Explorer API)
ri_opportunity_elasticachePurchase recommendation (Cost Explorer API)
ri_opportunity_opensearchPurchase recommendation (Cost Explorer API)
ri_opportunity_rdsPurchase recommendation (Cost Explorer API)
ri_opportunity_redshiftPurchase recommendation (Cost Explorer API)
savings_plan_coverage_gapCoverage analysis (Cost Explorer API)
sp_opportunity_computePurchase recommendation (Cost Explorer API)
sp_opportunity_ec2Purchase recommendation (Cost Explorer API)
sp_opportunity_sagemakerPurchase recommendation (Cost Explorer API)
unused_api_gatewayLive API Gateway + CloudWatch metrics
unused_distributionLive CloudFront + CloudWatch metrics
unused_hosted_zoneLive Route 53 API
unused_kms_keyLive KMS + CloudTrail API
unused_reserved_instanceCurrently doesn't run. For uploaded CUR files, see cur_unused_reservation.
unused_savings_planPurchase commitment analysis (Cost Explorer API)

Tip: For maximum Air-Gapped Mode coverage (85%), run the full export script including CloudWatch metrics. See our Air-Gapped Mode Guide for setup instructions.


Detector Referenceโ€‹

Compute Detectorsโ€‹

Idle EC2 Instanceโ€‹

Confidence: MEDIUM
Detection: hourly CloudWatch CPUUtilization over 14 days (7 in an Air-Gapped export): average < 5%, and no sustained or heavy peaks
Savings: Full instance compute cost (from AWS Pricing API); the recommended fix is to stop the instance

How it works:

  1. Instances owned by another service are skipped: Auto Scaling group members, EKS and Kubernetes nodes, Elastic Beanstalk instances (Beanstalk environments are judged by idle_beanstalk), EMR nodes, and Spot Fleet and EC2 Fleet instances
  2. We read hourly CPU (average and peak) for the window. At least 75% of the hours must have data, and the instance must be at least as old as the window
  3. The instance is flagged only when all of these hold: average CPU below 5%; 95% of hours averaging below 10%; 95% of hourly peaks below 20%; and no hourly peak at or above 50% (a nightly batch job or a CPU-credit burst means it is not idle)
  4. Missing or sparse CPU data is reported as a scan warning (the verdict is missing, not zero), never as an idle instance
  5. Monthly cost is calculated using the AWS Pricing API for the exact instance type

Example:

Instance: i-0abc123def456
Type: t3.large
Region: us-east-1
Avg CPU: 2.3% over 14 days (peak 6.1%)
Monthly Savings: $60.74 (100% of instance cost)

AWS Reference: EC2 On-Demand Pricing

Why MEDIUM confidence: CPU is the only signal; memory, network and disk activity are not read, so a quiet but needed instance can still match. Remediation is a reviewed plan (stop, with start as rollback), not a one-click fix.


Stopped EC2 with EBS Storageโ€‹

Confidence: MEDIUM
Detection: Instance state = stopped with attached EBS volumes
Savings: EBS storage costs for attached volumes

How it works:

  1. We identify stopped instances with BlockDeviceMappings
  2. EBS volumes continue to charge even when the instance is stopped
  3. Each attached volume is priced from its real size and type (DescribeVolumes), region-adjusted. A volume we can't read leaves the instance out of the scan, noted, rather than guessed
  4. Volumes with DeleteOnTermination off survive a terminate; the finding names them so they can be deleted separately

Example:

Instance: i-0abc123def456
State: stopped
EBS Volumes: 2 (100GB gp3 + 500GB gp2)
Monthly Savings: $58.00 ($8 + $50)

AWS Reference: EBS Pricing

Why MEDIUM confidence: The instance may be stopped on purpose (a seasonal or standby host); the storage cost itself is measured.


Over-Provisioned Lambdaโ€‹

Confidence: MEDIUM
Detection: Memory of 512 MB or more with average duration under 500 ms over 30 days
Savings: Cost of halving the memory allocation

How it works:

  1. We read each function's configured memory and its average duration from CloudWatch over the last 30 days
  2. Functions with 512 MB or more allocated that finish in under 500 ms on average are flagged
  3. We estimate the saving from halving the memory, which cuts the GB-seconds billed per invocation. Memory used is not measured, so test the lower setting before applying it

Example:

Function: my-api-handler
Memory Allocated: 1024MB
Average Duration: 180ms
Suggested Memory: 512MB

AWS Reference: Lambda Pricing

Why MEDIUM confidence: Memory reduction may affect cold start times and CPU allocation.


Idle Provisioned Concurrencyโ€‹

Confidence: HIGH
Detection: Provisioned Concurrency utilization < 10% over 14 days
Savings: Based on wasted fraction of allocated Provisioned Concurrency

How it works:

  1. We list all Provisioned Concurrency configurations for each Lambda function
  2. CloudWatch ProvisionedConcurrencyUtilization is analyzed over 14 days
  3. Configurations with average utilization below 10% are flagged as idle
  4. Savings are calculated from the wasted fraction of allocated concurrency

Example:

Function: my-api-handler:prod
Allocated PC: 50
Avg Utilization: 3%
Wasted Fraction: 97%
Monthly Savings: $48.60

AWS Reference: Lambda Pricing โ€” Provisioned Concurrency

Why HIGH confidence: Provisioned Concurrency has a fixed cost regardless of usage. Low utilization directly translates to waste.


Excessive Lambda Timeoutโ€‹

Confidence: MEDIUM
Detection: Timeout โ‰ฅ 10ร— average duration and at least 60 seconds, with more than 100 invocations
Savings: Recommendation only (no direct cost savings from timeout change)

How it works:

  1. We compare the configured timeout against actual average execution duration
  2. Functions where the timeout is 10ร— or more of the average duration are flagged
  3. This helps avoid accidental runaway costs and improves error handling
  4. Recommended timeout is 3ร— the longest observed duration, and never under 10 seconds; a function is not flagged when that is not below its current timeout, so the change never cuts off an invocation seen in the last 30 days

Example:

Function: my-batch-processor
Timeout: 900s (15 min)
Avg Duration: 2s (longest: 20s)
Ratio: 450ร—
Recommended: 60s
Monthly Savings: N/A (recommendation only)

AWS Reference: Lambda Configuration

Why MEDIUM confidence: Timeout changes do not directly reduce cost, but prevent runaway invocations and improve operational hygiene.


ARM64 Migration Opportunityโ€‹

Confidence: LOW
Detection: x86_64 function with ARM64-compatible runtime and monthly cost โ‰ฅ $1
Savings: ~20% cost reduction by migrating to Graviton2 (ARM64)

How it works:

  1. We check the function's architecture (x86_64) and runtime
  2. Functions on ARM64-compatible runtimes (Python, Node.js, Java, .NET, Ruby) are flagged
  3. Only functions with monthly cost โ‰ฅ $1 are included (to avoid noise)
  4. AWS offers a 20% price discount for Lambda on Graviton2

Example:

Function: my-data-pipeline
Runtime: python3.12
Architecture: x86_64
Monthly Cost: $45.00
Est. Savings: $9.00/month (20%)

AWS Reference: Lambda Graviton2

Why LOW confidence: Migration requires testing; some libraries may not support ARM64. Manual verification recommended.


Oversized ECS Taskโ€‹

Confidence: MEDIUM
Detection: Fargate service whose CloudWatch CPUUtilization maximum stays below 20% over 7 days
Savings: The CPU removed by moving to the next valid smaller Fargate size

How it works:

  1. We read the service's maximum CPUUtilization over the last 7 days. Only Fargate services are checked, and a service younger than 7 days is skipped rather than judged on a partial window
  2. A service whose maximum stays below 20% is flagged
  3. The recommendation is the next valid smaller Fargate CPU size with the memory unchanged; a service already at the smallest CPU, or whose memory is not valid one CPU size down, is not flagged
  4. The finding is reported when the saving is at least $0.01 a month

Example:

Service: my-web-service
Task CPU: 1024 units
Task Memory: 2048MB
Max CPU (7 days): 12%
Recommended: 512 CPU units, memory unchanged
Monthly Savings: ~$40.00

AWS Reference: ECS Pricing

Why MEDIUM confidence: Workload patterns may have periodic spikes outside the window.


Idle SageMaker Notebookโ€‹

Confidence: MEDIUM
Detection: Notebook instance InService, not started or modified, and with no Jupyter server log activity for the idle window
Savings: Full instance cost

How it works:

  1. We identify SageMaker notebook instances in InService status that have not been started or reconfigured within the window (LastModifiedTime)
  2. We read the last event time of the notebook's Jupyter server log (CloudWatch Logs /aws/sagemaker/NotebookInstances, stream <name>/jupyter.log), which records kernel, session and save activity
  3. Only a notebook with no log event in the window is flagged. No readable log (no log stream, a failed read, or an Air-Gapped export) is reported as missing data, never as idle
  4. Idle notebooks continue to charge full instance cost

Example:

Notebook: my-ml-notebook
Instance Type: ml.t3.medium
Status: InService
Activity (7 days): None
Monthly Savings: $37.00

AWS Reference: SageMaker Pricing

Why MEDIUM confidence: A long job running in a kernel that logs nothing would look idle; check before stopping.


Idle SageMaker Endpointโ€‹

Confidence: HIGH
Detection: Zero invocations for 7 days, on an endpoint at least 7 days old
Savings: Full endpoint cost

How it works:

  1. We query CloudWatch for endpoint invocation metrics
  2. Endpoints with zero invocations over the last 7 days are flagged
  3. Idle endpoints charge full compute cost continuously

Example:

Endpoint: my-model-endpoint
Instance Type: ml.m5.large
Invocations (7 days): 0
Monthly Savings: $96.00

AWS Reference: SageMaker Pricing

Why HIGH confidence: Zero invocations is unambiguous. Endpoint is not being used.


Idle WorkSpacesโ€‹

Confidence: HIGH
Detection: No user connection for more than 30 days
Savings: Full WorkSpace cost

How it works:

  1. We read each WorkSpace's last user connection from the WorkSpaces connection status API
  2. WorkSpaces with no user connection for more than 30 days are flagged
  3. Monthly running mode charges continuously whether used or not

Example:

WorkSpace: ws-abc123def
Bundle: Standard (Windows)
Connections (30 days): 0
Monthly Savings: $35.00

AWS Reference: WorkSpaces Pricing

Why HIGH confidence: No user connections means no one is using this virtual desktop.


Idle Lightsail Instanceโ€‹

Confidence: HIGH
Detection: Stopped, or Lightsail CPUUtilization < 5% for 14 days (7 in an Air-Gapped export)
Savings: Full instance cost (the bundle's monthly price)

How it works:

  1. We read 14 days of hourly CPU; a running instance is judged only when 75% of those hours are present, otherwise it is reported as missing data
  2. Instances with consistently low CPU (or stopped) are flagged
  3. Lightsail charges a fixed monthly bundle price whether the instance runs or not; bundles are priced from their id (size, Windows, IPv6-only, memory- or compute-optimized). An unrecognised bundle is reported as missing data, not guessed

Example:

Instance: my-lightsail-instance
Plan: nano_3_0 ($5/month, 512 MB RAM, 2 vCPUs)
Avg CPU: 2.1% over 14 days
Monthly Savings: $5.00

AWS Reference: Lightsail Pricing

Why HIGH confidence: Actual CloudWatch metrics confirm low utilization.


Unattached Lightsail Static IPโ€‹

Confidence: HIGH
Detection: Static IP allocated but not attached to any instance
Savings: $3.65/month per unattached IP

How it works:

  1. We list all Lightsail static IPs
  2. IPs where isAttached is false are flagged
  3. AWS charges $3.65/month for unattached static IPs

Example:

Static IP: my-static-ip
IP Address: 52.1.2.3
Attached: No
Monthly Savings: $3.65

AWS Reference: Lightsail Pricing

Why HIGH confidence: Attachment is a binary property from the Lightsail API. Confirm the IP is not reserved for future use before releasing it.


Unattached Lightsail Diskโ€‹

Confidence: HIGH
Detection: Block storage disk not attached to any instance
Savings: $0.10/GB/month

How it works:

  1. We list all Lightsail block storage disks
  2. Disks where isAttached is false are flagged
  3. Storage charges apply regardless of attachment

Example:

Disk: my-data-disk
Size: 80 GB
Attached: No
Monthly Savings: $8.00

AWS Reference: Lightsail Pricing

Why HIGH confidence: Attachment is a binary property from the Lightsail API. The disk may still contain important data, so remediation creates a snapshot before deletion.


Old Lightsail Snapshotโ€‹

Confidence: MEDIUM
Detection: Manual snapshot older than 90 days
Savings: $0.05/GB/month

How it works:

  1. We list all Lightsail instance snapshots
  2. Manual snapshots older than 90 days are flagged
  3. Auto-snapshots are excluded (managed by Lightsail)

Example:

Snapshot: my-old-snapshot
Size: 20 GB
Age: 120 days
Type: Manual
Monthly Savings: $1.00

AWS Reference: Lightsail Pricing

Why MEDIUM confidence: Old snapshots may still be needed for compliance.


Idle Lightsail Load Balancerโ€‹

Confidence: HIGH
Detection: Load balancer with zero healthy target instances
Savings: $18.00/month flat rate

How it works:

  1. We list all Lightsail load balancers
  2. LBs with zero healthy instances are flagged
  3. Lightsail charges a flat monthly rate regardless of traffic

Example:

Load Balancer: my-lb
Healthy Instances: 0
Monthly Savings: $18.00

AWS Reference: Lightsail Pricing

Why HIGH confidence: Zero healthy instances is read directly from the Lightsail API. Check the load balancer is not just between deployments before deleting it.


Idle Lightsail Databaseโ€‹

Confidence: HIGH
Detection: CPU < 5% and fewer than 1 database connection on average over 14 days (7 in an Air-Gapped export)
Savings: $15โ€“$115/month (doubled for HA)

How it works:

  1. We query CloudWatch CPUUtilization and DatabaseConnections metrics
  2. Databases with consistently near-zero CPU and connections are flagged
  3. Stopped databases are also detected
  4. High-availability databases cost 2x the standard bundle price

Example:

Database: my-idle-db
Bundle: medium_2_0
Engine: MySQL
HA: No
Avg CPU: 0.5% over 14 days
Connections: 0 for 14 days
Monthly Savings: $30.00

AWS Reference: Lightsail Pricing

Why HIGH confidence: Both CPU and connection metrics confirm zero usage.


Idle Elastic Beanstalk Environmentโ€‹

Confidence: HIGH
Detection: Unhealthy environment (Red health status)
Savings: Environment resource costs (EC2, ELB, etc.)

How it works:

  1. We check each Beanstalk environment's health status via the DataProvider
  2. Environments with Red health in Ready state are flagged as unhealthy. Grey health means Beanstalk has no health data for the environment, so its verdict is reported as missing (a scan warning), never as waste
  3. An environment flagged here is not also flagged as zero-traffic (beanstalk_idle_traffic): both claim the whole environment's cost, so only one may
  4. Savings are estimated from the underlying instance type, count, and load balancer

Example:

Environment: my-app-env
Health: Red (unhealthy)
Instance Type: t3.medium (1 instance)
Monthly Savings: $32.00

AWS Reference: Elastic Beanstalk Pricing

Why HIGH confidence: Red health is a definitive AWS signal that the environment is not functioning correctly.

Remediation: a reviewed plan (terminate the environment), not a one-click fix, until the check has been proven on a live test environment.


Idle Beanstalk Environment (Zero Traffic)โ€‹

Confidence: HIGH
Detection: Zero requests on the environment's load balancer (or enhanced health) for 14 days, on an environment more than 14 days old
Savings: Full environment resource costs (EC2, load balancer, EBS)

How it works:

  1. We find the environment's load balancer from its endpoint and read its RequestCount (Application or Classic Load Balancer). Because an empty request series looks the same as no data, the load balancer's HealthyHostCount must also have reported on at least 75% of the 14 days
  2. Without a usable load balancer (single-instance, or a Network Load Balancer, which publishes no request count), we use enhanced health's ApplicationRequestsTotal, when it is configured and covers 75% of the days
  3. Anything else is reported as missing data, never as zero traffic
  4. An environment created within the last 14 days is skipped: the window needs an environment that existed throughout it
  5. Savings include the running EC2 instances, the load balancer (if any) and ~10 GB of EBS per instance, region-adjusted

Example:

Environment: staging-api
Requests (14 days): 0
Instance Type: t3.small (2 instances, load-balanced)
Monthly Savings: $48.00

AWS Reference: Elastic Beanstalk Pricing

Why HIGH confidence: Zero requests over 14 days is a definitive signal of no traffic.


Unnecessary Load Balancer on Single-Instance Beanstalkโ€‹

Confidence: HIGH
Detection: Load-balanced environment with auto-scaling min=max=1
Savings: ~$22/month (ALB fixed cost + minimum LCU)

How it works:

  1. We check environment type (LoadBalanced) and Auto Scaling group settings
  2. If min=max=1, the ALB is serving a single instance with no scaling benefit
  3. Switching to SingleInstance type removes the ALB overhead

Example:

Environment: dev-api
Type: LoadBalanced, Min=1, Max=1
ALB Cost: $16.43/month + LCU: $5.84/month
Monthly Savings: $22.27

AWS Reference: Elastic Beanstalk Environment Types

Why HIGH confidence: Configuration analysis with no ambiguity โ€” min=max=1 means no scaling.


Beanstalk Previous-Generation Instancesโ€‹

Confidence: HIGH
Detection: Environment using previous-generation instance families (t2, m4, c4, etc.)
Savings: Price delta between old and new generation ร— instance count ร— 730 hours

How it works:

  1. We check the instance type configured for each environment
  2. If the family is in our previous-gen mapping (t2โ†’t3, m4โ†’m5, c4โ†’c5, etc.), we flag it
  3. Current-gen instances offer better performance at the same or lower price

Example:

Environment: legacy-worker
Instance Type: t2.medium โ†’ t3.medium
Monthly Savings: $3.50 per instance

AWS Reference: Previous Generation Instances

Why HIGH confidence: Instance family mapping is deterministic with known pricing.


Over-Provisioned Beanstalk Environmentโ€‹

Confidence: MEDIUM
Detection: Environment with 2+ running instances and <25% avg CPU over 14 days
Savings: Excess instance costs (EC2 + EBS per removed instance)

How it works:

  1. We find the environment's running instances by Elastic Beanstalk's own elasticbeanstalk:environment-name tag and read their hourly CPUUtilization (AWS/EC2)
  2. With 2+ instances and CPU on at least 75% of the window's hours, an average below 25% over 14 days marks the environment over-provisioned
  3. We recommend the smallest instance count that would run at 50% average CPU, priced region-adjusted
  4. The finding is reported when the saving is at least $10 a month

Example:

Environment: api-production
Instances: 4 ร— t3.medium at 12% avg CPU
Recommended: 2 instances
Monthly Savings: $65.00

AWS Reference: Elastic Beanstalk Auto Scaling

Why MEDIUM confidence: CPU metrics are a strong indicator but don't capture memory or I/O bottlenecks.


Orphaned RDS from Beanstalk Environmentโ€‹

Confidence: HIGH
Detection: RDS created by a Beanstalk environment that no longer exists, with zero DatabaseConnections for 14 days
Savings: RDS instance + storage costs

How it works:

  1. We scan RDS instances for Elastic Beanstalk's own tags: elasticbeanstalk:environment-id / environment-name, or a CloudFormation stack named awseb-<environment id>-stack. A database made by any other CloudFormation stack (CDK, SAM) is not Beanstalk's and is not checked
  2. A database whose environment still exists is not orphaned and is skipped
  3. We check CloudWatch DatabaseConnections โ€” zero on every day of the 14 days, with data on at least 75% of them, means no app is using it; sparse or missing data means the verdict is missing, not idle
  4. This detector is online-only (requires cross-service API calls)

Example:

RDS Instance: eb-prod-api-db (db.t3.micro)
Connections (14 days): 0
Associated Environment: prod-api (terminated)
Monthly Savings: $14.00

AWS Reference: Elastic Beanstalk with RDS

Why HIGH confidence: Zero connections for 14 days with EB tags is a definitive orphan signal.


EKS Extended Support Costโ€‹

Confidence: HIGH
Detection: EKS cluster version in extended support, or within 90 days of it
Savings: Extended support surcharge ($0.50/cluster-hour on top of the $0.10 standard fee)

How it works:

  1. We check each EKS cluster's Kubernetes version against AWS's EKS release calendar
  2. Versions past their end of standard support incur the extended support surcharge (1.31, 1.32 and 1.33 as of October 2026); versions within 90 days of it get an early warning
  3. Surcharge is per cluster-hour and can be significant for multi-cluster environments. A cluster already in extended support is reported when its monthly surcharge is at least $0.01

Example:

Cluster: my-production-cluster
Version: 1.32 (extended support since 2026-03-23)
Nodes: 5
Monthly Surcharge: the extended-support hourly surcharge ร— 730 hrs
Action: Upgrade to supported version

AWS Reference: EKS Extended Support Pricing

Why HIGH confidence: Cluster version is deterministic metadata. Extended support dates are published by AWS.


Oversized WorkSpacesโ€‹

Confidence: MEDIUM
Detection: Peak CPU and memory (CloudWatch CPUUsage / MemoryUsage) fit the next smaller bundle over 14 days, and the bundle price difference is at least 15 USD a month
Savings: Price difference to the next smaller bundle (PowerPro โ†’ Power โ†’ Performance โ†’ Standard, GraphicsPro โ†’ Graphics)

How it works:

  1. We read hourly peak CPUUsage and MemoryUsage for each downgradeable WorkSpace
  2. The p95 of hourly CPU peaks and the highest memory peak must each stay under 70% of the smaller bundle's capacity, measured on at least 75% of the 14 days
  3. Without that data (an Air-Gapped export does not carry it) the WorkSpace is reported as missing data, not flagged

Example:

WorkSpace: ws-abc123def
Bundle: Power (4 vCPU, 16 GB)
Peak CPU (p95): 18%, peak memory: 25% over 14 days
Recommended: Performance (2 vCPU, 7.5 GB)
Monthly Savings: $42.00

AWS Reference: WorkSpaces Pricing

Why MEDIUM confidence: Usage patterns may vary seasonally. Verify with the workspace owner before downsizing.


WorkSpaces AutoStop Opportunityโ€‹

Confidence: HIGH
Detection: AlwaysOn WorkSpace with > 7 days since last user connection, and an estimated AutoStop saving of at least 10 USD a month
Savings: Difference between AlwaysOn and AutoStop billing mode

How it works:

  1. We identify AlwaysOn WorkSpaces where the user hasn't connected in over 7 days
  2. AlwaysOn charges a fixed monthly rate regardless of usage
  3. AutoStop charges a lower base fee plus hourly usage, saving money for infrequent users
  4. Break-even analysis determines if AutoStop would be cheaper

Example:

WorkSpace: ws-abc123def
Bundle: Performance (AlwaysOn)
Last Connection: 12 days ago
AlwaysOn Cost: $60.00/month
Est. AutoStop Cost: $15.25/month (base + ~20 hours)
Monthly Savings: $44.75

AWS Reference: WorkSpaces Pricing

Why HIGH confidence: Connection status directly from AWS API. AlwaysOn vs AutoStop pricing is deterministic.


WorkSpaces Pool Overprovisioned Capacityโ€‹

Confidence: MEDIUM
Detection: WorkSpaces Pool peak utilization over 14 days โ‰ค 75% with excess capacity โ‰ฅ 2 slots, costing at least 25 USD a month
Savings: Idle slots times a floor price per slot. An always-on pool keeps idle slots running, so we use the lowest running pool rate in any region ($36.50 a month). Most bundles and regions cost more than that floor, so the estimate is on the low side. An auto-stop pool stops idle slots, so we use the lowest stopped rate ($18.25 a month), which is the same for every bundle

How it works:

  1. We check each running WorkSpaces Pool's desired sessions against its peak active (in-use) sessions: the highest hourly value of CloudWatch ActiveUserSessionCapacity over 14 days. The metric must have data on at least 11 of the 15 calendar dates that window touches (75% of the window's days, rounded up). A pool without that history is not judged
  2. Pools whose peak is at most 75% of their desired sessions, with at least 2 idle at the peak, are flagged. An Air-Gapped export has no session history, so offline scans use the active sessions at export time
  3. Excess slots incur charges regardless of whether users are connected. We price each idle slot by the pool's running mode, from the AWS Price List API (read 2026-11-09). For an always-on pool we use the lowest running pool rate in any region, because the API does not tell us the pool's bundle or region; most pools cost more. The stopped rate is the same for every bundle. If the running mode is missing, we use the lower stopped rate. These are the lowest rates in any region, so most pools cost more

Example:

Pool: engineering-pool
Configured Sessions: 20
Peak Active Sessions (14 days): 8 (40% utilization)
Running Mode: AUTO_STOP
Excess Slots: 12
Monthly Savings: $219.00

AWS Reference: WorkSpaces Pools Pricing

Why MEDIUM confidence: Pool capacity comes directly from the WorkSpaces API. Online scans judge demand by the pool's peak of ActiveUserSessionCapacity over the last 14 days, which can still miss a monthly or seasonal peak. Air-Gapped scans use the count when the export was taken, which is weaker. Only pools in the RUNNING state are checked.


WorkSpaces Windows License Optimizationโ€‹

Confidence: MEDIUM
Detection: โ‰ฅ 5 Windows license-included WorkSpaces (non-BYOL)
Savings: ~$4/month per desktop via BYOL or Linux alternatives

How it works:

  1. We count WorkSpaces running Windows with AWS-included licenses
  2. Organizations with Microsoft Volume Licensing or Software Assurance can bring their own licenses (BYOL)
  3. Alternatively, eligible users can migrate to Amazon Linux WorkSpaces to eliminate the license premium

Example:

Windows WorkSpaces (license-included): 25
License Premium: ~$4/desktop/month
Monthly Savings: $100.00

AWS Reference: WorkSpaces BYOL

Why MEDIUM confidence: BYOL eligibility depends on your Microsoft licensing agreement. This is an advisory finding โ€” verify licensing terms before acting.


Containers Detectorsโ€‹

Idle ECS Serviceโ€‹

Confidence: HIGH
Detection: ECS service with desiredCount > 0 but runningCount = 0, and no deployment in progress
Savings: $0 (reported as a hygiene finding)

How it works:

  1. We identify ECS services where desired tasks are set but no tasks are actually running
  2. This indicates a failed deployment or an abandoned service
  3. ECS bills running tasks only, so a service with none costs nothing for its tasks; the finding is shown at $0 rather than the cost of tasks that never run

Example:

Service: my-api-service
Cluster: production
Desired: 2, Running: 0
Task CPU: 1 vCPU, Memory: 2 GB
Monthly Savings: $0.00 (hygiene)

AWS Reference: ECS Pricing

Why HIGH confidence: A service actively trying to run tasks but failing is a clear operational issue.


ECS Without Auto-Scalingโ€‹

Confidence: MEDIUM
Detection: ECS service with โ‰ฅ 2 running tasks and no Application Auto Scaling target
Savings: ~30% of the service's monthly compute cost, taken after any CPU or memory rightsizing savings reported for the same service

How it works:

  1. We check ECS services with multiple running tasks
  2. Services without Application Auto Scaling registered are flagged
  3. Auto-scaling allows services to scale down during low-traffic periods
  4. The saving is priced for Fargate task sizes only, and a finding is reported when it is at least $0.01 a month

Example:

Service: my-web-frontend
Cluster: production
Running Tasks: 4 (constant)
Auto Scaling: Not configured
Est. Monthly Savings: $98.00 (30% of compute)

AWS Reference: ECS Auto Scaling

Why MEDIUM confidence: Some services intentionally run at fixed capacity (e.g., worker pools). Verify workload patterns before enabling auto-scaling.


Container Insights Wasteโ€‹

Confidence: HIGH
Detection: Container Insights enabled on a dev/test cluster, a cluster with fewer than 3 active services, or one with no running tasks
Savings: (50 metrics per active service + 20) ร— $0.07 a month, for example ~$4.90 for a one-service cluster

How it works:

  1. We check if Container Insights is enabled on ECS clusters
  2. Dev/staging/sandbox clusters or clusters with few services don't benefit enough to justify the monitoring cost
  3. We estimate ~50 metrics per active service plus 20 per cluster, at $0.07 per metric a month. A finding is reported when that is at least $0.01 a month

Example:

Cluster: dev-cluster
Environment: development
Services: 1
Container Insights: Enabled
Est. Metrics: 70
Monthly Savings: $4.90

AWS Reference: CloudWatch Container Insights Pricing

Why HIGH confidence: Cluster environment and service count are deterministic. Container Insights cost is directly proportional to metric volume.


Oversized ECS Memoryโ€‹

Confidence: MEDIUM
Detection: CloudWatch MemoryUtilization max < 40% of the task memory over 7 days
Savings: Proportional to memory reduction in Fargate task definition

How it works:

  1. We analyze ECS task memory utilization via CloudWatch metrics
  2. Fargate services whose busiest task peaks below 40% of the task memory are flagged. A service younger than 7 days is skipped
  3. The recommendation is the smallest valid memory size for the task's CPU that leaves 50% headroom over the peak and is not below the containers' own memory settings
  4. Reducing memory allocation in the task definition directly reduces Fargate cost; a finding is reported when the saving is at least $0.01 a month

Example:

Service: my-worker
Task Memory: 4 GB
Max Memory Used: 1.2 GB (30%)
Recommended: 2 GB
Monthly Savings: $23.40

AWS Reference: ECS Task Definition Parameters

Why MEDIUM confidence: Memory usage may spike under certain workloads not captured during the analysis window. Monitor after resizing.


Storage Detectorsโ€‹

Unattached EBS Volumeโ€‹

Confidence: HIGH
Detection: describe_volumes with status=available
Savings: size ร— price per GB (varies by volume type)

Pricing Table:

Volume TypePrice per GB-month
gp3$0.08
gp2$0.10
io1/io2$0.125 + IOPS
st1$0.045
sc1$0.015

Example:

Volume: vol-0abc123def456
Type: gp3
Size: 500GB
Status: available (not attached)
Monthly Savings: $40.00 (500 ร— $0.08)

AWS Reference: EBS Pricing

Why HIGH confidence: We get exact size and type from the API. Pricing is fixed per GB.


gp2 โ†’ gp3 Migrationโ€‹

Confidence: HIGH
Detection: describe_volumes with volume-type=gp2
Savings: size ร— the per-GB price difference between gp2 and gp3 (see the AWS EBS pricing page), less any gp3 IOPS and throughput needed to keep the volume's gp2 performance

How it works:

  1. gp2 and gp3 have different per-GB prices (see the AWS EBS pricing page)
  2. gp3 costs less per GB-month than gp2
  3. gp3 includes 3,000 IOPS and 125 MiB/s. gp2 delivers 3 IOPS per GiB (up to 16,000), so a volume above 1,000 GiB has more, and one of 334 GiB or more sustains 250 MiB/s. For those volumes the recommended modify-volume sets --iops and --throughput to the gp2 baseline, and the price of the extra IOPS and throughput (see the AWS EBS pricing page) comes off the saving.

Example:

Volume: vol-0abc123def456
Type: gp2
Size: 1000GB
Current Cost: gp2 price ร— 1000 GB
After Migration: gp3 price ร— 1000 GB + the extra throughput needed to keep 250 MiB/s
Monthly Savings: the difference

AWS Reference: EBS Pricing

Why HIGH confidence: AWS states gp3 costs less than gp2 with equal or better baseline performance.


Old EBS Snapshotsโ€‹

Confidence: MEDIUM
Detection: Snapshots at least 90 days old (the default threshold). The newest snapshot of a volume that still exists is never flagged.
Savings: VolumeSize ร— $0.05/GB-month

Example:

Snapshot: snap-0abc123def456
Age: 180 days
Size: 100GB
Monthly Savings: $5.00

AWS Reference: EBS Pricing (Snapshots)

Why MEDIUM confidence: The price is fixed at $0.05/GB-month, but age alone cannot tell an old snapshot from one kept on purpose for recovery.

Keep a snapshot on purpose: Tag it cloudcostwise:keep (any value) or DoNotDelete=true. The old, orphaned and AMI-orphaned snapshot checks then skip it, and the scan lists how many it skipped. Remove the tag to check the snapshot again.

Newest snapshot: The newest snapshot of each volume that still exists is never flagged as old, so a volume always keeps its latest restore point. Snapshots of a deleted volume are still checked as orphaned.


Orphaned EBS Snapshotsโ€‹

PropertyValue
Waste Typeorphaned_ebs_snapshot
CategoryStorage Optimization
ConfidenceHIGH
Risk LevelLOW
Savings Estimatevolume_size_gb ร— $0.05/month (upper-bound; actual size may be smaller due to incremental storage)

What it detects: EBS snapshots whose source volume has been deleted. When a volume is terminated, its snapshots remain and continue incurring charges indefinitely. These orphaned snapshots cannot be used for incremental recovery of the original workload โ€” they only serve as standalone restore points.

Detection logic: Cross-references all snapshots against existing EBS volumes. If a snapshot's VolumeId refers to a volume that no longer exists, the snapshot is flagged as orphaned.

Recommended action: Delete orphaned snapshots that are no longer needed. Consider creating an AWS Data Lifecycle Manager policy to automate snapshot retention.

AWS CLI:

# List orphaned snapshots (source volume deleted)
aws ec2 describe-snapshots --owner-ids self \
--query 'Snapshots[*].{ID:SnapshotId,Vol:VolumeId,Size:VolumeSize,Created:StartTime}' \
--output table

AMI Orphaned Snapshotsโ€‹

PropertyValue
Waste Typeami_orphaned_snapshot
CategoryStorage Optimization
ConfidenceHIGH
Risk LevelLOW
Savings Estimatevolume_size_gb ร— $0.05/month (upper-bound; actual size may be smaller due to incremental storage)

What it detects: EBS snapshots that were created by CreateImage to back an AMI, but the AMI has since been deregistered. When an AMI is deregistered, its backing snapshots are NOT automatically deleted โ€” they remain and continue incurring charges.

Detection logic: Matches snapshot descriptions against the pattern Created by CreateImage(i-xxx) for ami-xxx, then checks whether the referenced AMI is still registered. If the AMI no longer exists, the snapshot is flagged.

Recommended action: Delete the orphaned snapshot after confirming no other AMIs or launch templates reference it. Consider automating AMI cleanup to include backing snapshot deletion.

AWS CLI:

# Find snapshots created by CreateImage for deregistered AMIs
aws ec2 describe-snapshots --owner-ids self \
--query 'Snapshots[?starts_with(Description, `Created by CreateImage`)].{ID:SnapshotId,Desc:Description,Size:VolumeSize}' \
--output table

ECR Repository Without Lifecycle Policyโ€‹

Confidence: MEDIUM
Detection: ECR repository with no lifecycle policy configured
Savings: Measured size of the repository's old and untagged images ร— $0.10/GB-month; $0 when the Old/Untagged ECR Images finding already counts them

How it works:

  1. We check each ECR repository for a lifecycle policy
  2. Repos without policies accumulate old images indefinitely
  3. The saving is what a policy would remove today: the old and untagged images. When the same repository has an Old or Untagged ECR Images finding, the dollars stay on that finding and this one shows $0, so the repository is not counted twice

Example:

Repository: my-app-images
Lifecycle Policy: None
Total Images: 150 (120 untagged)
Old/untagged images: 30GB (reported by Old ECR Images)
Monthly Savings: $0 here ($3.00 on the Old ECR Images finding)

AWS Reference: ECR Pricing

Why MEDIUM confidence: Actual savings depend on cleanup behavior; lifecycle policies prevent future waste.


S3 Bucket Without Lifecycle Policyโ€‹

Confidence: MEDIUM
Detection: S3 bucket of at least 10 GB with no lifecycle rules configured
Savings: A conservative Intelligent-Tiering estimate, never more than 40% of the bucket's S3 Standard storage cost (the same estimate as S3 Wrong Storage Class): objects under 128 KB are excluded, the share is halved when there is no access signal, it is adjusted for request and egress cost, and the Intelligent-Tiering monitoring fee is subtracted

How it works:

  1. We check each S3 bucket for lifecycle configuration
  2. Buckets without lifecycle rules accumulate objects indefinitely
  3. Lifecycle rules can transition objects to cheaper storage or delete old data. The saving is an estimated share of the Standard storage bill, not the whole bill; when the bucket also has an S3 Wrong Storage Class finding, that finding carries the dollars and this one shows $0

Example:

Bucket: my-application-logs
Lifecycle Rules: None
Objects: 2.5 million
Size: 500GB
Monthly Cost: $11.50 (S3 Standard)
Recommendation: Add lifecycle to transition older objects to cheaper classes
Estimated Savings: $4.60/month (40%)

AWS Reference: S3 Pricing

Why MEDIUM confidence: Lifecycle configuration is binary (exists or not), but how much tiering helps depends on your access patterns, so the saving is a conservative estimate.


Incomplete Multipart Uploadsโ€‹

Confidence: HIGH
Detection: S3 multipart uploads started more than 7 days ago and never completed
Savings: Not measured (shown as $0)

How it works:

  1. We list incomplete multipart uploads in S3 buckets
  2. Uploads started in the last 7 days are skipped: they may still be in progress
  3. Failed or abandoned uploads leave orphaned parts, charged as regular S3 storage. Their size needs s3:ListMultipartUploadParts, which the CloudWise scan role does not have, so no saving is claimed; S3 Storage Lens shows the bytes
  4. Remediation is a plan you review (no one-click Fix This): aborting an upload discards its parts

Example:

Bucket: my-large-files
Incomplete Uploads older than 7 days: 15
Monthly Savings: $0 (size not measured)
Recommendation: Add lifecycle rule to abort incomplete uploads after 7 days

AWS Reference: S3 Pricing

Why HIGH confidence: Uploads started more than the threshold ago and never completed are abandoned; recent uploads are skipped because they may still be in progress.


Idle EFS File Systemโ€‹

Confidence: HIGH
Detection: File system with no mount targets
Savings: Full storage cost

How it works:

  1. We list each EFS file system and its mount targets
  2. File systems with no mount targets are flagged, because nothing can mount them
  3. EFS charges for stored data even when it cannot be reached

Example:

File System: fs-0abc123
Size: 100GB
Mount Targets: 0
Monthly Savings: $30.00

AWS Reference: EFS Pricing

Why HIGH confidence: With no mount targets, no instance or container can reach this file system.


Idle FSx File Systemโ€‹

Confidence: HIGH
Detection: Zero read and write bytes for 7 days
Savings: Provisioned storage plus throughput capacity, at the file system's own list price

How it works:

  1. We query CloudWatch for FSx DataReadBytes and DataWriteBytes
  2. File systems with zero I/O over the 7-day window are flagged. A file system younger than the window is skipped, and so is one costing less than $0.01 a month
  3. FSx charges continuously whether data is accessed or not. Prices depend on the type, deployment type and storage type (for example Windows SSD $0.13/GB Single-AZ, $0.23/GB Multi-AZ; throughput $2.20 or $4.50 per MBps), region-scaled

Example:

File System: fs-0abc123def456
Type: FSx for Windows
Deployment: Single-AZ 2, SSD
Capacity: 300GB, 32 MBps throughput
I/O (7 days): 0
Monthly Savings: $109.40 (300 ร— $0.13 + 32 ร— $2.20)

AWS Reference: FSx Pricing

Why HIGH confidence: Zero network I/O confirms no file access. FSx charges by provisioned capacity.


Oversized FSx Filesystemโ€‹

Confidence: MEDIUM
Detection: Storage utilization below 40% of provisioned capacity
Savings: Storage above twice the used capacity ร— the file system's own storage rate

How it works:

  1. We compare provisioned storage against actual used capacity via CloudWatch FreeStorageCapacity
  2. Filesystems where used capacity stays below 40% of provisioned (over the last 7 days) are flagged, when the saving is at least $0.01 a month
  3. FSx storage capacity can only be increased, and a backup restores at the original size, so reclaiming it means migrating to a new, smaller file system (for example with AWS DataSync)

Example:

File System: fs-0abc123def456
Type: FSx for Windows
Capacity: 2,000 GB provisioned (Single-AZ SSD)
Used: 500 GB (25%)
Estimated Savings: $130.00/month (1,000 GB above a 1,000 GB target ร— $0.13)

AWS Reference: FSx Pricing

Why MEDIUM confidence: Low utilization is a strong signal, but resizing requires operational caution. Some FSx types don't support in-place shrink.


FSx Throughput Overprovisionedโ€‹

Confidence: MEDIUM
Detection: Busiest hour's throughput below 30% of provisioned capacity
Savings: (current โˆ’ recommended throughput) ร— the deployment type's price per MBps-month

How it works:

  1. We read hourly DataReadBytes and DataWriteBytes over the last 7 days and take the busiest hour
  2. Filesystems (Windows/ONTAP/OpenZFS) whose busiest hour stays below 30% of capacity are flagged, when the saving is at least $0.01 a month
  3. The recommendation is the smallest throughput setting for the deployment type that the busiest hour would use at most 50% of; a file system already at its smallest setting, or with an unknown deployment type, is not flagged
  4. Lustre excluded: its throughput is part of the storage price
  5. Air-Gapped: export script 1.18.0+ records the hourly reads and writes; older exports leave this check out

Example:

File System: fs-0abc123def456
Type: FSx for ONTAP
Deployment: Single-AZ 1
Provisioned Throughput: 512 MB/s
Busiest hour: 40 MB/s (8%)
Recommended: 128 MB/s
Estimated Savings: $276.48/month (384 MBps ร— $0.72, ONTAP Single-AZ 1 throughput rate)

AWS Reference: FSx Pricing

Why MEDIUM confidence: Throughput is a major cost driver, but requires validation of latency baselines before changing.


Old FSx Backupโ€‹

Confidence: MEDIUM
Detection: Manual FSx backup at least 90 days old
Savings: Up to the backup's size (SizeInBytes) ร— $0.05/GB-month

How it works:

  1. We list all FSx backups and filter for manual/user-initiated type
  2. Backups at least 90 days old are flagged, when the estimated cost is at least $0.01 a month
  3. Retention-tagged backups (compliance, retain, legal-hold) are excluded
  4. Automatic backups managed by FSx retention policies are excluded
  5. FSx backups are incremental, so the billed share of one backup can be less than its size; the figure is an upper bound. A backup without a reported size is not flagged

Example:

Backup: backup-0abc123def456
Filesystem: fs-0abc123 (Windows)
Age: 180 days
Type: USER_INITIATED
Size: 400 GB
Monthly Savings: up to $20.00

Note: Backup deletion is irreversible. Verify backup is no longer needed for recovery.

AWS Reference: FSx Pricing

Why MEDIUM confidence: Manual backups past retention threshold are likely unnecessary, but deletion is irreversible.


Old ECR Imagesโ€‹

Confidence: MEDIUM
Detection: Container images pushed more than 90 days ago, once old plus untagged images in the repository exceed 0.5 GB
Savings: Image size ร— $0.10/GB-month

How it works:

  1. We list all images in ECR repositories
  2. Images older than 90 days are flagged for cleanup
  3. Old images often represent deprecated versions

Example:

Repository: my-app-images
Images > 90 days: 45
Total Size: 12GB
Monthly Savings: $1.20

AWS Reference: ECR Pricing

Why MEDIUM confidence: Old images may be needed for rollbacks. Consider keeping last N versions.


Over-Provisioned IOPSโ€‹

Confidence: MEDIUM
Detection: In-use EBS volume whose highest one-minute IOPS over 14 days is under 50% of its provisioned IOPS
Savings: Provisioned-IOPS price at the current setting minus the price at peak + 30% (io1 $0.065/IOPS-month; io2 $0.065 up to 32,000, then $0.0455, then $0.032; gp3 $0.005 above the 3,000 included IOPS)
Remediation: Fix This. On approval, one modify_volume sets the volume's IOPS to the recommendation, and only if the volume still has the type and IOPS it had when it was scanned. Undo restores the previous IOPS. EBS limits how often a volume can be changed, so undo opens a fixed wait after the change, when AWS normally accepts the next one, and closes with the rollback window. A large volume can still be optimizing then. AWS then rejects the undo and the action ends as Rollback failed, so check that the volume's modification has completed before you select Undo

How it works:

  1. We pick in-use io1/io2 volumes and gp3 volumes above their included 3,000 IOPS that have been attached for the whole 14 days
  2. One batched CloudWatch GetMetricData request reads each volume's one-minute VolumeReadOps + VolumeWriteOps as IOPS
  3. If the highest minute stays below 50% of provisioned, we recommend the peak plus 30% headroom (never below io1/io2's 100, or gp3's 3,000 or 1 IOPS per 0.25 MiB/s of its provisioned throughput, whichever is higher)
  4. A volume with less than 75% of the window's minutes measured is left out and named in the scan's data warnings, never read as idle

Example:

Volume: vol-0abc123def456
Type: io2, Size: 500 GB
Provisioned IOPS: 10,000
Peak one-minute IOPS (14d): 3,200 (32%)
Recommended: 4,160 IOPS
Monthly Savings: $379.60

Air-Gapped: export script 1.17.0+ records each candidate volume's peak one-minute IOPS over its 7-day window. Exports from older scripts leave the check out, with a data warning.

AWS Reference: EBS Pricing

Why HIGH confidence: CloudWatch IOPS metrics are precise. We use peak (not average) to avoid under-provisioning.


EFS Without Lifecycle Policyโ€‹

Confidence: MEDIUM
Detection: EFS filesystem โ‰ฅ 1 GB without Infrequent Access lifecycle transition
Savings: Standard ($0.30/GB) โ†’ IA ($0.025/GB) on ~80% of infrequently accessed files

How it works:

  1. We read each EFS filesystem's lifecycle configuration
  2. Filesystems with no TransitionToIA or TransitionToArchive rule keep all data in the Standard storage class (a TransitionToPrimaryStorageClass rule alone moves nothing)
  3. Most EFS data becomes infrequently accessed after 30 days โ€” significant savings opportunity
  4. If the lifecycle configuration can't be read, the filesystem is left out and named in the scan's data warnings, never reported as having no policy

Example:

Filesystem: fs-0abc123def456
Size: 250 GB (Standard)
Lifecycle Policy: None
Est. IA-eligible: 200 GB (80%)
Monthly Savings: $55.00

AWS Reference: EFS Lifecycle Management

Why MEDIUM confidence: Actual access patterns determine IA eligibility. The 80% estimate is based on typical workloads.


Untagged ECR Imagesโ€‹

Confidence: HIGH
Detection: ECR repository with untagged images totaling > 0.1 GB, while old plus untagged images stay at or under 0.5 GB (above that, Old ECR Images reports them)
Savings: Untagged image size ร— $0.10/GB/month

How it works:

  1. We scan ECR repositories for images without tags
  2. Untagged images are typically intermediate build layers or superseded images
  3. These accumulate silently and can grow to significant storage costs

Example:

Repository: my-service
Untagged Images: 156
Total Size: 8.5 GB
Monthly Savings: $0.85

AWS Reference: ECR Pricing

Why HIGH confidence: Image tag status and size are exact metadata from the ECR API.


Old Backup Recovery Pointsโ€‹

Confidence: MEDIUM
Detection: AWS Backup recovery point at least 180 days old without compliance hold
Savings: Backup storage at $0.05/GB/month

How it works:

  1. We list all AWS Backup recovery points across vaults
  2. Completed points at least 180 days old without a compliance-hold tag are flagged, unless their estimated cost is under $0.01 a month
  3. Organizations often forget to set lifecycle expiration on backup plans

Example:

Resource: arn:aws:rds:us-east-1:123456789:db:mydb
Recovery Point Age: 245 days
Size: 50 GB
Monthly Savings: $2.50

AWS Reference: AWS Backup Pricing

Why MEDIUM confidence: Compliance requirements vary. Some industries require longer retention. Verify before deleting.


Redundant Backup Recovery Pointsโ€‹

Confidence: MEDIUM
Detection: Two different backup plans backed up the same resource within 1 hour
Savings: Redundant point storage at $0.05/GB/month

How it works:

  1. We group recovery points by resource ARN
  2. A pair is redundant only when it comes from two different backup plans (both known) and the points are under 1 hour apart. One plan's consecutive points (a daily plan's start window can land them under 24 hours apart, or an hourly plan) are never flagged, and neither is an on-demand point
  3. The point flagged is the one that expires sooner, so the longer-retained point (say, a monthly rule's year-long copy) is always the one kept. A point whose estimated cost is under $0.01 a month is skipped
  4. Remediation is a plan you review (no one-click Fix This): deleting a recovery point cannot be undone

Example:

Resource: arn:aws:ec2:us-east-1:123456789:volume/vol-abc123
Plans: ebs-daily (7-day retention) and all-resources-daily (35-day retention), 20 minutes apart
Flagged: the ebs-daily point (expires sooner), 120 GB
Monthly Savings: $6.00

AWS Reference: AWS Backup Pricing

Why MEDIUM confidence: Some workloads intentionally create frequent backups for RPO requirements.


Backup Plan Without Lifecycle Tieringโ€‹

Confidence: MEDIUM
Detection: Backup plan rule that retains for 90 days or more but has no cold storage transition
Savings: the per-GB price difference between warm and cold storage (see the AWS Backup pricing page)

How it works:

  1. We analyze backup plan lifecycle rules
  2. Rules that delete after 90 days or more without a cold storage transition are flagged
  3. Cold storage is significantly cheaper but has longer retrieval times

Example:

Backup Plan: daily-rds-backup
Retention: 365 days
Cold Transition: None
Total Backup Size: 500 GB
Monthly Savings: $5.00

AWS Reference: AWS Backup Lifecycle

Why MEDIUM confidence: Cold storage retrieval is slower. Verify RTO requirements before enabling tiering.


Stale Backup Plan Assignmentโ€‹

Confidence: HIGH
Detection: Backup plan with explicit resource selections that has not run for 30 days or more
Savings: Backup plan resource selection overhead

How it works:

  1. We read each plan's last execution date and its resource selections
  2. A plan that has not run for 30 days or more (or has no recorded run) is stale; each of its selections that lists explicit resource ARNs is flagged, because the listed resources may no longer exist
  3. A plan that ran within the last 30 days is skipped

Example:

Backup Plan: weekly-ebs
Selection: vol-abc123 (DELETED)
Status: Resource not found
Action: Remove stale assignment

AWS Reference: AWS Backup Resource Assignment

Why HIGH confidence: The last run of the plan is recorded by AWS Backup. Verify the resource was not replaced with a different ID before removing the assignment.


Backup Cross-Region Copy Overreachโ€‹

Confidence: LOW
Detection: More than 10 copy jobs and 100 GB copied to one destination vault in 90 days
Savings: None claimed ($0); the monthly transfer cost of the copies is shown

How it works:

  1. We group the last 90 days of AWS Backup copy jobs by destination vault
  2. A destination receiving more than 10 copies and 100 GB is flagged for review, if the destination vault still exists
  3. Each cross-region copy incurs data transfer ($0.02/GB) and destination storage charges. Which copies your recovery plan needs is a DR decision, so no saving is claimed; the finding shows the copies' average monthly transfer cost as the cost at stake

Example:

Backup Plan: critical-data
Source: us-east-1
Destination vault: eu-west-1 dr-vault
Copied in 90 days: 900 GB (300 GB/month)
Cross-region transfer at stake: $6.00/month, plus destination storage
Monthly Savings: $0 (review)

AWS Reference: AWS Backup Cross-Region

Why LOW confidence: Cross-region copies serve DR purposes. Verify disaster recovery requirements before removing.


S3 Empty Bucketโ€‹

Confidence: LOW
Detection: S3 bucket with 0 objects, created at least 30 days ago
Savings: Zero direct savings (hygiene finding)

How it works:

  1. We identify empty S3 buckets that have existed for at least 30 days
  2. Infrastructure buckets (CDK, CloudFormation, logging prefixes) are excluded
  3. Empty buckets consume no storage but create management overhead

Example:

Bucket: old-data-export-2024
Objects: 0, Size: 0 bytes
Created: 2024-06-15 (300+ days ago)
Monthly Savings: $0.00 (cleanup only)

AWS Reference: S3 Pricing

Why LOW confidence: Buckets may be pre-provisioned for future use or referenced by application configuration.


S3 Wrong Storage Classโ€‹

Confidence: LOW
Detection: Bucket with at least 90% Standard storage, at least 50 GB, no Intelligent-Tiering
Savings: A conservative Intelligent-Tiering estimate, never more than 40% of the Standard storage cost, halved when there is no access signal, less the monitoring fee

How it works:

  1. We analyze S3 storage class distribution per bucket
  2. Buckets with predominantly Standard storage and no tiering transitions are flagged
  3. S3 Intelligent-Tiering automatically moves infrequently accessed objects to lower-cost tiers

Example:

Bucket: application-logs
Size: 250 GB (98% Standard)
Intelligent-Tiering: Not configured
Est. Monthly Savings: $1.44

AWS Reference: S3 Intelligent-Tiering

Why LOW confidence: Access patterns vary. Intelligent-Tiering adds a small monitoring fee ($0.0025/1000 objects).


S3 Rapid Growthโ€‹

Confidence: LOW
Detection: Bucket growth of 100% or more against the newest daily size from 28 to 32 days earlier, with at least 10 GB absolute growth, on a bucket of at least 1 GB
Savings: Variable โ€” depends on addressing root cause of growth

How it works:

  1. We compare today's size with the newest daily StandardStorage size from 28 to 32 days earlier (an Air-Gapped export carries its own earlier size)
  2. Buckets doubling in size with significant absolute growth are flagged
  3. Often indicates missing lifecycle rules, runaway logging, or misconfigured pipelines

Example:

Bucket: data-pipeline-output
30 days ago: 45 GB
Now: 120 GB (+167%)
Growth Rate: 2.5 GB/day
Monthly Savings: Potential $1.73+ if addressed

AWS Reference: S3 Storage Lens

Why LOW confidence: Growth may be expected (new data pipeline, migration). Requires investigation into root cause.


S3 High Request & Transfer Costโ€‹

Confidence: LOW
Detection: Non-storage costs (requests + transfer) exceed storage costs
Savings: Variable โ€” CDN, VPC endpoints, or caching can reduce transfer costs

How it works:

  1. We compare S3 request and transfer costs against storage costs
  2. When requests/transfer cost more than storage, architectural optimization may help
  3. Common solutions: CloudFront for public access, VPC endpoints for private access

Example:

Bucket: api-assets
Storage Cost: $5.00/month
Request + Transfer Cost: $12.00/month (240% of storage)
Monthly Savings: ~$6.00 with CloudFront

AWS Reference: S3 Request Pricing

Why LOW confidence: Architectural changes require significant effort. Cost ratio is an indicator, not a definitive finding.


Database Detectorsโ€‹

Idle RDS Instanceโ€‹

Confidence: HIGH
Detection: CloudWatch DatabaseConnections = 0 for 14 days, on an instance at least 14 days old
Savings: Full instance cost

How it works:

  1. We query CloudWatch for database connection metrics
  2. Zero connections for 14 days = definitely unused
  3. Monthly cost based on instance class and Multi-AZ configuration

Example:

Database: my-dev-database
Class: db.t3.medium
Engine: MySQL
Connections (14 days): 0
Monthly Savings: $49.28

AWS Reference: RDS Pricing

Why HIGH confidence: Zero connections is an unambiguous signal. No one is using this database.


Old RDS Snapshotsโ€‹

Confidence: MEDIUM
Detection: Manual snapshots at least 90 days old (the default threshold)
Savings: AllocatedStorage ร— $0.095/GB-month

Example:

Snapshot: my-database-snapshot-2024
Age: 120 days
Size: 200GB
Monthly Savings: $19.00

AWS Reference: RDS Pricing (Backup Storage)

Why MEDIUM confidence: Old snapshots may be needed for compliance. Review before deleting.


Aurora I/O Optimization Opportunityโ€‹

Confidence: HIGH
Detection: Aurora Standard cluster where I/O charges exceed 25% of total spend over the last 30 days
Savings: Difference between Standard (storage + I/O) and I/O-Optimized (storage only) costs

How it works:

  1. We identify Aurora clusters using Standard storage (not I/O-Optimized)
  2. Calculate the I/O-to-total-spend ratio from 30 days of CloudWatch VolumeReadIOPs and VolumeWriteIOPs
  3. If I/O charges exceed 25% of total Aurora spend and switching would save at least $0.01 a month, switching to I/O-Optimized eliminates per-I/O charges
  4. I/O-Optimized adds a 30% premium on compute and charges $0.225/GB storage (Standard: $0.10/GB) but includes unlimited I/O

Example:

Cluster: my-aurora-cluster
Engine: aurora-postgresql
Storage Type: Aurora Standard
Monthly I/O Cost: $450.00
Monthly Storage Cost: $180.00
I/O Ratio: 71.4% (above the break-even share)
Switch to I/O-Optimized Savings: $216.00/month

AWS Reference: Aurora Pricing โ€” I/O-Optimized

Why HIGH confidence: I/O costs are directly measurable from CloudWatch metrics. The 25% threshold is AWS's own recommended breakeven point.


Aurora Extended Support Costโ€‹

Confidence: HIGH
Detection: Aurora cluster running an end-of-life engine version under Extended Support
Savings: Per-vCPU hourly surcharge (Year 1-2: $0.100/vCPU-hr, Year 3+: $0.200/vCPU-hr)

How it works:

  1. We check the Aurora engine version against AWS's release calendars: Aurora PostgreSQL 11 (billed from 2024-04-01), 12 (2025-03-01), 13 (2026-03-01) and 14 (2027-03-01); Aurora MySQL 2 / 5.7 (2024-12-01, year 3 from 2026-12-01) and 3 / 8.0 (2028-05-01, no year 3)
  2. If the version has passed its standard support end date, AWS charges Extended Support fees
  3. Surcharges are per-vCPU per hour, escalating after year 2
  4. We also raise a warning (no surcharge yet) for clusters 90 days or fewer before Extended Support charges begin; a surcharge finding needs an estimated surcharge of at least $0.01 a month

Example:

Cluster: pg12-eol-cluster
Engine: aurora-postgresql 12.18
EOL Date: 2025-02-28
Extended Support: Year 1 rate
Instances: 2ร— db.r6g.xlarge (4 vCPUs each)
Surcharge: 8 vCPUs ร— $0.100/hr ร— 730 hrs = $584.00/month

AWS Reference: Aurora Extended Support Pricing

Why HIGH confidence: EOL dates and surcharge rates are published by AWS. vCPU counts are deterministic per instance class.


Aurora Serverless v2 Opportunityโ€‹

Confidence: MEDIUM
Detection: Low-utilization provisioned Aurora cluster suitable for Serverless v2 migration
Savings: Difference between provisioned instance cost and estimated Serverless v2 cost at 2 ACU baseline

How it works:

  1. We check the writer instance's CloudWatch CPU metrics: average at most 15% and max at most 40% over 14 days, with hourly readings for at least 75% of the window
  2. Excludes clusters already running Serverless, Global databases, and multi-writer setups
  3. Estimates Serverless v2 cost at 2 ACU baseline ร— $0.12/ACU-hr
  4. Compares against current provisioned instance cost; the saving must be positive and at least $0.01 a month

Example:

Cluster: low-traffic-aurora
Engine: aurora-postgresql
Instance: db.r6g.xlarge
CPU Avg: 8%, CPU Max: 22%
Provisioned Cost: $526.00/month
Estimated Serverless Cost: $175.20/month
Monthly Savings: $350.80

AWS Reference: Aurora Serverless v2 Pricing

Why MEDIUM confidence: Serverless v2 costs depend on actual ACU consumption which varies. The 2-ACU baseline is a conservative estimate. Workload spikes may increase actual Serverless costs.


DynamoDB Without Auto-Scalingโ€‹

Confidence: MEDIUM
Detection: Provisioned capacity DynamoDB table with no auto-scaling configured, whose provisioned capacity costs at least $5 a month
Savings: Varies based on provisioned capacity and utilization

How it works:

  1. We identify DynamoDB tables using provisioned capacity mode
  2. Check if Application Auto Scaling is configured for the table
  3. Tables without auto-scaling may be over-provisioned or at risk of throttling

Example:

Table: my-provisioned-table
Mode: Provisioned (10 RCU, 10 WCU)
Auto-Scaling: Not configured
Monthly Cost: $5.69 (10 ร— $0.00013 + 10 ร— $0.00065) ร— 730 hours
Recommendation: Enable auto-scaling or switch to on-demand

Read and write capacity are priced separately: a provisioned RCU is $0.00013/hour and a WCU is $0.00065/hour in us-east-1 (rates scale by region).

AWS Reference: DynamoDB Pricing

Why MEDIUM confidence: Whether auto-scaling is configured is binary, but whether it is worth enabling depends on your traffic pattern. Review before changing capacity settings.


Idle DynamoDB Tableโ€‹

Confidence: HIGH
Detection: Zero consumed read/write capacity for 14 days (7 in an Air-Gapped export), on a table at least 14 days old
Savings: Full table cost (provisioned) or minimum charges (on-demand)

How it works:

  1. We query CloudWatch for ConsumedReadCapacityUnits and ConsumedWriteCapacityUnits
  2. Tables with zero consumption for 14 days are flagged
  3. Provisioned tables charge continuously; on-demand has storage costs
note

Tables younger than the 14-day window are skipped. CloudWatch reports no datapoints for the period before a table existed, which is indistinguishable from a table that existed and sat idle โ€” so a table you created yesterday would otherwise be reported as having had "no activity in 14 days". You will not see this finding for a new table until it has been quiet for a full 14 days.

Example:

Table: my-old-table
Mode: Provisioned (5 RCU, 5 WCU)
Activity (14 days): 0 operations
Monthly Savings: $2.85 (5 ร— $0.00013 + 5 ร— $0.00065) ร— 730 hours

AWS Reference: DynamoDB Pricing

Why HIGH confidence: Zero consumed capacity means no reads or writes. Table is unused.


Over-Provisioned DynamoDBโ€‹

Confidence: MEDIUM
Detection: Read and write capacity utilization both < 20% for 14 days, on a table at least 14 days old
Savings: Based on right-sized capacity

How it works:

  1. We total ConsumedReadCapacityUnits / ConsumedWriteCapacityUnits from CloudWatch over the 14-day window and divide by the window length in seconds. That yields average consumed units per second โ€” the same unit provisioned RCU/WCU is defined in, so the two are directly comparable.
  2. Tables using less than 20% of provisioned read and write capacity are over-provisioned
  3. Right-sizing or switching to on-demand can reduce costs
note

As with idle tables, this finding is skipped for tables younger than 14 days. The utilization average divides by the full window regardless of how long the table has existed, so a brand-new table โ€” even a busy one โ€” would read as under-utilized.

note

Capacity units are a per-second rate throughout. CloudWatch's Average statistic on these metrics is units per request, not per second, and is not used here.

Example:

Table: my-app-data
Provisioned: 100 RCU, 100 WCU
Used (avg): 15 RCU/sec, 8 WCU/sec
Utilization: 15%, 8%
Provisioned cost: $56.94/month
On-demand estimate: $17.82/month (read $0.125/M RRU, write $0.625/M WRU)
Monthly Savings: ~$39.12

AWS Reference: DynamoDB Pricing

Why MEDIUM confidence: Traffic patterns may spike periodically. Review before right-sizing.


Idle ElastiCache Clusterโ€‹

Confidence: HIGH
Detection: Zero connections for 7 days
Savings: Full cluster cost

How it works:

  1. We query CloudWatch for CurrConnections metric
  2. Clusters with zero connections for 7 days are flagged
  3. ElastiCache charges continuously for node hours

Example:

Cluster: my-redis-cluster
Node Type: cache.t3.micro
Nodes: 2
Connections (7 days): 0
Monthly Savings: $24.82

AWS Reference: ElastiCache Pricing

Why HIGH confidence: No client connections means no application is using this cache.


Idle Redshift Clusterโ€‹

Confidence: HIGH Detection: Zero database connections for 7 days, on a cluster at least that old, costing at least $0.01 a month Savings: Full cluster cost

How it works:

  1. We read the cluster's DatabaseConnections from CloudWatch
  2. Clusters with zero connections for 7 days are flagged; the series must cover most of the window, so missing data is never read as idle
  3. A cluster younger than 7 days is not flagged, because it cannot show a full window of inactivity
  4. Redshift charges continuously for cluster nodes

Example:

Cluster: my-data-warehouse
Node Type: dc2.large
Nodes: 2
Connections (7 days): 0
Monthly Savings: $360.00

AWS Reference: Redshift Pricing

Why HIGH confidence: Zero queries means no one is using this data warehouse.


Idle OpenSearch Domainโ€‹

Confidence: MEDIUM Detection: No client requests and no indexing on any of 7 days Savings: Full domain cost

How it works:

  1. We query CloudWatch for OpenSearchRequests, InvalidHostHeaderRequests, IndexingRate, SearchRate, ClusterStatus.red, ClusterUsedSpace and FreeStorageSpace. Client requests are OpenSearchRequests minus InvalidHostHeaderRequests (internet scanners on a public endpoint do not count) and must be exactly 0 on every day of the 7-day window
  2. IndexingRate must be measured and average 0: a log or observability sink that indexes but is rarely searched is not idle
  3. SearchRate must stay at or below 100 searches per data node per day (the service's own background activity), ClusterStatus.red must be 0 on every day, and ClusterUsedSpace must be at most 10 MiB
  4. The busiest node's used disk (EBS volume minus the lowest FreeStorageSpace) must stay within what AWS itself reserves: 5% of the volume for the Linux root reserve plus the lesser of 20% of the volume and 20,480 MiB for OpenSearch Service, plus an allowance of 4% of the volume and 1,024 MiB for filesystem overhead. More than that suggests data in closed indices
  5. UltraWarm and cold storage must be known to be off, and a live check must find no cross-cluster connection
  6. Every series must cover every day of the window, and every measurement must be present: a domain with missing data is reported as missing data in the scan's warnings, never as idle. The domain must also cost at least $0.01 a month
  7. OpenSearch charges continuously for instance hours
  8. Remediation is a reviewed plan, not one-click Fix This, until this check is re-validated in a live account

Example:

Domain: my-search-domain
Instance Type: t3.small.search
Instances: 2
Client requests (7 days): 0
Monthly Savings: $51.10

AWS Reference: OpenSearch Pricing

Why MEDIUM confidence: Zero client requests and no indexing are strong signals, but metrics cannot see every use of a domain. Review before deleting it.


Oversized OpenSearch Domainโ€‹

Confidence: MEDIUM
Detection: Average CPU < 20%, 95% of hours averaging under 40% and 95% of hourly peaks under 50% over 7 days, with active search traffic
Savings: Estimated from stepping down instance type (or 30% conservative fallback)

How it works:

  1. We collect hourly CloudWatch CPUUtilization (average and maximum) over 7 days
  2. Domains with sustained low CPU but non-zero search traffic are flagged. A single spike (a restart, a reindex, a batch job) does not block the finding: we gate on the 95th percentile of the hourly readings, not the highest one. At least 75% of the window's hours must have a reading
  3. A step-down instance type map estimates savings from rightsizing
  4. If the instance type isn't in the step-down map, a conservative 30% savings is estimated
  5. The domain's monthly cost and the estimated savings must each be at least $0.01

Example:

Domain: oversized-search
Instance Type: r6g.xlarge.search
Instances: 3
Avg CPU: 12.0% Max CPU: 30.0%
Recommendation: Downsize to r6g.large.search
Monthly Savings: $368.00

AWS Reference: OpenSearch Pricing

Why MEDIUM confidence: Low CPU is a strong signal but workloads may have periodic spikes. Resize one dimension at a time and observe.


OpenSearch EBS Overprovisionedโ€‹

Confidence: MEDIUM
Detection: Free storage > 60% with data growth < 0.1 GB/day (EBS-backed domains only)
Savings: Reclaimable GB ร— ~$0.10/GB/month (gp3 approximate rate)

How it works:

  1. We query CloudWatch FreeStorageSpace (the average data node's free space) and compare it with one node's EBS volume to compute the percentage of unused EBS storage
  2. We calculate the storage growth rate from a 14-day trend
  3. Domains with >60% free storage and flat growth (<0.1 GB/day) are flagged
  4. We keep 30% headroom and estimate reclaimable GB; the estimated saving must be at least $0.01 a month

Example:

Domain: logs-archive
EBS: gp3, 500 GB ร— 2 nodes (1000 GB total)
Free Storage: 75%
Growth: 0.02 GB/day
Reclaimable: ~450 GB
Monthly Savings: $45.00

AWS Reference: OpenSearch Pricing

Why MEDIUM confidence: Storage trends may change. Verify index lifecycle policies and retention needs before resizing.


RI Opportunity: OpenSearchโ€‹

Confidence: HIGH
Detection: On-demand OpenSearch domain eligible for Reserved Instance purchase
Savings: Estimated annual savings from Cost Explorer RI recommendations

How it works:

  1. We query Cost Explorer for OpenSearch RI purchase recommendations
  2. Domains running on-demand that would benefit from a 1-year partial-upfront RI are flagged
  3. Savings estimates come directly from AWS Cost Explorer

Example:

Domain: production-search
Instance Type: r6g.2xlarge.search
Current: On-Demand
Recommendation: 1-year partial-upfront RI
Estimated Annual Savings: $4,800

AWS Reference: OpenSearch Reserved Instances

Why HIGH confidence: Uses AWS's own Cost Explorer recommendation. RI commitments are non-refundable, so verify workload stability before purchasing.


Idle Neptune Clusterโ€‹

Confidence: HIGH
Detection: Zero Gremlin and SPARQL requests for 7 days
Savings: Full cluster cost

How it works:

  1. We query CloudWatch for Neptune request metrics
  2. Clusters with zero requests for 7 days are flagged, provided the cluster is at least 7 days old (a younger cluster, or one recreated under a reused identifier, is not called idle on partial data)
  3. Neptune charges continuously for instance hours

Example:

Cluster: my-graph-database
Instance Type: db.t3.medium
Requests (7 days): 0
Monthly Savings: $59.86

AWS Reference: Neptune Pricing

Why HIGH confidence: Zero requests means no applications are using this graph database.


Neptune Serverless Migration Opportunityโ€‹

Confidence: HIGH
Detection: Provisioned Neptune cluster with consistently low CPU utilization
Savings: 50โ€“65% for low-utilization clusters

How it works:

  1. We identify provisioned Neptune clusters (not already Serverless)
  2. Hourly CloudWatch CPUUtilization is analyzed over 14 days; at least 75% of the window's hours must have a reading
  3. Clusters with <20% avg CPU, 95% of hourly averages under 40% and 95% of hourly maximums under 50% are flagged (one spike does not veto); clusters younger than 14 days are skipped
  4. Serverless is estimated per instance at the larger of 2 NCU and the NCUs its busy hours use (memory-equivalent NCUs ร— p95 CPU), at $0.1608/NCU-hr
  5. Only flags clusters whose provisioned cost exceeds that estimate by at least $0.01 a month (a db.r6g.large at $228.49/mo does not: 2 NCU is $234.77/mo)

Example:

Cluster: dev-graph-database
Instance Type: db.r6g.2xlarge
CPU Average (window): 3.1%
CPU p95 (window): 4.0%
Provisioned Cost: $913.96/month
Estimated Serverless Cost: $234.77/month (2 NCU)
Monthly Savings: $679.19

Recommended Action: Create a new Neptune Serverless cluster, restore from snapshot, and update application endpoints. Verify your query language โ€” Serverless supports Gremlin and openCypher but NOT SPARQL.

AWS Reference: Neptune Serverless

Why HIGH confidence: CPU utilization and instance pricing are well-defined. The 2-NCU-per-instance planning floor ($234.77/mo) is a conservative comparison point: a graph database's warm buffer cache rarely lets Serverless sit at its 1-NCU minimum.


Over-Provisioned Neptune Instanceโ€‹

Confidence: MEDIUM
Detection: Neptune instance with consistently low CPU utilization
Savings: 30โ€“50% per right-sizing step

How it works:

  1. We analyze CloudWatch CPUUtilization for each provisioned Neptune cluster
  2. Clusters with <20% avg CPU, 95% of hours averaging <40% and 95% of hourly peaks <50% are flagged. A single spike (a restart, maintenance, a batch job) does not block the finding. At least 75% of the 14 days' hours must have a reading, and the cluster must be at least 14 days old
  3. Each over-provisioned instance is flagged with a target smaller instance class
  4. Savings calculated from published per-hour pricing difference; an instance is flagged only when the saving is at least $0.01 a month

Example:

Instance: graph-writer-1 (db.r6g.xlarge)
Cluster: prod-graph
CPU Average (window): 12.1%
CPU Maximum (window): 22.5%
Current Cost: $456.98/month
Target: db.r6g.large ($228.49/month)
Monthly Savings: $228.49

Recommended Action: Modify the instance class via ModifyDBInstance. Brief downtime during change (5โ€“15 minutes). Start with reader instances to minimize impact.

Agentic Tier โ€” Automated Fix Available

Click Fix This to downsize the instance with automatic backup snapshot and one-click rollback.

AWS Reference: Neptune Pricing

Why MEDIUM confidence: CPU is a good proxy but doesn't capture memory-bound graph traversals. Some workloads burst memory without high CPU. Validate with monitoring after downsizing.


Old Neptune Cluster Snapshotโ€‹

Confidence: HIGH
Detection: Manual Neptune cluster snapshot at least 90 days old
Savings: $0.021/GB-month per snapshot

How it works:

  1. We query Neptune for manual cluster snapshots (DescribeDBClusterSnapshots)
  2. Snapshots at least 90 days old are flagged, provided their storage costs at least $0.01 a month
  3. Only manual snapshots โ€” automated backups are managed by retention policy

Example:

Snapshot: pre-upgrade-snap-2025-06
Cluster: my-graph-db
Age: 280 days
Storage: 100 GB
Monthly Cost: $2.10

Recommended Action: Delete the snapshot if the data is no longer needed. Verify it's not a critical disaster recovery backup.

Agentic Tier โ€” Automated Fix Available

Click Fix This to delete the old snapshot. Note: snapshot deletion is irreversible.

AWS Reference: Neptune Pricing โ€” Backup Storage

Why HIGH confidence: Snapshot age is deterministic. Manual snapshots are created for specific events and rarely needed after 90 days.


Idle DocumentDB Clusterโ€‹

Confidence: HIGH
Detection: Zero connections and combined read+write IOPS under 10 per second for 7 days
Savings: Full cluster cost

How it works:

  1. We query CloudWatch for DocumentDB connection and I/O metrics
  2. Clusters with zero connections and combined read+write IOPS averaging under 10 per second for 7 days are flagged. The connection series must cover at least 75% of the window's hours; a missing series is reported as missing data, never as idle
  3. The cluster must be at least 7 days old, so a recreated cluster is not judged on partial data
  4. DocumentDB charges continuously for instance hours

Example:

Cluster: my-docdb-cluster
Instance Type: db.t3.medium
Connections (7 days): 0
Monthly Savings: $59.86

AWS Reference: DocumentDB Pricing

Why HIGH confidence: Zero connections means no applications are connected to this database.


Old DocumentDB Snapshotโ€‹

Confidence: MEDIUM
Detection: Manual snapshot at least 90 days old
Savings: ~$0.02/GB-month backup storage

How it works:

  1. Lists all manual DocumentDB cluster snapshots
  2. Groups snapshots by source cluster
  3. Protects the newest manual snapshot per cluster (disaster-recovery safety net)
  4. Skips snapshots tagged with retention, compliance, legal, or do-not-delete
  5. Flags remaining snapshots that are at least 90 days old

Example:

Snapshot: my-docdb-cluster-backup-2024-01
Cluster: my-docdb-cluster
Age: 180 days (threshold: 90)
Storage: 50 GB
Monthly Savings: $1.00

AWS Reference: DocumentDB Pricing

Why MEDIUM confidence: Old snapshots probably aren't needed, but deletion is irreversible. Some may be kept for compliance.


Overprovisioned DocumentDB Clusterโ€‹

Confidence: MEDIUM
Detection: Hourly CPU average < 20%, 95% of hours averaging < 40% and 95% of hourly peaks < 50%, average connections < 50 and combined read+write IOPS < 100 per second over 7 days
Savings: ~30% of cluster cost (instance downsizing)

How it works:

  1. Fetches CloudWatch metrics for each DocumentDB cluster over 7 days; hourly CPU readings must cover at least 75% of the window's hours
  2. Checks four signals: average CPU, the 95th percentile of hourly averages and of hourly peaks (a single spike does not block the finding), connection count, and combined read+write IOPS. A cluster that looks idle is not also called overprovisioned
  3. If all metrics are below thresholds and the cluster is at least 7 days old, it is a rightsizing candidate
  4. Advisory-only in v1 โ€” no automated remediation actions are generated

Example:

Cluster: my-docdb-cluster
Instance Type: db.r5.2xlarge (2 instances)
Avg CPU: 8.2%
Max CPU: 15.0%
Connections: 12
IOPS: 30 (read: 20, write: 10)
Monthly Savings: $219.00 (30% of cluster cost)

AWS Reference: DocumentDB Pricing

Why MEDIUM confidence: Multi-signal analysis shows consistent under-utilization, but workload patterns may be bursty. Snapshot the cluster before resizing.


Aurora to RDS Downgrade Opportunityโ€‹

Confidence: MEDIUM
Detection: Aurora Standard cluster with at most 1 reader instance, writer average CPU at most 20%, average connections at most 100, and I/O charges at least 25% of total Aurora spend over 30 days
Savings: 20โ€“40% by switching to RDS for the same engine

How it works:

  1. We identify Aurora Standard clusters (not I/O-Optimized, Serverless v1 or v2, or a Global secondary) with a writer and at most 1 reader
  2. The writer must average at most 20% CPU (with hourly readings for at least 75% of the window) and at most 100 connections over 30 days, and the cluster must hold at most 64 TiB
  3. Chatty, low-compute workloads whose per-I/O charges are at least 25% of the Aurora bill don't benefit from Aurora's architecture: RDS gp3 includes baseline IOPS at no extra charge
  4. We flag the cluster when the RDS estimate saves money and the saving is at least $0.01 a month

Example:

Cluster: my-app-aurora
Engine: aurora-mysql
Instances: 1 writer
Writer CPU: 9%, connections: 12
I/O charges: 41% of spend
Monthly Savings: $85.00 (switch to RDS MySQL)

The RDS side is priced at RDS gp3 rates: $0.115/GB-month storage and $0.02 per provisioned IOPS-month above the 3,000 IOPS baseline (Single-AZ; doubled for a cluster with a reader, which maps to Multi-AZ).

AWS Reference: Aurora Pricing vs RDS

Why MEDIUM confidence: Aurora provides higher availability and faster failover. Verify that the application doesn't rely on Aurora-specific features.


Oversized ElastiCache Clusterโ€‹

Confidence: MEDIUM
Detection: ElastiCache cluster CPU utilization < 10% over 7 days with active connections
Savings: 40โ€“50% from downsizing to smaller node type

How it works:

  1. We analyze ElastiCache CPU utilization and connection metrics via CloudWatch
  2. Clusters with consistently low CPU (< 10%) despite having connections are oversized
  3. Downsizing to a smaller node type reduces cost proportionally

Example:

Cluster: session-cache
Node Type: cache.r6g.xlarge
Nodes: 2
Avg CPU: 6% over 7 days
Recommended: cache.r6g.large
Monthly Savings: $198.00

AWS Reference: ElastiCache Pricing

Why MEDIUM confidence: Cache performance is sensitive to memory capacity. Verify hit rates after downsizing.


Oversized Redshift Clusterโ€‹

Confidence: MEDIUM Detection: Average CPU utilization below 15% on an ra3.16xlarge, ra3.4xlarge or dc2.8xlarge cluster, with at least $0.01 a month saved by the downsize Savings: Current cost minus the cost of the same node count on the next smaller node type

How it works:

  1. We read Redshift CPU utilization from CloudWatch over the metrics window; clusters with zero connections are left to idle_redshift
  2. Clusters below 15% average CPU are matched to the next smaller type (ra3.16xlarge to ra3.4xlarge, ra3.4xlarge to ra3.xlplus, dc2.8xlarge to dc2.large); other node types are skipped
  3. A DC2 downsize also needs the disk use measured and the data projected to fit the smaller node at no more than 70% disk, because DC2 storage is on the node; unknown disk use withholds the finding. RA3 keeps data in managed storage, so it has no disk gate

Example:

Cluster: analytics-dw
Node Type: ra3.4xlarge
Nodes: 2
Avg CPU: 9%
Recommended: ra3.xlplus (2 nodes)
Monthly Savings: $3,174.04

AWS Reference: Redshift Pricing

Why MEDIUM confidence: Query workloads can be bursty. Analyze query queuing times before resizing.


Underutilized Redshift Clusterโ€‹

Confidence: MEDIUM Detection: Average CPU above zero and below 10% with non-zero connections, on a cluster costing at least $0.01 a month Savings: An estimated 30% of cluster cost from downsizing

How it works:

  1. We read Redshift CPU utilization and database connections from CloudWatch over the metrics window
  2. Clusters with zero connections are left to idle_redshift; a CPU average of 0 is treated as no data and skipped
  3. A cluster below the CPU threshold is flagged as over-provisioned and a smaller node type or fewer nodes is suggested

Example:

Cluster: reporting-dw
Node Type: ra3.xlplus
Nodes: 3
Avg CPU: 8%
Monthly Savings: $713.00

AWS Reference: Redshift Pricing

Why MEDIUM confidence: Distinguishes from oversized_redshift by incorporating query volume. Still requires workload analysis.


Redshift Without Pause Scheduleโ€‹

Confidence: MEDIUM Detection: RA3 or DC2 cluster with no pause schedule that is not already paused, with zero connections in at least 40% of hours, costing at least $0.01 a month Savings: Cluster cost multiplied by the zero-connection share of hours

How it works:

  1. We check each RA3 or DC2 cluster (the families that support pause and resume) for a scheduled pause action
  2. We measure the share of hours with zero connections; clusters at or above 40% are flagged
  3. A cluster that is fully idle is only flagged here once it is at least 7 days old and has enough connection data; otherwise it is left to idle_redshift

Example:

Cluster: analytics-dw
Nodes: 4
Scheduled Actions: None
Zero-connection hours: 50%
Monthly Savings: $730.00

AWS Reference: Redshift Pause and Resume

Why MEDIUM confidence: Some ETL jobs run overnight. Verify usage patterns before enabling pause schedules.


Redshift Legacy DC2 Nodesโ€‹

Confidence: LOW Detection: Redshift cluster on a dc2.* node type, costing at least $0.01 a month Savings: An estimated 20% of cluster cost from migration to RA3 with managed storage

How it works:

  1. We check the node type of Redshift clusters
  2. DC2 nodes use local SSD storage and are a legacy architecture
  3. RA3 nodes offer managed storage (pay separately) and are typically cheaper overall; we suggest ra3.xlplus for dc2.large and ra3.4xlarge for other DC2 types

Example:

Cluster: legacy-dw
Node Type: dc2.large
Nodes: 4
Monthly Savings: $146.00 (migration to ra3.xlplus)

AWS Reference: Redshift Node Types

Why LOW confidence: Migration requires cluster resize and potential query plan changes. Test thoroughly.


Redshift Concurrency Scaling Wasteโ€‹

Confidence: MEDIUM Detection: Concurrency scaling beyond the free 3,600 seconds a day, with an estimated monthly scaling cost of at least $0.01 Savings: An estimated 60% of the concurrency scaling cost, from WLM tuning

How it works:

  1. We read total concurrency scaling seconds from CloudWatch for the metrics window; clusters with zero connections are left to idle_redshift
  2. We subtract the free credit (3,600 seconds for each day of the window); nothing is flagged while usage stays inside it
  3. The billable remainder is priced at the cluster's per-second node rate and extrapolated to a month

Example:

Cluster: analytics-dw
Concurrency Scaling: 3 hours/day average
Monthly Savings: $120.00

AWS Reference: Redshift Concurrency Scaling

Why MEDIUM confidence: Concurrency scaling is billed per-second. Savings depend on workload patterns.


Redshift WLM Over-Provisionedโ€‹

Confidence: MEDIUM Detection: Cluster-wide WLM queue near-empty (average queue length at most 0.5, average wait at most 1 second) and average running queries below 50% of 5 assumed WLM slots, on a cluster costing at least $0.01 a month Savings: An estimated 40% of cluster cost from halving the node count

How it works:

  1. We read the cluster's WLM queue length, queue wait and running-query metrics from CloudWatch; if the WLM read fails the check is withheld, and clusters with zero connections are left to idle_redshift
  2. The slot count is the Redshift default for automatic WLM (5), not read from the cluster's configuration
  3. A cluster that queues nothing and runs fewer queries than half the slots has more parallel capacity than it needs, so we suggest half the nodes

Example:

Cluster: analytics-dw
Nodes: 4
Avg queue length: 0.1, avg wait: 0.2s
Avg running queries: 1.0 (20% of 5 slots)
Recommended: 2 nodes
Monthly Savings: $438.00

AWS Reference: Redshift WLM Configuration

Why MEDIUM confidence: WLM tuning affects query queuing behavior. Test with representative workloads.


RI Opportunity: ElastiCacheโ€‹

Confidence: HIGH
Detection: AWS Cost Explorer recommendation for ElastiCache Reserved Instance
Savings: 40โ€“55% discount vs On-Demand (exact AWS calculation)

How it works:

  1. AWS Cost Explorer analyzes 30 days of ElastiCache usage
  2. Identifies consistent node usage suitable for RI commitment
  3. Returns exact savings calculation for 1-year or 3-year terms

Example:

Node Type: cache.r6g.large
Current: On-Demand ($0.252/hr)
RI (1yr, No Upfront): $0.155/hr
Monthly Savings: $70.81

AWS Reference: ElastiCache Reserved Nodes

Why HIGH confidence: Uses AWS's own Cost Explorer analysis with exact pricing.


RI Opportunity: Redshiftโ€‹

Confidence: HIGH
Detection: AWS Cost Explorer recommendation for Redshift Reserved Instance
Savings: 25โ€“40% discount vs On-Demand (exact AWS calculation)

How it works:

  1. AWS Cost Explorer analyzes 30 days of Redshift cluster usage
  2. Identifies consistent node usage suitable for RI commitment
  3. Returns exact savings calculation for 1-year or 3-year terms

Example:

Node Type: ra3.xlplus
Nodes: 4
Current: On-Demand ($1.086/hr)
RI (1yr): $0.750/hr
Monthly Savings: $980.00

AWS Reference: Redshift Reserved Nodes

Why HIGH confidence: Uses AWS's own Cost Explorer analysis with exact pricing.


RDS Extended Support Costโ€‹

Confidence: HIGH
Detection: RDS engine version in or approaching extended support window
Savings: vCPU-based surcharge ($0.10โ€“$0.20 per vCPU-hour ร— 730 hours)

How it works:

  1. We check RDS engine versions against AWS lifecycle policy
  2. Versions past standard support incur per-vCPU extended support fees
  3. Warning is raised 90 days before a version enters extended support

Example:

Instance: mydb-production
Engine: mysql 5.7
Status: Extended support (billed since 2024-03-01; year 3 since 2026-03-01)
vCPUs: 4
Monthly Surcharge: 4 vCPUs ร— $0.200/hr ร— 730 hrs = $584.00
Action: Upgrade to MySQL 8.4 (5.7 โ†’ 8.0 โ†’ 8.4). MySQL 8.0 is itself in
Extended Support since 2026-08-01, so stopping at 8.0 still pays.

AWS Reference: RDS Extended Support Pricing

Why HIGH confidence: Engine version and vCPU count are exact metadata. Surcharge rates are published by AWS.


DocumentDB Extended Support Costโ€‹

Confidence: MEDIUM
Detection: DocumentDB cluster version in extended support
Savings: 80% premium on the instance cost (160% in year 3)

How it works:

  1. We check DocumentDB cluster versions against the AWS lifecycle policy. Only 3.6 has Extended Support: it is billed from July 1, 2026 (year 3 from March 31, 2028). 4.0, 5.0 and 8.0 have no end-of-standard-support date.
  2. Versions past standard support incur extended support surcharges; a warning is raised 90 days before a version enters Extended Support, and a surcharge finding needs an estimated surcharge of at least $0.01 a month
  3. Upgrading to a supported version eliminates the surcharge

Example:

Cluster: my-docdb
Engine: docdb 3.6
Status: Extended support
Monthly Surcharge: $324.12 (80% of $405.15)
Action: Upgrade to DocumentDB 5.0

AWS Reference: DocumentDB Pricing

Why MEDIUM confidence: Extended support surcharge rate varies by engine version and instance type.


ElastiCache Extended Support Costโ€‹

Confidence: MEDIUM
Detection: ElastiCache engine version in extended support
Savings: 80% premium on the node's on-demand cost (160% in year 3)

How it works:

  1. We check Redis OSS versions against the AWS lifecycle policy: Redis OSS 4 and 5 are billed from February 1, 2026, Redis OSS 6 from February 1, 2027. Valkey and Memcached have no Extended Support and are never flagged.
  2. End-of-life versions incur extended support surcharges per node; a warning is raised 90 days before a version enters Extended Support
  3. Upgrading to a current version eliminates the surcharge

Example:

Cluster: session-cache
Engine: Redis 5.0.6 (extended support)
Nodes: 3
Monthly Surcharge: $262.80 (80% of $328.50)
Action: Upgrade to Redis 7.x

AWS Reference: ElastiCache Pricing

Why MEDIUM confidence: Surcharge rates depend on version and node type. AWS publishes specific rates.


ElastiCache Replication Waste (Non-Production)โ€‹

Confidence: HIGH
Detection: Non-production cluster with unnecessary replicas
Savings: Full replica node cost (doubles or triples cluster price)

How it works:

  1. We check ElastiCache replication groups for replica configuration
  2. Clusters tagged with non-production environment names (dev, staging, test, sandbox) are flagged if they have replicas
  3. Each replica is a full node at the same hourly rate as the primary

Example:

Replication Group: dev-session-cache
Environment: dev (tag)
Node Type: cache.r6g.large
Shards: 1, Replicas per Shard: 2
Replica Cost: 2 ร— $0.164/hr ร— 730 hrs = $239.44/month
Monthly Savings: $239.44

AWS Reference: ElastiCache Pricing

Why HIGH confidence: Non-production environments don't need high availability replicas. The environment tag clearly identifies the cluster as non-production.


ElastiCache Valkey Migration Savingsโ€‹

Confidence: HIGH
Detection: Redis OSS or Memcached cluster eligible for Valkey migration
Savings: 20% of current cluster cost (node-based) or 33% (Serverless)

How it works:

  1. We check the engine type of each ElastiCache cluster
  2. Redis OSS and Memcached clusters are flagged as migration candidates
  3. Valkey is priced lower than Redis OSS for node-based and for Serverless clusters (see the ElastiCache pricing page), with Redis 7.x API compatibility

Example:

Cluster: session-cache
Engine: Redis OSS 7.0.7
Node Type: cache.r7g.xlarge ร— 2 nodes
Current Cost: Redis OSS node price ร— 2 nodes ร— 730 hours
Valkey Cost: Valkey node price ร— 2 nodes ร— 730 hours
Monthly Savings: the difference

AWS Reference: ElastiCache Pricing

Why HIGH confidence: AWS prices Valkey below Redis OSS. Valkey is API-compatible with Redis 7.x. Existing Redis OSS reservations apply to Valkey.


ElastiCache Serverless Optimizationโ€‹

Confidence: MEDIUM
Detection: Node-based cluster with spiky traffic: average CPU under 15%, peak CPU above 60%, and daily connections varying with a coefficient of variation above 2
Savings: Varies (eliminates over-provisioned capacity during low-traffic periods)

How it works:

  1. We analyze CPU utilization and connection metrics via CloudWatch over 30 days
  2. Clusters with high traffic variance (low average, high peaks) are candidates
  3. The Serverless cost is estimated from measured data: BytesUsedForCache (at least the engine's billed minimum) and the node's request volume (ProcessedCommands, or GetTypeCmds + SetTypeCmds, and network bytes: one ECPU per command or per KB transferred, whichever is larger), at the engine's own Serverless rates (Redis OSS $0.125/GB-hr and $0.0034 per million ECPUs; Valkey $0.084/GB-hr and $0.0023 per million)
  4. A cluster whose data size or request volume cannot be measured gets no estimate and no finding (noted in the scan's warnings). Online Mode only in practice: the Air-Gapped export carries no command counts

Example:

Cluster: api-cache (Redis OSS)
Node Type: cache.r6g.xlarge ร— 2 nodes
Avg CPU: 8.5% (over-provisioned)
Peak CPU: 72% (needs capacity for bursts)
Connection CV: 3.2 (highly variable)
Data: 2.0 GB; requests: 3.0 billion over 30 days
Current Cost: $478.88/month
Estimated Serverless: $192.84/month ($182.50 data + $10.34 ECPU)
Monthly Savings: ~$286.04

AWS Reference: ElastiCache Serverless Pricing

Why MEDIUM confidence: The estimate is built from the node's measured data size and request volume, but per-request ECPU also depends on command complexity, and traffic can change. Not available for Memcached.


ElastiCache Data Tiering Opportunityโ€‹

Confidence: MEDIUM
Detection: Memory-only cluster eligible for R6gd data tiering
Savings: Up to 52% for large datasets (fewer nodes needed)

How it works:

  1. We identify clusters using memory-optimized R5/R6g/R7g nodes at xlarge size or larger
  2. R6gd nodes combine memory + NVMe SSD with ~5ร— total storage capacity
  3. Multi-node clusters can often consolidate to fewer R6gd nodes

Example:

Cluster: large-dataset-cache
Current: 4 ร— cache.r6g.16xlarge ($5.254/hr each)
Current Cost: $15,342/month
With Data Tiering: 1 ร— cache.r6gd.16xlarge ($9.98/hr)
Data Tiering Cost: $7,285/month
Monthly Savings: $8,057 (52%)

AWS Reference: ElastiCache Data Tiering

Why MEDIUM confidence: Savings depend on data access patterns. Best for workloads where only a small share of the data is accessed frequently (CloudWise does not measure access patterns). SSD-resident data has slightly higher first-access latency. Not available with Serverless.


OpenSearch Extended Support Costโ€‹

Confidence: MEDIUM
Detection: OpenSearch/Elasticsearch version in extended support
Savings: $0.0065 per Normalized Instance Hour; 100% of the instance cost from November 7, 2026 for versions whose extension AWS has already lengthened

How it works:

  1. We check OpenSearch domain engine versions against the AWS end-of-support schedule
  2. Elasticsearch 1.5, 2.3, 5.1-5.6, 6.0-6.7, 7.1-7.8 and OpenSearch 1.0-1.2, 2.3-2.9 are billed now. Elasticsearch 6.8, 7.9, 7.10 and OpenSearch 1.3, 2.11-2.19 stay in standard support until November 7, 2027
  3. A warning is raised 90 days before a version enters legacy support, and a surcharge finding needs an estimated surcharge of at least $0.01 a month
  4. Upgrading to a current OpenSearch version eliminates the surcharge

Example:

Domain: search-logs
Engine: Elasticsearch 5.6 (extended support)
Instances: 3 ร— r6g.large.search
Monthly Surcharge: $56.94 (3 ร— 4 NIH ร— $0.0065 ร— 730 h)
Action: Upgrade to OpenSearch 3.x

AWS Reference: OpenSearch Pricing

Why MEDIUM confidence: Extended support for OpenSearch is versioned and may vary by instance type.


Network Detectorsโ€‹

Unattached Elastic IPโ€‹

Confidence: HIGH
Detection: describe_addresses where no InstanceId or NetworkInterfaceId
Savings: the hourly price of a public IPv4 address ร— 730 hours (see the AWS VPC pricing page)

How it works:

  • AWS bills every public IPv4 address, attached or not, at an hourly price (see the AWS VPC pricing page).
  • An unattached EIP serves nothing for that price, so the saving is the whole charge
  • This is a fixed AWS price, no estimation

Example:

Elastic IP: 52.123.45.67
Attached: No
Monthly Savings: $3.65

AWS Reference: VPC Pricing (Public IPv4)

Why HIGH confidence: Binary detection (attached or not) with fixed AWS pricing.


Idle NAT Gatewayโ€‹

Confidence: HIGH
Detection: CloudWatch BytesOutToDestination = 0 for 7 days, on a gateway at least 7 days old
Savings: the NAT Gateway's base hourly charge ร— 730 hours (see the AWS VPC pricing page)

How it works:

  1. NAT Gateways have a fixed hourly charge (see the pricing page below)
  2. We check CloudWatch for any traffic over 7 days
  3. Zero traffic = wasting the base charge

Example:

NAT Gateway: nat-0abc123def456
Traffic (7 days): 0 bytes
Monthly Savings: the base charge

AWS Reference: VPC Pricing (NAT Gateway)

Why HIGH confidence: CloudWatch confirms zero usage. Fixed AWS pricing.


Idle Load Balancerโ€‹

Confidence: HIGH
Detection: No healthy targets via describe_target_health
Savings: ~$16.20/month (ALB/NLB base charge)

How it works:

  • ALB/NLB have a base charge of ~$0.0225/hour
  • If no healthy targets, the load balancer isn't serving traffic
  • You're paying for infrastructure that does nothing

Example:

Load Balancer: my-unused-alb
Healthy Targets: 0
Monthly Savings: $16.20

AWS Reference: Elastic Load Balancing Pricing

Why HIGH confidence: No healthy targets = definitely not serving requests.


Low-Traffic Load Balancerโ€‹

Confidence: MEDIUM
Detection: <100 requests in 14 days with healthy targets
Savings: ~$16.20/month (ALB/NLB base charge)

How it works:

  • Checks Application Load Balancers that have registered healthy targets
  • Queries CloudWatch RequestCount over 14 days
  • Flags if total requests < 100 (approximately <7/day)
  • Excludes LBs with zero targets (caught by idle_load_balancer)
  • Network Load Balancers are not judged: they publish no RequestCount. Each NLB with healthy targets is named in the scan's data warnings instead

Example:

Load Balancer: staging-api-alb (application)
Healthy Targets: 3
Requests (14 days): 42
Monthly Savings: $16.20

AWS Reference: Elastic Load Balancing Pricing

Why MEDIUM confidence: Low traffic may be intentional (internal health checks, canary endpoints). Review before deleting.


High LCU Cost ALBโ€‹

Confidence: LOW
Detection: Average ConsumedLCUs cost exceeds 2ร— ALB base fee
Savings: ~30% of estimated LCU overage (conservative)

How it works:

  • Queries CloudWatch ConsumedLCUs metric over 14 days
  • Calculates estimated monthly LCU cost: avg_lcus ร— $0.008 ร— 730 hours
  • Flags if LCU cost > 2ร— the load balancer's base cost
  • Suggests reviewing NLB migration or architecture changes

Example:

Load Balancer: prod-websocket-alb (application)
Average LCUs: 12.4
Estimated LCU Cost: $72.41/month
Base Fee: $16.43/month
Total: $88.84/month
Savings Potential: ~$21.72/month (30% of LCU cost)

AWS Reference: ALB Pricing โ€” LCU Details

Why LOW confidence: High LCU usage may be legitimate for the workload. This is an advisory detector โ€” the team should review whether the architecture is optimal.


Classic Load Balancer Migrationโ€‹

Confidence: LOW
Detection: Load balancer type is classic
Savings: ~$2.05/month base + consolidation savings

How it works:

  • Detects any Classic Load Balancer (CLB)
  • Recommends migration to ALB (HTTP/HTTPS) or NLB (TCP/TLS)
  • CLBs cost $0.025/hr ($18.25/mo) vs ALBs at $0.0225/hr ($16.43/mo)
  • Consolidating multiple CLBs into one ALB with routing rules saves significantly more

Example:

Load Balancer: legacy-web-clb (classic)
Scheme: internet-facing
Migration Target: ALB (for HTTP/HTTPS workloads)
Monthly Savings: $2.05 (base) + consolidation potential

AWS Reference: Classic Load Balancer Migration Guide

Why LOW confidence: Migration requires testing. This is a housekeeping recommendation, similar to deprecated runtime detection.


Unused CloudFront Distributionโ€‹

Confidence: LOW
Detection: Zero requests for 30 days
Savings: Minimal base cost, prevents future data transfer charges

How it works:

  1. We query CloudWatch for CloudFront request metrics
  2. Distributions with zero requests over the last 30 days are flagged
  3. CloudFront has minimal costs when idle, but active distributions incur data transfer fees

Example:

Distribution: E1ABCDEFGH2IJK
Origin: my-old-bucket.s3.amazonaws.com
Requests (30 days): 0
Monthly Savings: ~$0.01 (housekeeping)

AWS Reference: CloudFront Pricing

Why LOW confidence: Zero requests over the window means no recent traffic, but a distribution may be kept for seasonal use or failover. Confirm before deleting it.


Unused Route53 Hosted Zoneโ€‹

Confidence: MEDIUM
Detection: Hosted zone with only NS and SOA records (2 records or fewer)
Savings: $0.50/month per zone

How it works:

  1. We list all Route53 hosted zones
  2. Zones with only default NS and SOA records (no custom DNS records, 2 records or fewer) are flagged
  3. Empty zones still incur the $0.50/month hosting fee

Example:

Hosted Zone: old-domain.example.com
Records: 2 (NS, SOA only)
Monthly Savings: $0.50

AWS Reference: Route53 Pricing

Why MEDIUM confidence: Detection is binary: the zone has only NS and SOA records. Confirm no registrar or parent zone still delegates to it before deleting it.


Unused Global Acceleratorโ€‹

Confidence: HIGH
Detection: Deployed, enabled accelerator whose endpoint groups hold no endpoints
Savings: ~$25.55/month (accelerator + 2 IPv4 addresses)

How it works:

  1. We enumerate all deployed, enabled Global Accelerators and their listeners/endpoint groups
  2. Accelerators with no endpoints in any endpoint group are flagged
  3. Global Accelerator charges $0.025/hour plus IPv4 fees regardless of traffic

Example:

Accelerator: arn:aws:globalaccelerator::123456789:accelerator/abc123
Endpoints: 0
Monthly Savings: $25.55

AWS Reference: Global Accelerator Pricing

Why HIGH confidence: With no endpoints the accelerator cannot route any traffic.


Idle Global Acceleratorโ€‹

Confidence: HIGH
Detection: Deployed accelerator with endpoints but zero processed bytes for 30 days
Savings: ~$25.55/month (accelerator + 2 IPv4 addresses)

How it works:

  1. We enumerate all Global Accelerators and their listeners/endpoint groups
  2. For accelerators with endpoints, we check CloudWatch ProcessedBytesIn and ProcessedBytesOut
  3. Accelerators with zero traffic for 30 days despite having endpoints are flagged
  4. When CloudWatch publishes no traffic metric for the accelerator at all (AWS publishes none without traffic), it is flagged only if it is at least 14 days old, measured from the later of its creation and last modification, and ListMetrics lists no series for it. This path is MEDIUM confidence, and an accelerator whose endpoint groups are all dialled to zero (a standby) is skipped

Example:

Accelerator: arn:aws:globalaccelerator::123456789:accelerator/idle-abc
Endpoints: 3 (across 2 listeners)
ProcessedBytesIn (30d): 0
ProcessedBytesOut (30d): 0
Monthly Savings: $25.55

AWS Reference: Global Accelerator Pricing

Why HIGH confidence: Zero traffic with active endpoints indicates the accelerator is not used in any request path.


Disabled Global Acceleratorโ€‹

Confidence: HIGH
Detection: Accelerator with Enabled=false still incurring fixed hourly charges
Savings: ~$25.55/month (accelerator + 2 IPv4 addresses)

How it works:

  1. We check the Enabled flag on each deployed Global Accelerator
  2. Disabled accelerators continue to reserve static anycast IPs and incur hourly charges
  3. Accelerators that are disabled but not deleted are flagged

Example:

Accelerator: arn:aws:globalaccelerator::123456789:accelerator/disabled-xyz
Status: DEPLOYED
Enabled: false
IP Addresses: [75.2.x.x, 99.83.x.x]
Monthly Savings: $25.55

AWS Reference: Global Accelerator Pricing

Why HIGH confidence: A disabled accelerator cannot route traffic and serves no purpose while still incurring charges.


Orphaned DNS Recordโ€‹

Confidence: MEDIUM
Detection: Route 53 DNS record pointing to a resource that no longer exists
Savings: ~$0.50/month per stale hosted zone record (cleanup finding)

How it works:

  1. We scan Route 53 hosted zones for alias and CNAME records
  2. Target resources (EC2, ELB, CloudFront) are verified against current inventory
  3. Records pointing to deregistered or terminated resources are flagged

Example:

Hosted Zone: example.com
Record: api.example.com (CNAME)
Target: old-lb-123.us-east-1.elb.amazonaws.com (DELETED)
Action: Remove or update DNS record

AWS Reference: Route 53 Pricing

Why MEDIUM confidence: Orphaned DNS records may be waiting for a replacement resource. Verify with the application team. Also a security concern (dangling DNS can be hijacked).


Unused VPC Endpointโ€‹

Confidence: HIGH
Detection: VPC endpoint with zero data processed in 14 days, on an endpoint at least 14 days old
Savings: Interface endpoint โ€” $0.01/GB + $0.01/hour/AZ (~$7.30/month/AZ)

How it works:

  1. We check VPC endpoint data processing metrics over 14 days
  2. Interface endpoints with zero bytes processed are unused
  3. Gateway endpoints (S3, DynamoDB) are free โ€” only Interface endpoints incur charges

Example:

Endpoint: vpce-0abc123def456
Service: com.amazonaws.us-east-1.sqs
Type: Interface
AZs: 3
Data Processed (14d): 0 bytes
Monthly Savings: $21.90 ($7.30/AZ ร— 3)

AWS Reference: VPC Endpoint Pricing

Why HIGH confidence: Zero data processed definitively indicates no traffic through this endpoint.


Serverless Detectorsโ€‹

Unused Lambda Functionsโ€‹

Confidence: HIGH (detection), but LOW savings
Detection: CloudWatch Invocations = 0 for 30 days, on a function last changed at least 30 days ago
Savings: Minimal (~$0.01-$0.08/month for storage)

Important Note: Lambda is pay-per-use. Unused functions cost essentially $0 for execution. We flag these for cleanup purposes, not cost savings.

Example:

Function: my-old-function
Invocations (30 days): 0
Monthly Savings: $0.01 (storage only)
Purpose: Housekeeping

AWS Reference: Lambda Pricing

Why flagged: Unused functions clutter your account and may contain outdated code or security issues.


Deprecated Lambda Runtimeโ€‹

Confidence: LOW
Detection: Function running on a deprecated or end-of-life runtime
Savings: Minimal ($0.01 โ€” housekeeping flag)

Important Note: This is a compliance and security detector, not a cost-savings detector. Deprecated runtimes may lose security patches and eventually become unsupported.

Deprecated Runtimes Detected: every runtime past its deprecation date in AWS's Lambda runtimes table, including nodejs20.x (April 2026), ruby3.2 (March 2026), python3.9 (December 2025), nodejs18.x (September 2025), provided.al2 (July 2026), python3.8, nodejs16.x, dotnet6, java8, go1.x and older. Runtimes with a scheduled deprecation (python3.10 on 2026-10-31, dotnet8 on 2026-11-10, ...) are flagged from that date.

Example:

Function: my-legacy-handler
Runtime: python3.7
Status: EOL since November 2023
Monthly Savings: $0.01 (housekeeping)
Recommendation: Upgrade to python3.12 or later

AWS Reference: Lambda Runtime Support Policy

Why LOW confidence: Runtime upgrades may require code changes. Manual testing is essential before upgrading.


Unused API Gatewayโ€‹

Confidence: HIGH (detection), but LOW savings
Detection: CloudWatch Count = 0 for 30 days, on a REST API at least 30 days old
Savings: Minimal ($0.01) unless caching is enabled

Every REST API in the region is read (not only the first page), and a failed metric read is reported as missing data, not as an unused API.

Important Note: API Gateway is pay-per-request. Unused APIs cost $0 for requests. Only caching adds fixed costs (~$14/month for 0.5GB cache).

AWS Reference: API Gateway Pricing


Unused AppSync APIโ€‹

Confidence: LOW
Detection: Zero requests (CloudWatch Latency sample count) for 30 days, on an API at least 30 days old
Savings: Minimal (AppSync is pay-per-request)

How it works:

  1. We query CloudWatch for AppSync request metrics
  2. APIs with zero requests for 30+ days are flagged. AWS does not report when an AppSync API was created, so an unknown age counts as old and the finding says so
  3. AppSync charges per query/mutation, so unused APIs cost $0

Example:

API: my-graphql-api
Queries (30 days): 0
Monthly Savings: $0.00
Purpose: Housekeeping

AWS Reference: AppSync Pricing

Why flagged: Unused APIs clutter your account and may have unnecessary permissions.


Idle AppSync Cacheโ€‹

Confidence: MEDIUM Detection: Withheld, not active. The check does not fire today Savings: $32โ€“$4,878/month depending on cache size, once the check can fire

Why it is withheld:

  1. We enumerate all AppSync APIs and check for attached caches via GetApiCache
  2. AWS does not publish CacheHitCount or CacheMissCount under AWS/AppSync for any dimension. The only cache metrics are the paid Enhanced CacheHit and CacheMiss, keyed per resolver and emitted only when per-resolver enhanced metrics are on
  3. An empty series would read as a dead cache, so every available cache is reported as unmeasured in the scan's data warnings and never as idle (CLO-577). There is no numeric threshold because no verdict is drawn

Cache pricing (hourly):

Type$/hr
SMALL (t2.small)$0.044
MEDIUM (r4.large)$0.182
LARGE (r4.xlarge)$0.365
XLARGE (r4.2xlarge)$0.730
LARGE_2X (r4.4xlarge)$1.461
LARGE_4X (r4.8xlarge)$2.921
LARGE_8X$4.339
LARGE_12X$6.775

AWS Reference: AppSync Caching

Why flagged (once measurable): AppSync caches incur hourly charges regardless of usage. An idle cache provides no performance benefit while adding cost.


Idle AppSync Subscriptionsโ€‹

Confidence: MEDIUM
Detection: Active WebSocket connections with < 100 GraphQL requests in 14 days, on an API at least 14 days old
Savings: Estimated from connection-minutes ($0.08 per million connection-minutes)

How it works:

  1. We check CloudWatch ConnectSuccess metric to identify APIs with active WebSocket connections
  2. For those APIs, we check Latency (SampleCount) to see if actual GraphQL operations are occurring
  3. APIs with connections but fewer than 100 requests over 14 days are flagged. An unknown API age counts as old and the finding says so
  4. Monthly cost is estimated from the ConnectSuccess count ร— connection-minutes

Example:

API: my-realtime-api
Active Connections (avg): 150
GraphQL Requests (14 days): 23
Estimated Monthly Cost: $5.26

AWS Reference: AppSync Real-time Subscriptions

Why flagged: WebSocket connections that rarely receive data suggest abandoned or misconfigured subscription clients, wasting connection-minute charges.


Idle Step Functions State Machineโ€‹

Confidence: LOW
Detection: Zero executions for 14 days
Savings: Minimal (Step Functions is pay-per-transition)

How it works:

  1. We query CloudWatch for Step Functions execution metrics
  2. State machines with zero executions in the last 14 days are flagged (as never used when the past year has none either, otherwise as stale)
  3. Step Functions charges per state transition, so idle machines cost $0

Example:

State Machine: my-workflow
Executions (14 days): 0
Monthly Savings: $0.00
Purpose: Housekeeping

AWS Reference: Step Functions Pricing

Why flagged: Unused workflows may be obsolete or indicate broken integrations.


Step Functions Retry Stormโ€‹

Confidence: HIGH
Detection: Retry ratio โ‰ฅ 25% AND failure rate โ‰ฅ 20% over 14 days, with at least 50 executions
Savings: Estimated from avoidable transition volume

How it works:

  1. We collect CloudWatch metrics: ExecutionsFailed, ExecutionsTimedOut, StateTransition
  2. We estimate the proportion of transitions wasted on retry/failure paths
  3. State machines exceeding both thresholds are flagged

Example:

State Machine: order-processor
Failure Rate: 35%
Retry Ratio: 28% of transitions
Estimated Wasted Transitions: 14,000/month
Estimated Savings: $0.35/month

AWS Reference: Step Functions Pricing

Why flagged: Retry loops multiply transitions and downstream compute invocations without business value.


High Transition Density Workflowโ€‹

Confidence: MEDIUM
Detection: Average transitions per successful execution > 50 (configurable), with at least 50 executions
Savings: Estimated from reducible transitions

How it works:

  1. We compute average transitions per successful execution from CloudWatch
  2. Workflows with excessive state granularity are flagged
  3. Conservative 30% reduction estimate applied for savings

Example:

State Machine: data-pipeline
Avg Transitions per Success: 85
Monthly Executions: 2,000
Estimated Savings: $1.28/month

AWS Reference: Step Functions Pricing

Why flagged: Excessive state granularity inflates per-execution cost. Collapsing pass states and simplifying branching reduces transitions.


Express Workflow Duration Wasteโ€‹

Confidence: MEDIUM
Detection: Express workflow with p95 duration > 30,000 ms and at least 10,000 executions a month (and at least 50 in the window)
Savings: Estimated from reducible duration component

How it works:

  1. We identify Express workflows via describe-state-machine
  2. We collect ExecutionTime CloudWatch metrics (Average, Maximum)
  3. Workflows with persistently high duration and sufficient volume are flagged

Example:

State Machine: realtime-processor (EXPRESS)
p95 Duration: 45,000ms
Monthly Executions: 15,000
Estimated Savings: $0.50/month

AWS Reference: Step Functions Pricing

Why flagged: Express billing includes per-request + duration charges. Unnecessary waits and payload overhead inflate duration costs.


Idle Transfer Family Serverโ€‹

Confidence: HIGH Detection: ONLINE server with a service-managed identity provider and no configured users Savings: $0.30/hour (~$216/month) per enabled protocol

How it works:

  1. We list Transfer Family servers and keep the ONLINE ones
  2. Only servers whose identity provider is service-managed are checked for users. A server whose users live in an external identity provider (API Gateway, Directory Service or Lambda), or whose protocols were read as AS2 only (AS2 has no users), is judged on traffic instead (see Zero Activity below). A server whose identity provider could not be read is left out and named in the scan's data warnings. When the protocols could not be read (the standard scan role cannot read server details), the server is checked for users and priced at one protocol
  3. We list each server's users; a server whose user list could not be read is left out and named in the scan's data warnings
  4. Servers with zero users are flagged: they cannot receive any file transfer, whatever their traffic history
  5. Transfer Family charges $0.30/hour per protocol regardless of activity

Example:

Server: s-0abc123def456
Protocol: SFTP
Users: 0
Monthly Savings: $216.00

AWS Reference: Transfer Family Pricing

Why HIGH confidence: A server with a service-managed identity provider and no users cannot serve any SFTP, FTPS or FTP client.


Idle Transfer Family Server โ€” Zero Activityโ€‹

Confidence: HIGH
Detection: ONLINE server with zero file transfers for 30 days (a measured zero on a server at least 30 days old), judged on traffic only
Savings: $216/month per protocol ($0.30/hr ร— 720 hrs)

How it works:

  1. We take the ONLINE servers that have service-managed users, plus those whose users live in an external identity provider (API Gateway, Directory Service or Lambda) or whose protocols were read as AS2 only; for these last two the user count says nothing about use, so traffic alone decides
  2. We check CloudWatch FilesIn/FilesOut metrics for the last 30 days; a missing read, or an empty series on a server with AS2 enabled or whose protocols could not be read (AWS publishes AS2 metrics only when a message is processed), is left out and named in the scan's data warnings
  3. Servers with zero transfers are flagged โ€” the most common Transfer waste pattern

Example:

Server: s-0abc123def456
Users: 3
Protocols: SFTP
File Transfers (30 days): 0
Monthly Savings: $216.00

AWS Reference: Transfer Family Pricing

Why HIGH confidence: Zero transfers for 30+ days on a server old enough to have a full window strongly indicates the server is abandoned.


Unused Protocol on Transfer Family Serverโ€‹

Confidence: MEDIUM
Detection: Multi-protocol server where at least one protocol shows zero transfers for 30 days while another protocol is active
Savings: $216/month per unused protocol

How it works:

  1. We identify servers with multiple protocols enabled (SFTP, FTPS, FTP, AS2)
  2. We check CloudWatch metrics per protocol for the last 30 days (only when the server as a whole had transfers in its own window)
  3. Protocols with zero transfers are flagged, provided no protocol's read was missing โ€” each unused protocol costs $216/month

Example:

Server: s-0abc123def456
Protocols: SFTP, FTPS
Active: SFTP (142 transfers)
Unused: FTPS (0 transfers)
Monthly Savings: $216.00

AWS Reference: Transfer Family Pricing

Why MEDIUM confidence: Removing a protocol is reversible but could break clients configured for that protocol.


Idle Transfer Family Web Appโ€‹

Confidence: HIGH
Detection: Web App with zero active sessions for 30 days
Savings: $360/month per provisioned unit ($0.50/hr ร— 720 hrs)

How it works:

  1. We query Transfer Family Web Apps and check provisioned units
  2. We check CloudWatch ActiveSessions metric for the last 30 days; an empty series is left out and named in the scan's data warnings, only measured zeros count
  3. Web Apps with zero sessions are flagged โ€” they run continuously once created

Example:

Web App: webapp-0abc123def
Provisioned Units: 2
Active Sessions (30 days): 0
Monthly Savings: $720.00

AWS Reference: Transfer Family Pricing

Why HIGH confidence: Zero sessions for 30+ days means no users are accessing the Web App.


Analytics Detectorsโ€‹

Old Glue Jobโ€‹

Confidence: LOW
Detection: AWS Glue job whose last run is more than 90 days ago, or with no run history and not modified for more than 90 days
Savings: Minimal (Glue jobs are pay-per-use)

How it works:

  1. We list all Glue jobs, skip any modified within the last 90 days, and read the latest run of the rest
  2. A job whose latest run started more than 90 days ago is flagged
  3. A job with no run history at all is flagged when its definition was last modified more than 90 days ago (we judge by the last modification, since there is no run to date)
  4. If the run history cannot be read, the job gets no verdict and the scan reports missing data

Example:

Job: etl-pipeline-v1
Last Run: 95 days ago (or never)
Monthly Savings: $0.00 (housekeeping)
Purpose: Cleanup unused ETL jobs

AWS Reference: Glue Pricing

Why LOW confidence: Jobs may be scheduled for future use or disaster recovery.


Idle Glue Crawlerโ€‹

Confidence: MEDIUM
Detection: AWS Glue crawler whose last crawl started more than 90 days ago, or that has never run and was created more than 90 days ago
Savings: Minimal (Glue crawlers are pay-per-use)

How it works:

  1. We list all Glue crawlers and check their last run time
  2. Crawlers that haven't run in more than 90 days are flagged
  3. Crawlers that have never run are flagged once they were created more than 90 days ago

Example:

Crawler: data-catalog-crawler
Last Run: 120 days ago
Monthly Savings: $0.00 (housekeeping)
Purpose: Cleanup unused data catalog crawlers

AWS Reference: Glue Pricing

Why MEDIUM confidence: Crawlers may be scheduled periodically or kept for future use.


Idle EMR Clusterโ€‹

Confidence: HIGH
Detection: Cluster in WAITING state now and running for more than 24 hours
Savings: Full cluster cost

How it works:

  1. We identify EMR clusters in WAITING state (up, with no step running) and measure how long they have been up, from the time the cluster became ready
  2. Clusters that have been up for more than 24 hours are flagged
  3. EMR charges for all EC2 instances in the cluster

Example:

Cluster: j-0ABC123DEF456
State: WAITING
Active Steps: 0
Instance Count: 5 (1 master, 4 core)
Monthly Savings: $730.00

AWS Reference: EMR Pricing

Why HIGH confidence: Clusters in WAITING state with no steps are consuming resources without processing data.


Idle Kinesis Streamโ€‹

Confidence: HIGH
Detection: Zero records for 14 days, on a stream at least 14 days old
Savings: Shard hours ($0.015/shard-hour)

How it works:

  1. We query CloudWatch for IncomingRecords metrics
  2. Streams with zero records over the 14-day window are flagged; a stream younger than the window is skipped, because its data is partial
  3. Kinesis charges per shard-hour regardless of activity

Example:

Stream: my-data-stream
Shards: 4
Incoming Records (14 days): 0
Monthly Savings: $43.80

AWS Reference: Kinesis Data Streams Pricing

Why HIGH confidence: Zero incoming records means no data is being streamed.


Idle MSK Clusterโ€‹

Confidence: HIGH Detection: No user-owned partition on any broker for 7 days (CloudWatch UserPartitionExists = 0), on a cluster at least 7 days old Savings: Full cluster cost (instance-type-aware)

How it works:

  1. We read CloudWatch AWS/Kafka per broker (MessagesInPerSec, BytesInPerSec, BytesOutPerSec, UserPartitionExists)
  2. AWS runs its own canary on every cluster, so traffic is never exactly zero. A cluster is flagged only when each broker stays at or under 0.3 messages/s and 64 bytes/s (in or out) and no broker had a user-owned partition during the 7-day window
  3. Canary-level traffic on a cluster that still has user topics, or an unmeasured UserPartitionExists, is never called idle: it is left out and named in the scan's data warnings
  4. MSK charges for broker instance hours and storage

Example:

Cluster: my-kafka-cluster
Brokers: 3 (kafka.m5.large)
User partitions (7 days): none
Monthly Savings: $459.90

AWS Reference: MSK Pricing

Why HIGH confidence: No user topic means no producers or consumers are using this Kafka cluster.


Oversized MSK Clusterโ€‹

Confidence: MEDIUM
Detection: Busiest-broker CPU < 20% AND network throughput < 50% of one broker's capacity for 7 days (cluster is not idle)
Savings: Recommendation only (depends on target instance type)

How it works:

  1. We query CloudWatch for CpuUser, BytesInPerSec, and BytesOutPerSec metrics per broker; the CPU used is the busiest broker's, and a cluster whose CPU was not measured is left out
  2. Network utilization is calculated as (BytesIn + BytesOut) / instance network capacity โ€” each instance type has a known throughput ceiling
  3. If both the busiest broker's average CPU is below 20% AND network throughput is below 50% of instance capacity over 7 days, the cluster may be over-provisioned
  4. We flag the cluster for manual review โ€” MSK is network-bound, so we check both signals to avoid false positives

Example:

Cluster: my-kafka-cluster
Brokers: 3 (kafka.m5.2xlarge)
Average CPU: 8.2%
Network Utilization: 4.3%
Messages/sec: 150
Current Cost: $1,839/month
Action: Consider downsizing to kafka.m5.large ($460/month)

AWS Reference: MSK Instance Types

Why MEDIUM confidence: MSK is network-bound, not CPU-bound. Low CPU alone doesn't indicate over-provisioning โ€” a cluster saturating its network at 10% CPU is properly sized. CloudWise requires both low CPU and low network utilization before flagging. Review consumer lag, partition count, and storage throughput before downsizing.


Oversized Glue Jobโ€‹

Confidence: LOW
Detection: JVM heap utilization < 30% average over 14 days OR short execution with high DPU allocation
Savings: Proportional to excess DPUs ร— average run duration ร— frequency; findings under $5 a month are dropped

How it works:

  1. We examine each Glue ETL job's DPU allocation (MaxCapacity or NumberOfWorkers) and skip jobs with 2 DPUs or fewer
  2. We read the job's latest 5 runs and query CloudWatch for glue.ALL.jvm.heap.usage to measure worker memory utilization
  3. If average heap usage is below 30%, the job has more workers than it needs
  4. We also apply a heuristic: with at least 3 successful runs averaging under 0.5 hours on 10 or more DPUs, the job is likely oversized
  5. A job with no run history is flagged for review when it allocates at least 5 times the minimum DPUs above, and never fewer than 10 DPUs
  6. Savings are calculated based on the excess DPUs, average run duration, and estimated run frequency

Example:

Job: daily-customer-etl
Allocated: 20 DPUs (G.1X workers)
Avg JVM Heap: 15%
Avg Duration: 12 minutes
Recommended: 6 DPUs
Estimated Runs: 30/month
Monthly Excess Cost: $55.44

Recommended Action: Test with reduced DPUs in a development environment. If the job completes successfully within acceptable time, update production.

AWS Reference: Glue Job Properties | Monitoring with CloudWatch

Why LOW confidence: JVM heap usage is a reliable signal, but some jobs have legitimate peak memory needs that don't show in averages. The heuristic fallback (short duration + high DPUs) is less reliable.


Glue Job โ€” Excessive Timeout Riskโ€‹

Confidence: MEDIUM
Detection: Configured timeout โ‰ฅ 10ร— average execution duration, and at least 60 minutes
Savings: Risk mitigation โ€” prevents catastrophic billing from stuck jobs

How it works:

  1. We compare the job's configured Timeout against its average execution duration over the latest 5 runs
  2. Jobs where the timeout is at least 10ร— the average duration are flagged
  3. Only jobs with recent successful runs and a timeout of at least 60 minutes are evaluated
  4. A job with no run history is flagged when its timeout is at or above the 2,880-minute AWS default
  5. We recommend setting timeout to 3ร— average duration (industry best practice)

Example:

Job: nightly-data-sync
Configured Timeout: 2,880 minutes (48 hours โ€” the default)
Average Duration: 8 minutes
Timeout Ratio: 360ร—
Recommended Timeout: 24 minutes (3ร— average)
Risk: If this job hangs, it burns $211 before timing out
(10 DPUs ร— 48 hours ร— $0.44)

Recommended Action: Set the timeout to 3ร— your average duration. This protects against stuck jobs while allowing for normal execution variance.

Agentic Tier โ€” Automated Fix Available

Click Fix This to update the timeout configuration automatically, with full rollback capability.

AWS Reference: Glue Job Timeout

Why MEDIUM confidence: The timeout-to-duration ratio is a strong signal, but some jobs have legitimate long-tail executions on certain data partitions.


Failing Glue Job with Retriesโ€‹

Confidence: HIGH
Detection: โ‰ฅ 50% failure rate across at least 3 recent runs
Savings: Direct โ€” eliminates DPU cost from repeated failed runs; findings under $5 a month are dropped

How it works:

  1. We examine the last 10 job runs for each Glue job
  2. Jobs with at least 3 runs and a failure rate of 50% or more are flagged (the MaxRetries setting is shown in the finding but is not a condition)
  3. A job with MaxRetries > 0 and no run history at all is flagged for verification
  4. We calculate the total DPU-hours wasted on failed runs
  5. Monthly waste is projected from the observed failure frequency
  6. The most recent error message is captured for root cause analysis

Example:

Job: inventory-etl
Failure Rate: 80% (8 of 10 runs FAILED)
MaxRetries: 3
DPUs: 10
Avg Failed Run Duration: 15 minutes
Monthly Wasted DPU-hours: 90
Monthly Waste: $39.60
Last Error: "S3 bucket 'inventory-raw' does not exist"

Recommended Action: Disable retries immediately to stop the cost bleeding, then investigate and fix the root cause error.

Agentic Tier โ€” Automated Fix Available

Click Fix This to disable retries automatically, with full rollback capability.

AWS Reference: Glue Job Retries

Why HIGH confidence: A sustained failure rate at or above the threshold across the recent runs is a definitive pattern. Combined with retry configuration, this is very likely waste.


Glue Data Catalog Bloatโ€‹

Confidence: LOW
Detection: Data Catalog object count exceeds the 1,000,000-object free tier, or table versions exceed 500,000
Savings: $1.00 per 100,000 objects above the free tier

How it works:

  1. We count total objects in the Data Catalog (databases, tables, table versions, partitions)
  2. Catalogs exceeding 1,000,000 objects are flagged with the calculated overage cost
  3. Catalogs still under the free tier with more than 500,000 table versions get an early warning
  4. The primary bloat source is table versions โ€” every UpdateTable call creates a new version

Example:

Data Catalog Objects:
Databases: 12
Tables: 450
Table Versions: 1,200,000 โ† The problem
Partitions: 350,000
Total: 1,550,462
Overage: 550,462 objects above free tier
Monthly Cost: $5.50

Recommended Action: Delete old table versions using aws glue batch-delete-table-version. Keep only the latest 5โ€“10 versions per table.

AWS Reference: Data Catalog Pricing | Table Versioning

Why LOW confidence: The cost is accurate but relatively low. The detector's value is catching uncontrolled growth before it becomes expensive.


Idle MQ Brokerโ€‹

Confidence: HIGH
Detection: Zero stored messages, consumers and producers (RabbitMQ: connections) at every hour for 14 days, on a broker at least 14 days old
Savings: Full broker cost

How it works:

  1. We read CloudWatch AWS/AmazonMQ by broker name (ActiveMQ <name>-1, plus <name>-2 for an active/standby pair; RabbitMQ <name>)
  2. ActiveMQ: hourly maximum of TotalMessageCount, TotalConsumerCount and TotalProducerCount. RabbitMQ: MessageCount, ConsumerCount and ConnectionCount
  3. Brokers where every one stays at zero for 14+ days are flagged. A broker with less than 75% of the hours measured is left out and named in the scan's data warnings, never read as idle
  4. Savings account for instance type pricing and HA deployment multipliers; a broker whose monthly cost is below $0.01 is left out

Example:

Broker: my-activemq-broker
Engine: ActiveMQ
Instance Type: mq.t3.micro
Connections (14 days): 0
Monthly Savings: $24.82

AWS Reference: Amazon MQ Pricing

Why HIGH confidence: Zero connections means no applications are connected to this message broker.


Over-Provisioned MQ Brokerโ€‹

Confidence: MEDIUM
Detection: MQ broker with average CPU at or below 15% over 14 days
Savings: $50โ€“$1,400/month per right-sizing step

How it works:

  1. We query Amazon MQ for all running brokers
  2. CloudWatch CpuUtilization (RabbitMQ: SystemCpuUtilization) is read by broker name and averaged over 14 days
  3. Brokers with average CPU at or below 15% on mq.m5.large or larger are flagged; a broker whose CPU wasn't measured is left out (data warning), never read as 0%
  4. We identify the next smaller instance type as the downsizing target
  5. Savings multiply by the number of instances (HA = 2ร—, RabbitMQ cluster = 3ร—); a downsize saving below $0.01 a month is left out

Example:

Broker: dev-event-bus
Engine: ActiveMQ
Instance Type: mq.m5.large
Deployment Mode: ACTIVE_STANDBY_MULTI_AZ
CPU Average (14 days): 4.2%
Current Cost: $402.96/month (2 instances ร— $201.48)
Target: mq.t3.micro ($49.64/month for 2 instances)
Monthly Savings: $353.32

Recommended Action: Create a new broker with the smaller instance type, migrate configuration and queue definitions, update application connection strings, verify message flow, then delete the old broker.

Important

Amazon MQ does not support in-place instance type changes. Downsizing requires creating a new broker and migrating. Plan for brief message delivery interruption during cutover.

AWS Reference: Amazon MQ Pricing

Why MEDIUM confidence: CPU is a good proxy for message throughput but doesn't capture memory-bound scenarios (large message batches). Validate with monitoring after downsizing.


Long-Running EMR Clusterโ€‹

Confidence: MEDIUM
Detection: EMR cluster in RUNNING state for more than 720 hours with no running step and no step finished in the last 7 days
Savings: ~30% from using Spot instances or transient clusters

How it works:

  1. We identify EMR clusters in RUNNING state that have been up for more than 720 hours
  2. We read the step history: a cluster is flagged only when no step is running and the last step ended more than 7 days ago (or there never was one). If the step history cannot be read, the cluster gets no verdict
  3. Long-running clusters may be better served by transient (job-scoped) clusters

Example:

Cluster: j-ABC123DEF456
Name: analytics-etl
Running: 45 days
Last step finished: 20 days ago
Instance Type: m5.xlarge ร— 5
Monthly Savings: $450.00 (Spot + transient)

AWS Reference: EMR Pricing

Why MEDIUM confidence: Some EMR clusters intentionally run continuously for interactive workloads (Spark Thrift Server, Presto). Verify workload pattern.


Over-Provisioned EMR Clusterโ€‹

Confidence: MEDIUM
Detection: YARN memory available above 60% averaged over 14 days, with fewer than 2 running applications on average, on a RUNNING cluster up for more than 168 hours
Savings: Proportional to instance count reduction; the saving must be at least $0.01 a month

How it works:

  1. We pull CloudWatch YARNMemoryAvailablePercentage and running-application counts for each RUNNING cluster over 14 days
  2. If average available memory exceeds 60%, the CORE and TASK groups with more than one instance are over-provisioned
  3. Recommended new count = current ร— (1 โˆ’ available_pct / 100) plus 30% headroom, minimum 1

Example:

Cluster: j-OVERPROV001
Name: data-pipeline
YARN Memory Available: 72%
CORE Nodes: 8 โ†’ 4 recommended
Monthly Savings: $700.00

AWS Reference: EMR Instance Groups

Why MEDIUM confidence: YARN memory is a good proxy for utilisation but workloads may have burst patterns. Monitor after resize.


EMR Missing Auto-Terminationโ€‹

Confidence: MEDIUM
Detection: Keep-alive cluster running more than 24 hours with no AutoTerminationPolicy
Savings: Prevents indefinite idle costs

How it works:

  1. We check clusters with KeepJobFlowAliveWhenNoSteps=True that have been up for more than 24 hours
  2. If no AutoTerminationPolicy is set (confirmed by a successful read), the cluster will never terminate on its own. Releases that cannot take a policy (before emr-5.30.0 and emr-6.1.0) are not flagged
  3. Recommendation: set a 1-hour idle timeout

Example:

Cluster: j-NOAUTOTERM01
Name: adhoc-queries
Keep Alive: Yes
Auto-Termination: Not configured
Instances: 5

AWS Reference: EMR Auto-Termination

Why MEDIUM confidence: Some clusters are intentionally long-lived (e.g., serving Presto/Hive endpoints). Verify before applying.


EMR Previous-Generation Instancesโ€‹

Confidence: MEDIUM
Detection: Cluster up for more than 24 hours using previous-generation instance families
Savings: ~15โ€“20% cost reduction with current-generation instances

How it works:

  1. We check instance types in all instance groups of clusters up for more than 24 hours
  2. If any instance family is in the previous-gen map (m3โ†’m5, m4โ†’m5, c3โ†’c5, c4โ†’c5, r3โ†’r5, r4โ†’r5, i2โ†’i3, d2โ†’d3, p2โ†’p3, g2โ†’g4dn), we flag the cluster
  3. Current-gen instances offer better performance at lower cost

Example:

Cluster: j-PREVGEN001
Name: reports-cluster
Current Type: m4.xlarge ($0.240/hr)
Recommended: m5.xlarge ($0.192/hr)
Savings per Instance: $14.60/month

AWS Reference: EC2 Instance Types

Why MEDIUM confidence: Current-gen instances are generally drop-in replacements but EMR release compatibility should be verified.


EMR Spot Opportunityโ€‹

Confidence: MEDIUM
Detection: TASK instance group of 2 or more nodes using On-Demand pricing, on a RUNNING or WAITING cluster up for more than 48 hours
Savings: the estimate assumes Spot costs less than On-Demand for the task nodes (see EC2 Spot pricing for current rates); the saving must be at least $0.01 a month

How it works:

  1. We identify TASK instance groups with Market=ON_DEMAND and at least 2 running instances
  2. Task nodes are fault-tolerant โ€” YARN reschedules work on Spot interruption
  3. Spot pricing is lower than On-Demand (see the EC2 Spot pricing page)

Example:

Cluster: j-SPOTOPPTY01
Task Nodes: 4 ร— r5.2xlarge (On-Demand)
On-Demand Cost: $0.504/hr per instance
Spot Estimate: ~$0.17/hr per instance
Monthly Savings: $964.00

AWS Reference: EMR Spot Instances

Why MEDIUM confidence: Spot instances can be interrupted. Verify workload is fault-tolerant before switching.


Over-Provisioned Kinesis Streamโ€‹

Confidence: MEDIUM
Detection: Kinesis Data Stream with more than 1 shard and per-shard utilization < 20% over 14 days
Savings: Excess shards ร— $11/shard/month; the saving must be at least $0.01 a month

How it works:

  1. We analyze Kinesis stream IncomingBytes relative to shard capacity, on streams at least 14 days old
  2. Each shard can handle 1 MB/s input and 2 MB/s output
  3. Streams with < 20% utilization per shard have excess capacity; the recommended size keeps 30% headroom

Example:

Stream: event-ingestion
Shards: 10
Avg Throughput: 1.2 MB/s (12% per-shard utilization)
Recommended: 4 shards
Monthly Savings: $66.00 ($11/shard ร— 6 excess shards)

AWS Reference: Kinesis Data Streams Pricing

Why MEDIUM confidence: Stream throughput can be bursty. Analyze peak utilization and consider On-Demand mode for variable workloads.


Kinesis On-Demand Downgradeโ€‹

Confidence: MEDIUM
Detection: On-Demand stream at least 30 days old with stable throughput (coefficient of variation < 0.3) over 14 days
Savings: On-Demand vs Provisioned cost delta; the saving must exceed $0.01 a month

How it works:

  1. Identifies On-Demand streams at least 30 days old with predictable traffic patterns
  2. Calculates coefficient of variation (CV) of daily IncomingBytes over 14 days, which needs at least 7 daily datapoints
  3. CV < 0.3 indicates stable throughput suitable for Provisioned mode
  4. Computes recommended shard count with 30% headroom

Example:

Stream: log-pipeline
Mode: ON_DEMAND
CV: 0.12 (stable)
On-Demand Cost: $58.20/month
Provisioned (2 shards): $21.90/month
Monthly Savings: $36.30

AWS Reference: Kinesis Data Streams Pricing


Kinesis Extended Retention Wasteโ€‹

Confidence: HIGH
Detection: Extended retention (more than 24 hours) with zero GetRecords in 14 days, on a stream at least that old
Savings: Shards ร— $0.020/shard-hour ร— 730 hours/month

How it works:

  1. Identifies streams with retention period of more than 24 hours (the default)
  2. Checks if any consumer is reading data (GetRecords metric)
  3. If zero reads for 14 days, extended retention is waste

Example:

Stream: clickstream-archive
Retention: 168 hours (7 days)
Shards: 4
GetRecords (14d): 0
Monthly Savings: $58.40 ($0.020 ร— 4 shards ร— 730 hours)

Kinesis Enhanced Fan-Out Wasteโ€‹

Confidence: HIGH
Detection: Enhanced fan-out consumer, at least 14 days old, registered but never subscribed in 14 days
Savings: Shards ร— $0.015/consumer-shard-hour ร— 730 hours/month
Availability: Online Mode only, and only when the CloudWise role can call cloudwatch:ListMetrics

How it works:

  1. Lists enhanced fan-out consumers registered on each stream, and skips any consumer younger than 14 days
  2. Asks CloudWatch which metrics exist for the stream and consumer (ListMetrics, filtered on both StreamName and ConsumerName). AWS publishes SubscribeToShard.Success at least every 5 minutes while a consumer is subscribed, and ListMetrics reports every metric that had data in the past two weeks
  3. A consumer with no SubscribeToShard metric at all is reported: it is registered, billed, and has not subscribed in 14 days. A consumer with any published series is subscribed, even one reading zero records from a quiet stream, and is never reported (the stream-level checks cover an idle stream). If CloudWatch can't be read, or the role isn't allowed to call ListMetrics (the monitoring template grants it from version 1.29.0; an older stack is shown a notice to update), the consumer is not judged and the scan reports missing data, not an idle consumer

Example:

Stream: events/analytics-consumer
Shards: 4
Consumer: analytics-consumer (ACTIVE, registered 60 days ago)
SubscribeToShard metrics in the past two weeks: none
Monthly Savings: $43.80 ($0.015 ร— 4 shards ร— 730 hours)

Idle Kinesis Firehoseโ€‹

Confidence: HIGH
Detection: Firehose delivery stream with zero IncomingRecords for 14 days, on a stream at least that old
Savings: $0 direct (pay-per-use), but eliminates forgotten infrastructure

How it works:

  1. Lists all Firehose delivery streams
  2. Checks IncomingRecords and IncomingBytes metrics over 14 days
  3. Idle streams are flagged as forgotten infrastructure
  4. Notes if Lambda transforms are configured (may have associated costs)

Example:

Delivery Stream: logs-to-s3-firehose
Status: ACTIVE
Records (14d): 0
Lambda Transform: Yes (may incur invocation costs)

ML/AI Detectorsโ€‹

Idle SageMaker Notebook Instanceโ€‹

Confidence: MEDIUM
Detection: Notebook instance InService, not started or modified, and with no Jupyter server log activity for the idle window
Savings: Full instance cost

How it works:

  1. We identify SageMaker notebook instances in InService status that have not been started or reconfigured within the window (LastModifiedTime)
  2. We read the last event time of the notebook's Jupyter server log (CloudWatch Logs /aws/sagemaker/NotebookInstances, stream <name>/jupyter.log), which records kernel, session and save activity
  3. Only a notebook with no log event in the window is flagged. No readable log (no log stream, a failed read, or an Air-Gapped export) is reported as missing data, never as idle
  4. Idle notebooks continue to charge full instance cost

Example:

Notebook: my-ml-notebook
Instance Type: ml.t3.medium
Status: InService
Activity (7 days): None
Monthly Savings: $37.00

AWS Reference: SageMaker Pricing

Why MEDIUM confidence: A long job running in a kernel that logs nothing would look idle; check before stopping.


Idle SageMaker Endpointโ€‹

Confidence: HIGH
Detection: Zero invocations for 7 days, on an endpoint at least 7 days old
Savings: Full endpoint cost

How it works:

  1. We query CloudWatch for endpoint invocation metrics
  2. Endpoints with zero invocations over the last 7 days are flagged
  3. Idle endpoints charge full compute cost continuously

Example:

Endpoint: my-model-endpoint
Instance Type: ml.m5.large
Invocations (7 days): 0
Monthly Savings: $96.00

AWS Reference: SageMaker Pricing

Why HIGH confidence: Zero invocations is unambiguous. Endpoint is not being used.


Oversized SageMaker Endpointโ€‹

Confidence: MEDIUM
Detection: Average CPU under 20% and memory under 20% over 7 days, with at least 100 invocations
Savings: Based on right-sized instance

How it works:

  1. We analyze CloudWatch metrics for endpoint CPU and memory utilization
  2. Endpoints with consistently low utilization are flagged
  3. Right-sizing to smaller instance type reduces costs

Example:

Endpoint: my-inference-endpoint
Instance Type: ml.c5.xlarge
Avg CPU: 15%
Avg Memory: 20%
Monthly Savings: ~$70.00 (if downsized to ml.c5.large)

AWS Reference: SageMaker Pricing

Why MEDIUM confidence: Inference workloads may have periodic spikes. Review before right-sizing.


Stopped SageMaker Notebook Storageโ€‹

Confidence: HIGH
Detection: Stopped notebook instance with attached EBS volume
Savings: $0.58 โ€“ $58/month per notebook (based on EBS volume size)

How it works:

  1. We identify SageMaker notebook instances in Stopped status
  2. Stopped notebooks still incur SageMaker notebook ML storage charges ($0.14/GB-month in us-east-1)
  3. Notebooks stopped for 7 days or more are flagged
  4. Deleting the notebook removes the attached EBS volume

Example:

Notebook: dev-ml-notebook
Status: Stopped (30 days)
Volume Size: 50 GB
Monthly Storage Cost: $5.80
Action: Delete notebook instance to remove EBS volume

AWS Reference: SageMaker Pricing

Why HIGH confidence: Stopped status is binary and volume size is exact. No ambiguity.


Previous-Generation SageMaker Instanceโ€‹

Confidence: HIGH
Detection: Notebook or endpoint using a previous-gen ml.m4 or ml.c4 instance type with a mapped upgrade (ml.m4.xlarge, ml.m4.2xlarge, ml.m4.4xlarge, ml.c4.xlarge, ml.c4.2xlarge)
Savings: 10-30% of current cost

How it works:

  1. We check instance types against a known previous-generation prefix list
  2. Each flagged resource includes a specific upgrade recommendation
  3. Covers both notebook instances and endpoint instances
  4. Upgrade map: ml.m4โ†’ml.m5, ml.c4โ†’ml.c5 (same size). Other sizes have no priced upgrade and are not flagged

Example:

Notebook: legacy-training-nb
Instance Type: ml.m4.xlarge ($0.28/hr)
Recommended: ml.m5.xlarge ($0.23/hr)
Monthly Savings: $36.50 (18% reduction)

AWS Reference: SageMaker Pricing

Why HIGH confidence: Instance type is deterministic metadata. No ambiguity in detection.


SageMaker Savings Plan Opportunityโ€‹

Confidence: HIGH
Detection: AWS Cost Explorer recommendation for SageMaker Savings Plan, reported only when the estimated saving is at least 10 USD a month
Savings: Exact AWS calculation (typically 20โ€“40% vs On-Demand)

Note: AWS doesn't say whether its Savings Plan coverage figures include SageMaker usage, so this saving comes off the Savings Plan Coverage Gap finding rather than adding to it.

How it works:

  1. AWS Cost Explorer analyzes 30 days of SageMaker usage (training, inference, notebooks)
  2. Identifies consistent usage patterns suitable for a SageMaker Savings Plan commitment
  3. Returns exact savings calculation for 1-year or 3-year terms

Example:

Current SageMaker Spend: $850.00/month (On-Demand)
Recommended SP: $550.00/month commitment
Monthly Savings: $300.00 (35%)
Term: 1-year, No Upfront

AWS Reference: SageMaker Savings Plans

Why HIGH confidence: Uses AWS's own Cost Explorer analysis with exact pricing data.


Security Detectorsโ€‹

Unused Secrets Manager Secretโ€‹

Confidence: MEDIUM
Detection: Secret not accessed for 90 days
Savings: $0.40/month per secret

How it works:

  1. We check the LastAccessedDate for each secret
  2. Secrets last accessed more than 90 days ago are flagged
  3. A secret that has never been accessed is flagged (LOW confidence) once it was created more than 30 days ago
  4. Secrets Manager charges $0.40/month per secret

Example:

Secret: old-api-key
Last Accessed: 120 days ago
Monthly Savings: $0.40

AWS Reference: Secrets Manager Pricing

Why MEDIUM confidence: The access date is tracked by AWS, but a rarely used secret (for example an annual job) can still be needed.


Unused KMS Keyโ€‹

Confidence: LOW
Detection: Enabled customer-managed key not referenced by any CloudTrail trail
Savings: $1.00/month per key

How it works:

  1. We list the customer-managed keys (skipping keys pending deletion) and the KMS keys that CloudTrail trails encrypt with (KmsKeyId)
  2. Keys no trail references are flagged as a hygiene hint, because per-key usage cannot be verified
  3. KMS charges $1.00/month per customer-managed key

Example:

Key: alias/old-encryption-key
Key ID: arn:aws:kms:us-east-1:123456789:key/abc123
Referenced by a CloudTrail trail: no
Monthly Savings: $1.00

AWS Reference: KMS Pricing

Why this is only a hint: CloudWise cannot see cryptographic operations on a key, so a flagged key may still be used by an application or AWS service. Verify before scheduling deletion.


Unencrypted EBS Volumeโ€‹

Confidence: HIGH
Detection: EBS volume with encrypted = false in in-use state
Savings: Zero (security/compliance finding)

How it works:

  1. We check the encrypted attribute on all EBS volumes
  2. Volumes in in-use state without encryption at rest are flagged
  3. EBS encryption has no performance penalty and uses AWS-managed or customer-managed KMS keys

Example:

Volume: vol-0abc123def456
State: in-use
Encrypted: No
Size: 500 GB
Action: Enable default EBS encryption, recreate volume from snapshot

AWS Reference: EBS Encryption

Why HIGH confidence: Encryption status is deterministic metadata from the EBS API.


Unencrypted RDS Instanceโ€‹

Confidence: HIGH
Detection: RDS instance with StorageEncrypted = false
Savings: Zero (security/compliance finding)

How it works:

  1. We check the StorageEncrypted attribute on all RDS instances
  2. Unencrypted instances cannot be encrypted in-place โ€” requires snapshot + restore
  3. This is a compliance requirement for most security frameworks (SOC 2, HIPAA, PCI)

Example:

Instance: mydb-production
Engine: mysql 8.0
Encrypted: No
Action: Create encrypted snapshot, restore encrypted instance

AWS Reference: RDS Encryption

Why HIGH confidence: Encryption status is exact API metadata. No estimation needed.


Unencrypted EFS Filesystemโ€‹

Confidence: HIGH
Detection: EFS filesystem with encrypted = false
Savings: Zero (security/compliance finding)

How it works:

  1. We check the Encrypted attribute on all EFS filesystems
  2. EFS encryption must be enabled at creation โ€” cannot be added to existing filesystems
  3. Migration requires creating a new encrypted filesystem and copying data

Example:

Filesystem: fs-0abc123def456
Encrypted: No
Size: 150 GB
Action: Create encrypted EFS, migrate data with DataSync

AWS Reference: EFS Encryption

Why HIGH confidence: Encryption status is deterministic. No false positives possible.


Unencrypted DocumentDB Clusterโ€‹

Confidence: HIGH
Detection: DocumentDB cluster with StorageEncrypted = false
Savings: Zero (security/compliance finding)

How it works:

  1. We check the StorageEncrypted attribute on DocumentDB clusters
  2. Unencrypted clusters cannot be encrypted in-place
  3. Migration requires creating an encrypted cluster and restoring from snapshot

Example:

Cluster: my-docdb-cluster
Encrypted: No
Action: Create encrypted cluster, restore from snapshot

AWS Reference: DocumentDB Encryption

Why HIGH confidence: Encryption status is exact metadata from the DocumentDB API.


S3 Without Default Encryptionโ€‹

Confidence: MEDIUM
Detection: S3 bucket without default encryption configuration
Savings: Zero (compliance finding)

How it works:

  1. We check for ServerSideEncryptionConfiguration on each S3 bucket
  2. Buckets without default encryption rely on individual object-level encryption
  3. Since January 2023, AWS enables SSE-S3 by default โ€” this detector catches legacy buckets

Example:

Bucket: legacy-data-bucket
Default Encryption: None
Action: Enable SSE-S3 or SSE-KMS default encryption

AWS Reference: S3 Default Encryption

Why MEDIUM confidence: Legacy buckets may have object-level encryption already applied. Bucket-level policy provides a safety net.


OpenSearch Encryption Not Enabledโ€‹

Confidence: HIGH
Detection: OpenSearch domain with EncryptionAtRestOptions.Enabled = false
Savings: Zero (security/compliance finding)

How it works:

  1. We check the EncryptionAtRestOptions configuration on OpenSearch domains
  2. Domains without encryption at rest are flagged as a compliance issue
  3. Encryption can be enabled on existing domains (in-place, no downtime)

Example:

Domain: search-logs
Encryption at Rest: Disabled
Action: Enable encryption at rest in domain settings

AWS Reference: OpenSearch Encryption

Why HIGH confidence: Encryption configuration is exact API metadata.


RDS Without Deletion Protectionโ€‹

Confidence: HIGH
Detection: RDS instance with DeletionProtection = false
Savings: Zero (reliability/governance finding)

How it works:

  1. We check the DeletionProtection attribute on all RDS instances
  2. Production databases without this safeguard risk accidental deletion
  3. Enabling deletion protection is a one-click change with no downtime

Example:

Instance: production-db
Deletion Protection: Disabled
Action: Enable in RDS console โ†’ Modify โ†’ Deletion Protection

AWS Reference: RDS Deletion Protection

Why HIGH confidence: Configuration attribute is exact metadata. All production databases should have this enabled.


DynamoDB Without Deletion Protectionโ€‹

Confidence: HIGH
Detection: DynamoDB table with DeletionProtectionEnabled = false
Savings: Zero (reliability/governance finding)

How it works:

  1. We check the DeletionProtectionEnabled attribute on DynamoDB tables
  2. Tables without protection can be accidentally deleted via API or console
  3. Enabling deletion protection prevents accidental table deletion

Example:

Table: user-sessions
Deletion Protection: Disabled
Action: Enable via UpdateTable API or console

AWS Reference: DynamoDB Deletion Protection

Why HIGH confidence: Attribute is exact boolean metadata from the DynamoDB API.


RDS Publicly Accessibleโ€‹

Confidence: HIGH
Detection: RDS instance with PubliclyAccessible = true
Savings: Zero (security finding)

How it works:

  1. We check the PubliclyAccessible attribute on RDS instances
  2. Publicly accessible databases are reachable from the internet (if security groups allow)
  3. Best practice is to place databases in private subnets with VPC-only access

Example:

Instance: mydb-production
Publicly Accessible: Yes
Action: Modify instance โ†’ set Publicly Accessible to No

AWS Reference: RDS Security Best Practices

Why HIGH confidence: Boolean attribute from the RDS API. Combined with open security groups, this is a critical security risk.


Resource Without Backup Coverageโ€‹

Confidence: MEDIUM
Detection: RDS/DynamoDB resource with no AWS Backup recovery points
Savings: Backup cost (~$0.50โ€“$5/month per resource)

How it works:

  1. We cross-reference RDS instances and DynamoDB tables against AWS Backup inventory
  2. Resources with zero recovery points across all vaults are flagged
  3. This is a governance/resilience finding rather than a cost finding

Example:

Resource: arn:aws:rds:us-east-1:123456789:db:mydb
Recovery Points: 0
Action: Create backup plan covering this resource

AWS Reference: AWS Backup

Why MEDIUM confidence: Some resources use native snapshots (RDS automated backups) rather than AWS Backup. Verify backup strategy.


AWS Compute Optimizer Integrationโ€‹

CloudWise integrates with AWS Compute Optimizer to provide ML-backed rightsizing recommendations. This is a FREE AWS service that analyzes 14 days of CloudWatch metrics to recommend optimal resource configurations.

Oversized EC2 (Compute Optimizer)โ€‹

Confidence: HIGH
Detection: AWS Compute Optimizer ML analysis, reported only when the estimated saving is at least 5 USD a month
Savings: Exact calculation based on recommended instance type

How it works:

  1. AWS Compute Optimizer analyzes 14 days of CPU, memory, network, and disk metrics
  2. Returns specific recommendations (e.g., "t3.large โ†’ t3.medium")
  3. CloudWise surfaces these with exact savings calculations

Example:

Instance: i-0abc123def456
Current: m5.xlarge ($140.16/month)
Recommended: m5.large ($70.08/month)
Monthly Savings: $70.08 (50%)
Confidence: HIGH (AWS Compute Optimizer)

AWS Reference: Compute Optimizer

Why HIGH confidence: AWS ML model analyzes real utilization. Specific target instance provided.


Oversized EBS (Compute Optimizer)โ€‹

Confidence: HIGH
Detection: AWS Compute Optimizer IOPS/throughput analysis, reported only when the estimated saving is at least 5 USD a month
Savings: Based on recommended volume configuration

How it works:

  1. Compute Optimizer analyzes EBS IOPS and throughput patterns
  2. Recommends optimal volume type and size
  3. Exact savings calculated from current vs. recommended cost

AWS Reference: Compute Optimizer - EBS


Over-Provisioned Lambda (Compute Optimizer)โ€‹

Confidence: HIGH
Detection: AWS Compute Optimizer memory analysis, reported only when the estimated saving is at least 5 USD a month
Savings: Based on recommended memory configuration

How it works:

  1. Compute Optimizer analyzes Lambda memory utilization
  2. Recommends right-sized memory allocation
  3. Lower memory = lower cost per invocation

AWS Reference: Compute Optimizer - Lambda


Oversized RDS (Compute Optimizer)โ€‹

Confidence: HIGH
Detection: AWS Compute Optimizer ML analysis of RDS instance, reported only when the estimated saving is at least 5 USD a month
Savings: Exact calculation based on recommended instance type

How it works:

  1. AWS Compute Optimizer analyzes RDS instance CPU, memory, and I/O metrics
  2. Returns specific downsizing recommendations (e.g., "db.r6g.xlarge โ†’ db.r6g.large")
  3. CloudWise surfaces these with exact savings calculations

Example:

Instance: mydb-production
Current: db.r6g.xlarge ($0.48/hr)
Recommended: db.r6g.large ($0.24/hr)
Monthly Savings: $175.20

AWS Reference: Compute Optimizer - RDS

Why HIGH confidence: AWS Compute Optimizer uses 14+ days of ML-backed analysis. Recommendations include exact pricing.


Reserved Instance & Savings Plans Recommendationsโ€‹

CloudWise provides official AWS recommendations for commitment-based pricing. These are based on 30 days of actual usage and provide exact savings calculations.

API Cost Note: These recommendations are refreshed weekly to minimize Cost Explorer API costs ($0.01/request). Data is cached for 7 days.

EC2 Reserved Instance Opportunityโ€‹

Confidence: HIGH
Detection: AWS Cost Explorer 30-day usage analysis, reported only when the estimated saving is at least 10 USD a month
Savings: Exact AWS calculation (typically 30-40%)

How it works:

  1. AWS Cost Explorer analyzes 30 days of EC2 usage patterns
  2. Identifies consistent usage that would benefit from RI commitment
  3. Returns specific purchase recommendations with exact savings

Example:

Recommendation: Purchase 2x t3.large 1-Year No Upfront RI
Region: us-east-1
Monthly Savings: $42.40 (37% savings)
Break-even: 7 months

AWS Reference: EC2 Reserved Instances

Why HIGH confidence: AWS official recommendation based on actual usage patterns.


RDS Reserved Instance Opportunityโ€‹

Confidence: HIGH
Detection: AWS Cost Explorer 30-day usage analysis, reported only when the estimated saving is at least 10 USD a month
Savings: Exact AWS calculation

How it works:

  1. Analyzes RDS instance usage patterns
  2. Recommends RI purchases for consistent database workloads
  3. Includes engine-specific recommendations (MySQL, PostgreSQL, etc.)

AWS Reference: RDS Reserved Instances


Compute Savings Plan Opportunityโ€‹

Confidence: HIGH
Detection: AWS Cost Explorer 30-day usage analysis, reported only when the estimated saving is at least 10 USD a month
Savings: Exact AWS calculation (typically 20-30%)

Note: An EC2 Reserved Instance, a Compute Savings Plan and an EC2 Instance Savings Plan can price the same EC2 hours, so CloudWise shows only the largest of the three. That finding names the others and what they would save.

How it works:

  1. Analyzes all compute usage (EC2, Fargate, Lambda)
  2. Recommends hourly commitment level
  3. Compute SP covers all regions and instance families (most flexible)

Example:

Recommendation: $10/hour Compute Savings Plan (1-Year)
Monthly Commitment: $7,300/month
Current On-Demand: $9,500/month
Monthly Savings: $2,200 (23%)
Coverage: EC2, Fargate, Lambda (all regions)

AWS Reference: Savings Plans


EC2 Instance Savings Plan Opportunityโ€‹

Confidence: HIGH
Detection: AWS Cost Explorer 30-day usage analysis, reported only when the estimated saving is at least 10 USD a month
Savings: Higher than Compute SP (locked to instance family/region)

Note: EC2 Instance Savings Plans trade flexibility for a deeper discount. CloudWise shows AWS's recommendation as returned; it applies no extra savings-percentage filter.

AWS Reference: Savings Plans


Unused Reserved Instanceโ€‹

Status: Currently doesn't run. For uploaded CUR files, see cur_unused_reservation. Planned detection: RI utilization < 20% over 30 days, with an unused fee of at least $10/month.

What to do: Determine if the low utilization is permanent (migrate fleet, sell RI) or temporary (workload scaling back up). Standard RIs can be sold on the AWS Reserved Instance Marketplace. Convertible RIs can be exchanged for a different type.

AWS Reference: Reserved Instances in Amazon EC2


Expiring Reserved Instanceโ€‹

Confidence: HIGH Detection: Active EC2 RI with end date within 90 days Savings: Monthly cost delta between current RI rate and on-demand equivalent (cost avoidance)

How it works:

  1. CloudWise reads your active EC2 Reserved Instances weekly
  2. Any RI expiring within 90 days is flagged with an urgency level (URGENT โ‰ค30d / WARNING โ‰ค60d / NOTICE โ‰ค90d). WARNING and NOTICE items are reported only when the reservation costs at least $10/month; URGENT items are always reported
  3. The estimated impact shows what the covered instance(s) would cost at on-demand rates post-expiry

Example:

  • Instance type: r5.4xlarge
  • Reserved Instances: 2 (All Upfront 1-year)
  • Expiry: 22 days from now โ€” URGENT
  • Current RI rate: ~$320/month each
  • On-demand equivalent: ~$960/month each
  • Risk: +$1,280/month if not renewed

Why HIGH confidence: Expiry date is deterministic โ€” describe_reserved_instances returns exact ISO 8601 end timestamps.

What to do: (a) Purchase a replacement RI before expiry, (b) switch to a Compute Savings Plan for more instance-type flexibility, or (c) if the workload is ending, allow expiry and plan to terminate the instance promptly.


Convertible RI Exchange Opportunityโ€‹

Confidence: MEDIUM Detection: Convertible EC2 RI on a previous-generation instance family where a current-gen exchange is available Savings: On-demand price differential between old and new instance type ร— quantity

How it works:

  1. CloudWise identifies active Convertible RIs tied to previous-gen instance families (m4, r4, c5, etc.)
  2. Maps each to the recommended current-gen equivalent (m7i, r7i, c7i, etc.)
  3. If the target type has a lower on-demand reference price, the exchange is flagged
  4. Exchange is free per AWS policy โ€” no financial outlay required
  5. Only reservations costing at least $10/month are flagged; no dollar saving is claimed (the commitment does not go down)

Example:

  • RI type: m4.xlarge Convertible (3-year, active, 18 months remaining)
  • Recommended exchange: m7i.xlarge
  • m4.xlarge on-demand: $0.192/hr โ†’ $140.16/month
  • m7i.xlarge on-demand: $0.1785/hr โ†’ $130.31/month
  • Savings on reference pricing: $9.85/month per RI
  • Exchange cost: $0

Why MEDIUM confidence: Pricing differential relies on public on-demand pricing; actual RI discount rates are calculated by AWS at exchange time and may differ slightly.

What to do: Log in to EC2 Console โ†’ Reserved Instances โ†’ select the RI โ†’ Actions โ†’ Exchange Reserved Instance. Select the target instance type. Review the exchange ratio (AWS may require more than one old RI to match one new RI in some cases). Confirm the exchange โ€” it takes effect immediately.

Note: Graviton-family targets (t4g, m7g, c7g) are not recommended automatically as ARM64 compatibility requires code-level validation.


Expiring Savings Planโ€‹

Confidence: HIGH
Detection: Savings Plan with end date within 90 days
Savings: Monthly commitment amount at risk after expiry

How it works:

  1. We check all active Savings Plans for upcoming expiration dates
  2. Plans expiring within 30 days are flagged as URGENT, within 60 days as WARNING, and within 90 days as NOTICE; WARNING and NOTICE plans are reported only when the monthly commitment is at least $10
  3. Early notification allows time to renew or adjust commitment strategy

Example:

Savings Plan: sp-abc123def456
Type: Compute Savings Plan
Commitment: $500.00/month
Expires: 2026-05-15 (37 days away)
Urgency: WARNING
Action: Renew or purchase replacement SP

AWS Reference: Savings Plans

Why HIGH confidence: Expiration dates are exact metadata from the Savings Plans API. No estimation involved.


Unused Savings Planโ€‹

Confidence: HIGH
Detection: Savings Plan utilization < 20% over 30 days, with at least $10/month of unused commitment
Savings: Unused commitment over the 30-day window

How it works:

  1. We analyze Savings Plan utilization via Cost Explorer over the past 30 days
  2. Plans with utilization below 20% and at least $10 of unused commitment are flagged โ€” the commitment is being paid but not offsetting On-Demand usage
  3. This often indicates workload changes, instance type shifts, or over-commitment

Example:

Savings Plan: sp-abc123def456
Type: EC2 Instance Savings Plan
Commitment: $300.00/month
Utilization: 12%
Unused: $264.00/month
Action: Review commitment strategy

AWS Reference: Savings Plans Utilization

Why HIGH confidence: Utilization data comes directly from AWS Cost Explorer with exact dollar amounts.


Savings Plan Coverage Gapโ€‹

Confidence: MEDIUM
Detection: Significant On-Demand spend not covered by existing Savings Plans
Savings: ~25% of uncovered On-Demand spend (conservative estimate), less what EC2 and SageMaker purchase recommendations already count

How it works:

  1. We analyze Cost Explorer data for compute spend covered vs. uncovered by Savings Plans
  2. Accounts with < 50% SP coverage and at least $100/month uncovered spend are flagged (and the estimated saving must reach $10/month)
  3. Purchasing additional SP commitment could reduce the uncovered portion by 25โ€“40%
  4. With fewer than 7 days of coverage data there is no finding, because a few days can't support a monthly estimate. The scan's data warnings say so

Example:

Total Compute Spend: $2,000/month
SP Coverage: 40% ($800 covered)
Uncovered On-Demand: $1,200/month
Potential SP Savings: $300.00/month (25%)

AWS Reference: Savings Plans Coverage

Why MEDIUM confidence: Savings estimate depends on the SP type/term chosen and actual usage stability. The 25% is conservative.


CUR Unused Reservationโ€‹

Confidence: HIGH
Detection: CUR-based analysis of unused Reserved Instance cost (at least $10 in the latest billing period)
Savings: Unused RI fee from CUR cost breakdown

How it works:

  1. We analyze CUR (Cost and Usage Report) line items for RI amortization
  2. RI fees without corresponding On-Demand equivalent usage indicate waste
  3. This provides more granular insight than the Cost Explorer summary

Example:

RI: ri-abc123def456
Type: m5.xlarge
Amortized Cost: $250.00/month
Utilized: 35%
Waste: $162.50/month
Action: Sell on RI Marketplace or modify

AWS Reference: Understanding CUR Reserved Instance Data

Why HIGH confidence: CUR data provides exact line-item cost allocation. No estimation needed.


CUR Savings Plan Wasteโ€‹

Confidence: MEDIUM
Detection: CUR-based analysis of Savings Plan commitment not fully utilized (at least $10 unused in the latest billing period)
Savings: Unused SP commitment from CUR breakdown

How it works:

  1. We analyze CUR line items for Savings Plan amortization and coverage
  2. SP commitment cost without matching covered usage indicates waste
  3. Provides daily granularity on SP utilization trends

Example:

Savings Plan: sp-abc123def456
Type: Compute SP
Monthly Commitment: $400.00
Utilized: 65%
Waste: $140.00/month
Action: Adjust workloads or reduce commitment at renewal

AWS Reference: Understanding CUR Savings Plans

Why MEDIUM confidence: CUR data is exact but utilization patterns may fluctuate month to month.


IPv4 Address Optimizationโ€‹

Since February 2024, AWS bills every public IPv4 address, attached or not, at an hourly price (see the AWS VPC pricing page).

EIP on Stopped Instanceโ€‹

Confidence: HIGH
Detection: Elastic IP attached to stopped EC2 instance
Savings: $3.65/month per EIP

How it works:

  1. Identifies Elastic IPs attached to stopped instances
  2. AWS bills every public IPv4 address, attached or not, at an hourly price (see the AWS VPC pricing page). A stopped instance does not use its address, so the charge buys nothing
  3. Release the EIP to save its monthly charge per IP (a detached EIP is still billed)

Example:

Elastic IP: 52.123.45.67
Attached to: i-0abc123 (stopped)
Monthly Savings: $3.65
Action: Release EIP or terminate instance

AWS Reference: VPC Pricing - Public IPv4


Multiple EIPs per Instanceโ€‹

Confidence: HIGH
Detection: Instance with more than one Elastic IP attached
Savings: $3.65/month per extra EIP

How it works:

  1. Identifies instances with multiple EIPs attached
  2. AWS bills every public IPv4 address, attached or not, at an hourly price (see the AWS VPC pricing page). Multiple public IPs is usually an anti-pattern
  3. Consolidate to single EIP to save the monthly charge per extra IP

AWS Reference: VPC Pricing


Management Detectorsโ€‹

CloudWatch Log Group Without Retentionโ€‹

Confidence: MEDIUM (groups above 1 GB stored) / LOW (smaller groups)
Detection: CloudWatch log groups with no retention policy (logs stored forever)
Savings: Varies based on log volume ($0.03/GB-month for storage)

How it works:

  1. We identify all CloudWatch log groups without a retention policy
  2. Log groups without retention store logs indefinitely (growing storage costs)
  3. Larger log groups (>1 GB) are flagged with higher confidence, and savings are estimated as a fixed share of the log group's storage cost

Example:

Log Group: /aws/lambda/my-function
Size: 5.2GB
Retention: Never expires (no policy)
Monthly Savings: $0.16/month (storage) + preventing future growth
Recommendation: Set 30-90 day retention policy

AWS Reference: CloudWatch Pricing

Why flagged: Without retention, logs grow indefinitely. Most logs are only useful for 30-90 days.


Excessive Log Group Retentionโ€‹

Confidence: MEDIUM / HIGH (HIGH when >10 GB stored)
Detection: Log group with retention โ‰ฅ 365 days and more than 0.1 GB stored data
Savings: Proportional to stored data ร— retention reduction factor

How it works:

  1. We check each log group's retention policy via the CloudWatch Logs API
  2. Log groups with retention of 365 days or more and more than 0.1 GB of stored data are flagged
  3. Savings are calculated based on reducing retention to 30 days: savings = storage_cost ร— (1 โˆ’ 30/current_retention_days)

Example:

Log Group: /aws/lambda/api-handler
Retention: 365 days
Stored Data: 50 GB
Current Storage Cost: $1.50/month
Recommended Retention: 30 days
Estimated Savings: $1.38/month (92% reduction)

Recommended Action: Reduce retention to 30 days. For compliance, consider S3 export + Glacier instead.

aws logs put-retention-policy \
--log-group-name '/aws/lambda/api-handler' \
--retention-in-days 30
Agentic Tier โ€” Automated Fix Available

Click Fix This to reduce retention automatically, with full rollback capability.

AWS Reference: CloudWatch Logs Pricing | PutRetentionPolicy API

Why flagged: 99% of log searches target the last 7 days. Storing 365+ days of logs in CloudWatch Logs is rarely cost-effective compared to S3 + Glacier for long-term archival.


Empty Log Groupโ€‹

Confidence: LOW
Detection: Log group with 0 bytes stored and no ingestion for at least 30 days
Savings: $0.00 (hygiene โ€” quota reclamation)

How it works:

  1. We check each log group's stored bytes from the CloudWatch Logs API
  2. Log groups with exactly 0 bytes are evaluated for staleness
  3. If the log group was created at least 30 days ago (or last received logs at least 30 days ago), it's flagged
  4. No savings โ€” empty log groups are free. This is a housekeeping detector.

Example:

Log Group: /aws/lambda/deleted-processor
Stored Data: 0 bytes
Created: 180 days ago
Last Event: Never received logs
Status: Orphaned (Lambda function was deleted)

Recommended Action: Delete the empty log group to reduce clutter and free quota (see the quota page below).

aws logs delete-log-group \
--log-group-name '/aws/lambda/deleted-processor'
Agentic Tier โ€” Automated Fix Available

Click Fix This to delete the empty log group automatically. CloudWatch will recreate the log group automatically if the associated service resumes logging.

AWS Reference: CloudWatch Logs Quotas

Why flagged: While free, orphaned log groups consume regional quota. Accounts with many microservices, Lambda functions, or ECS tasks can accumulate hundreds of empty log groups over time.


Old Log Groupโ€‹

Confidence: MEDIUM
Detection: CloudWatch log group with > 0.5 GB and no new log events for more than 90 days
Savings: Storage cost at $0.03/GB/month

How it works:

  1. We check the last ingestion timestamp for each log group
  2. Groups with no new events for more than 90 days and stored volume > 0.5 GB are flagged
  3. Stale data should be archived to S3 or deleted to reduce storage costs

Example:

Log Group: /aws/lambda/old-data-processor
Size: 2.3 GB
Last Event: 120 days ago
Monthly Savings: $0.07
Action: Export to S3 and set retention, or delete

AWS Reference: CloudWatch Logs Pricing

Why MEDIUM confidence: Some log groups are retained for audit/compliance. Verify retention requirements before deleting.


Unused CloudWatch Dashboardโ€‹

Confidence: MEDIUM
Detection: CloudWatch dashboard beyond the first 3 free dashboards, not modified in more than 90 days
Savings: $3.00/month per unused dashboard

How it works:

  1. AWS provides 3 free CloudWatch dashboards โ€” additional dashboards cost $3.00/month each
  2. We identify paid-tier dashboards that haven't been modified in more than 90 days
  3. Dashboards that haven't been updated may no longer reflect the current infrastructure

Example:

Dashboard: legacy-monitoring
Last Modified: 145 days ago
Tier: Paid (dashboard #5 of 8)
Monthly Savings: $3.00
Action: Delete or consolidate into active dashboards

AWS Reference: CloudWatch Dashboards Pricing

Why MEDIUM confidence: Dashboards may still be viewed even if not modified. The "not modified" heuristic may not capture viewing activity.


CloudTrail Optimizationโ€‹

Duplicate CloudTrail Trailsโ€‹

Confidence: HIGH
Detection: Two or more logging trails, owned by this account, with identical region scope and event selectors
Savings: $2.00 per 100,000 events for duplicate trails (reported as a $2.00/month floor; event volume is not available per trail)

How it works:

  1. Analyzes CloudTrail configurations across the account, including multi-region trails homed in other regions
  2. Skips trails that are not logging and organization trails owned by another account
  3. Identifies trails with identical event selectors and reports each duplicate once, from its home region's scan
  4. Recommends consolidation to single trail

Example:

Trail 1: my-trail (logging all management events)
Trail 2: security-trail (logging all management events)
Issue: Duplicate logging of same events
Savings: at least $2.00/month; scales with event volume

AWS Reference: CloudTrail Pricing


CloudTrail S3 Without Lifecycleโ€‹

Confidence: MEDIUM
Detection: CloudTrail S3 bucket without lifecycle policy that holds logs over 365 days old (a year)
Savings: S3 Standard GB already stored a year ago ร— $0.023/GB-month (what a 365-day expiration frees)

How it works:

  1. Identifies S3 buckets receiving CloudTrail logs from trails homed in the scanned region
  2. Checks for lifecycle policy to transition/expire old logs
  3. Reads the bucket's S3 Standard size today and a year ago (CloudWatch BucketSizeBytes, in the bucket's own region); CloudTrail only appends, so data present a year ago is over a year old now
  4. Recommends lifecycle policy to reduce storage costs. The 90-day Glacier transition is not counted in the saving: S3 Lifecycle skips objects under 128 KB by default, which most CloudTrail log files are
  5. A bucket with no year-old data, or whose size cannot be read, is not flagged; neither is one whose estimated saving is below $0.01/month

AWS Reference: S3 Lifecycle


๐Ÿค– Copilot Integrationโ€‹

The Copilot uses waste detection data to provide intelligent recommendations:

User: "What can I do to reduce my AWS costs?"

Copilot (illustrative): "I found 3 waste items worth reviewing that could save you $82.40/month:

1. **Unattached EBS Volume** (vol-abc123) - 500GB gp3 not attached to any instance
Savings: $40/month
Action: `aws ec2 delete-volume --volume-id vol-abc123`

2. **Idle NAT Gateway** (nat-xyz789) - No traffic for 7 days
Savings: the base monthly charge
Action: Delete via VPC console if not needed

3. **2 Old EBS Snapshots** - Over 90 days old
Savings: $10/month
Action: Review and delete if no longer needed for recovery

Would you like me to explain any of these in more detail?"

The Copilot answers from your scan findings. Where a finding carries an explanation, a risk note or an action command, you can ask the Copilot about it.


๐Ÿ“š AWS Pricing Referencesโ€‹

ServiceOfficial Pricing URL
EC2https://aws.amazon.com/ec2/pricing/on-demand/
EBShttps://aws.amazon.com/ebs/pricing/
RDShttps://aws.amazon.com/rds/pricing/
DynamoDBhttps://aws.amazon.com/dynamodb/pricing/
VPC (EIP, NAT)https://aws.amazon.com/vpc/pricing/
ELBhttps://aws.amazon.com/elasticloadbalancing/pricing/
Lambdahttps://aws.amazon.com/lambda/pricing/
S3https://aws.amazon.com/s3/pricing/
ECRhttps://aws.amazon.com/ecr/pricing/
EFShttps://aws.amazon.com/efs/pricing/
FSxhttps://aws.amazon.com/fsx/pricing/
Gluehttps://aws.amazon.com/glue/pricing/
CloudWatchhttps://aws.amazon.com/cloudwatch/pricing/
CloudTrailhttps://aws.amazon.com/cloudtrail/pricing/
Route53https://aws.amazon.com/route53/pricing/
CloudFronthttps://aws.amazon.com/cloudfront/pricing/
Global Acceleratorhttps://aws.amazon.com/global-accelerator/pricing/
Kinesishttps://aws.amazon.com/kinesis/data-streams/pricing/
EMRhttps://aws.amazon.com/emr/pricing/
MSKhttps://aws.amazon.com/msk/pricing/
Amazon MQhttps://aws.amazon.com/amazon-mq/pricing/
ElastiCachehttps://aws.amazon.com/elasticache/pricing/
Redshifthttps://aws.amazon.com/redshift/pricing/
OpenSearchhttps://aws.amazon.com/opensearch-service/pricing/
Neptunehttps://aws.amazon.com/neptune/pricing/
DocumentDBhttps://aws.amazon.com/documentdb/pricing/
SageMakerhttps://aws.amazon.com/sagemaker/pricing/
WorkSpaceshttps://aws.amazon.com/workspaces/pricing/
Lightsailhttps://aws.amazon.com/lightsail/pricing/
Elastic Beanstalkhttps://aws.amazon.com/elasticbeanstalk/pricing/
ECShttps://aws.amazon.com/ecs/pricing/
Step Functionshttps://aws.amazon.com/step-functions/pricing/
AppSynchttps://aws.amazon.com/appsync/pricing/
Transfer Familyhttps://aws.amazon.com/aws-transfer-family/pricing/
API Gatewayhttps://aws.amazon.com/api-gateway/pricing/
Secrets Managerhttps://aws.amazon.com/secrets-manager/pricing/
KMShttps://aws.amazon.com/kms/pricing/
Compute Optimizerhttps://aws.amazon.com/compute-optimizer/
Savings Planshttps://aws.amazon.com/savingsplans/

๐Ÿ”ง Threshold Configurationโ€‹

CloudWise uses a two-tier threshold system to give you complete control over waste detection and notifications:

Understanding the Two Threshold Systemsโ€‹

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ TIER 1: DETECTION PHASE โ”‚
โ”‚ Per-Account Setting (aws-accounts table) โ”‚
โ”‚ โ”‚
โ”‚ AWS Account Scan โ†’ Find $0.20 EBS gp2โ†’gp3 migration opportunity โ”‚
โ”‚ Check: Is $0.20 >= min_waste_threshold_usd ($0.01)? โ”‚
โ”‚ Result: YES โ†’ Store in database โœ“ โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
โ†“
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ TIER 2: NOTIFICATION PHASE โ”‚
โ”‚ Per-User Setting (users table โ†’ notification_preferences) โ”‚
โ”‚ โ”‚
โ”‚ Check user's total waste findings โ†’ Total: $45.00/month โ”‚
โ”‚ Check: Is $45.00 >= min_savings_threshold ($50.00)? โ”‚
โ”‚ Result: NO โ†’ Don't send email notification โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Tier 1: Account-Level Detection Thresholdโ€‹

These settings control what waste items are discovered and stored in the database during scans.

SettingDefaultRangeDescription
cloudwatch_enabledtrueon/offEnable CloudWatch metrics for accurate utilization analysis
min_waste_threshold_usd$0.01minimum 0 (no enforced upper limit in the account edit form; the waste-settings API sets one)Minimum monthly savings to include a waste item

Where to configure:

  1. Go to AWS Accounts page (Settings โ†’ AWS Accounts)
  2. Click the Edit (pencil) icon on any connected account
  3. Scroll to Waste Detection Settings section
  4. Adjust the settings and click Update Account

Impact: Items whose (reconciled) monthly savings fall below min_waste_threshold_usd are not stored, except governance and security checks, which are always kept. With the default of $0.01, virtually all waste is captured.

Tier 2: User-Level Notification Thresholdโ€‹

These settings control when email and Slack notifications are sent about waste findings.

SettingDefaultRangeDescription
min_savings_threshold$10.00$0 and up (the form caps the upper end)Minimum total monthly savings to trigger notification
frequencydailyimmediate / daily / weekly / disabledHow often to send waste summary notifications
high_priority_immediatetrueon/offSend immediately for high-priority findings
high_priority_threshold$50.00$0 and up (the form caps the upper end)Monthly savings threshold for high priority

Slack notifications: Waste detection can send summaries to a dedicated Slack channel. The message includes total findings, potential monthly savings, top 5 resources, and a View All Waste button. (The button currently links to /waste, which has no page; open Workspace โ†’ Findings instead.)

To enable Slack for waste detection:

  1. Go to Settings โ†’ Notifications
  2. Find the Waste Detection Alerts card
  3. Under Slack Channel, turn on Send to Slack and paste an https://hooks.slack.com/ webhook URL
  4. Click Test Connection

See the Slack Integration Guide for full multi-channel setup.

Where to configure:

  1. Go to Settings โ†’ Notifications
  2. Find the Waste Detection Alerts card
  3. Adjust your notification preferences
  4. Click Save Changes

Impact: Emails are only sent if total waste across all accounts exceeds min_savings_threshold.

Use CaseDetection ThresholdNotification ThresholdNotes
Catch Everything (default)$0.01$10.00See all waste, get notified for meaningful amounts
High-Volume Accounts$1.00$50.00Reduce noise from micro-optimizations
Enterprise$5.00$100.00Focus only on significant waste
Cost-Conscious Startup$0.01$1.00Every penny counts

Configuration Examplesโ€‹

Example 1: Default (Catch Everything)

Account Detection:   $0.01  โ†’ Stores $0.05 EBS snapshot, $2.30 idle EC2
User Notification: $10.00 โ†’ Email sent if total waste > $10/month

Example 2: Enterprise (Reduce Noise)

Account Detection:   $5.00  โ†’ Ignores $0.05 EBS snapshot, stores $2.30 idle EC2
User Notification: $100.00 โ†’ Email only for significant waste

CloudWatch Metrics Toggleโ€‹

With CloudWatch Metrics enabled (recommended), CloudWise reads utilization metrics and can run idle and over-provisioned checks. With it disabled, those utilization-based checks do not run: you only get configuration-based checks such as unattached volumes, old snapshots and missing lifecycle policies.

When to disable CloudWatch:

  • Accounts with strict IAM policies preventing CloudWatch access
  • Reducing CloudWatch API calls

๐Ÿ”ง Additional Configurationโ€‹

Waste detection includes additional thresholds configurable by CloudWise support:

SettingDefaultDescription
ec2_idle_cpu_threshold5%CPU below this = idle
ec2_idle_days14Days to analyze
rds_idle_days14Days to analyze connections
snapshot_age_days90Snapshots at least this old are flagged

Contact support to adjust these for your organization.


๐Ÿ’ก Best Practicesโ€‹

  1. Start with HIGH confidence items - The strongest signals; still review each one
  2. Review MEDIUM confidence items - Check for exceptions before acting
  3. Use LOW confidence items for cleanup - Not cost savings, but good hygiene
  4. Enable CloudWatch metrics - Required for idle detection
  5. Tag your resources - Helps identify ownership for waste items
  6. Schedule regular reviews - Check your findings on a regular rhythm