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.
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).
๐ฏ 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:
| Situation | Effect 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 Plans | Already discounted |
| EDP or private pricing agreements | Blanket discount |
| Credits | The 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:
| Label | Meaning |
|---|---|
| Checked against your AWS bill | Reconciled. The figure is capped at real billed cost. |
| AWS bills $0 for this resource | Confirmed free-tier or fully credited. Nothing to recover. |
| Estimated at list price โ enable resource IDs | Your CUR has no per-resource detail yet. |
| Estimated at list price โ covered by an RI/Savings Plan/credit | Your effective rate is lower than list, by an amount we can't see. |
| Estimated at list price โ not enough billing history | The resource is too new to extrapolate from safely. |
| Estimated at list price โ no line in your Cost & Usage Report yet | New resources take a day or two to appear in the report. |
| Estimated at list price โ Container Insights metrics are billed for your whole account | The 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.
| Category | Waste types | Description |
|---|---|---|
| Compute Optimization | 35 | EC2 (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 Optimization | 27 | EBS (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 Optimization | 41 | RDS (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 Optimization | 19 | Elastic 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 Optimization | 12 | Lambda (unused, over-provisioned memory, outdated runtimes, Compute Optimizer rightsizing), API Gateway, AppSync, Step Functions |
| Container Optimization | 5 | ECS (idle services, oversized tasks and memory, missing auto scaling, Container Insights overhead) |
| Analytics Optimization | 22 | EMR, Kinesis Data Streams, Kinesis Data Firehose, MSK, Glue, Amazon MQ |
| ML/AI Optimization | 6 | SageMaker Notebooks, Endpoints, Oversized Endpoints, Stopped Notebook Storage, Previous-Gen Instances, SageMaker Savings Plans recommendations |
| Management & Operations | 7 | CloudWatch Logs, CloudTrail, CloudWatch Dashboards |
| Security & Compliance | 12 | Secrets 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 Type | Why it can't be validated |
|---|---|
appsync_idle_cache | The smallest resource that triggers it costs more to run for the check's window than our test budget allows |
aurora_io_optimization_opportunity | The smallest resource that triggers it costs more to run for the check's window than our test budget allows |
aurora_serverless_opportunity | The smallest resource that triggers it costs more to run for the check's window than our test budget allows |
convertible_ri_exchange_opportunity | Needs a Reserved Instance or Savings Plan commitment, or AWS's own purchase recommendation |
cur_savings_plan_waste | Needs a Reserved Instance or Savings Plan commitment, or AWS's own purchase recommendation |
cur_unused_reservation | Needs a Reserved Instance or Savings Plan commitment, or AWS's own purchase recommendation |
documentdb_extended_support_cost | AWS no longer allows the configuration it looks for |
emr_over_provisioned | The smallest resource that triggers it costs more to run for the check's window than our test budget allows |
expiring_reserved_instance | Needs a Reserved Instance or Savings Plan commitment, or AWS's own purchase recommendation |
expiring_savings_plan | Needs a Reserved Instance or Savings Plan commitment, or AWS's own purchase recommendation |
glue_catalog_bloat | The smallest resource that triggers it costs more to run for the check's window than our test budget allows |
high_lcu_cost_alb | The smallest resource that triggers it costs more to run for the check's window than our test budget allows |
idle_global_accelerator | The smallest resource that triggers it costs more to run for the check's window than our test budget allows |
idle_redshift | The smallest resource that triggers it costs more to run for the check's window than our test budget allows |
idle_transfer_no_activity | The smallest resource that triggers it costs more to run for the check's window than our test budget allows |
idle_transfer_server | The smallest resource that triggers it costs more to run for the check's window than our test budget allows |
idle_transfer_web_app | The smallest resource that triggers it costs more to run for the check's window than our test budget allows |
idle_workspace | The smallest resource that triggers it costs more to run for the check's window than our test budget allows |
kinesis_on_demand_downgrade | The smallest resource that triggers it costs more to run for the check's window than our test budget allows |
long_running_emr | The smallest resource that triggers it costs more to run for the check's window than our test budget allows |
oversized_redshift | The smallest resource that triggers it costs more to run for the check's window than our test budget allows |
redshift_concurrency_scaling_waste | The smallest resource that triggers it costs more to run for the check's window than our test budget allows |
redshift_legacy_dc2 | AWS no longer allows the configuration it looks for |
redshift_no_pause | The smallest resource that triggers it costs more to run for the check's window than our test budget allows |
redshift_wlm_over_provisioned | The smallest resource that triggers it costs more to run for the check's window than our test budget allows |
ri_opportunity_ec2 | Needs a Reserved Instance or Savings Plan commitment, or AWS's own purchase recommendation |
ri_opportunity_elasticache | Needs a Reserved Instance or Savings Plan commitment, or AWS's own purchase recommendation |
ri_opportunity_opensearch | Needs a Reserved Instance or Savings Plan commitment, or AWS's own purchase recommendation |
ri_opportunity_rds | Needs a Reserved Instance or Savings Plan commitment, or AWS's own purchase recommendation |
ri_opportunity_redshift | Needs a Reserved Instance or Savings Plan commitment, or AWS's own purchase recommendation |
s3_high_request_and_transfer_cost | The smallest resource that triggers it costs more to run for the check's window than our test budget allows |
s3_no_default_encryption | AWS no longer allows the configuration it looks for |
savings_plan_coverage_gap | Needs a Reserved Instance or Savings Plan commitment, or AWS's own purchase recommendation |
sp_opportunity_compute | Needs a Reserved Instance or Savings Plan commitment, or AWS's own purchase recommendation |
sp_opportunity_ec2 | Needs a Reserved Instance or Savings Plan commitment, or AWS's own purchase recommendation |
sp_opportunity_sagemaker | Needs a Reserved Instance or Savings Plan commitment, or AWS's own purchase recommendation |
underutilized_redshift | The smallest resource that triggers it costs more to run for the check's window than our test budget allows |
unused_accelerator | The smallest resource that triggers it costs more to run for the check's window than our test budget allows |
unused_reserved_instance | Needs a Reserved Instance or Savings Plan commitment, and currently doesn't run. For uploaded CUR files, see cur_unused_reservation |
unused_savings_plan | Needs a Reserved Instance or Savings Plan commitment, or AWS's own purchase recommendation |
unused_transfer_protocol | The smallest resource that triggers it costs more to run for the check's window than our test budget allows |
workspaces_pool_overprovisioned_capacity | The smallest resource that triggers it costs more to run for the check's window than our test budget allows |
workspaces_windows_license_optimization | The 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โ
| Feature | Air-Gapped Mode | Online Mode |
|---|---|---|
| AWS Connection | None required | IAM role required |
| Detectors Available | 158 of 186 | All 186 |
| Daily scans and alerts | โ | โ |
| Compute Optimizer | โ | โ |
| RI/SP Recommendations | โ | โ |
| Ideal For | Evaluation, compliance | Production 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 Type | Confidence | Detection |
|---|---|---|
idle_ec2 | MEDIUM | Hourly 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_ebs | MEDIUM | Stopped instance with EBS volumes, priced per attached volume |
unused_lambda | HIGH | Zero invocations for 30 days, on a function last changed at least 30 days ago |
over_provisioned_lambda | MEDIUM | Memory of 512 MB or more with average duration under 500 ms over 30 days |
lambda_provisioned_concurrency_idle | HIGH | Provisioned Concurrency utilization < 10% for 14 days |
lambda_excessive_timeout | MEDIUM | Timeout โฅ 10ร average duration and above 3ร the longest run |
lambda_arm64_migration | LOW | x86_64 function with ARM64-compatible runtime |
oversized_ecs_task | MEDIUM | Fargate service whose busiest task stayed under 20% CPU for 7 days |
idle_ecs_service | HIGH | Tasks desired but none running, no rollout in progress ($0: hygiene) |
ecs_no_autoscaling | MEDIUM | Fargate service with 2+ running tasks and no Application Auto Scaling |
ecs_container_insights_waste | HIGH | Container Insights on a dev/test cluster, a cluster with fewer than 3 active services, or one with no running tasks |
oversized_ecs_memory | MEDIUM | Peak memory under 40% of the task memory for 7 days |
idle_sagemaker_notebook | MEDIUM | InService with no start, change or Jupyter log activity in the window |
idle_sagemaker_endpoint | HIGH | Zero invocations for 7 days |
stopped_sagemaker_notebook_storage | HIGH | Stopped notebook with an ML storage volume |
previous_gen_sagemaker_instance | HIGH | Previous-gen instance type (ml.m4/c4) |
idle_workspace | HIGH | No user connection for more than 30 days |
oversized_workspace | MEDIUM | Peak CPU and memory fit the next smaller bundle, and the price difference is at least 15 USD a month |
workspaces_autostop_opportunity | HIGH | AlwaysOn WorkSpace with low usage โ switch to AutoStop billing, with an estimated saving of at least 10 USD a month |
workspaces_pool_overprovisioned_capacity | MEDIUM | WorkSpaces 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_optimization | MEDIUM | Windows license-included desktops eligible for BYOL or Linux alternatives |
idle_lightsail | HIGH | Stopped, or CPU < 5% for 14 days (75% coverage) |
lightsail_unattached_static_ip | HIGH | Allocated but not attached to any instance |
lightsail_unattached_disk | HIGH | Block storage disk not attached to any instance |
lightsail_old_snapshot | MEDIUM | Manual snapshot older than 90 days |
lightsail_idle_load_balancer | HIGH | Load balancer with zero healthy instances |
lightsail_idle_database | HIGH | Database with CPU < 5% and fewer than 1 connection on average over 14 days |
idle_beanstalk | HIGH | Unhealthy environment (Red health) |
beanstalk_idle_traffic | HIGH | Zero load-balancer (or enhanced-health) requests for 14 days |
beanstalk_unnecessary_alb | HIGH | Load-balanced with single instance (min=max=1) |
beanstalk_previous_gen_instances | HIGH | Using previous-generation instance types (t2, m4, c4, etc.) |
beanstalk_over_provisioned | MEDIUM | Instance CPU <25% for 14 days across 2+ instances |
beanstalk_orphaned_rds | HIGH | RDS left behind by a terminated Beanstalk environment, with zero connections for 14 days |
eks_extended_support_cost | HIGH | EKS cluster version within 90 days of Extended Support or already in it |
Storage Optimization (26 Waste Types)โ
| Waste Type | Confidence | Detection |
|---|---|---|
unattached_ebs | HIGH | Volume status = available |
old_ebs_snapshot | MEDIUM | Snapshot at least 90 days old; never the newest snapshot of a volume that still exists |
orphaned_ebs_snapshot | HIGH | Source volume deleted โ snapshot serves no recovery purpose |
ami_orphaned_snapshot | HIGH | AMI deregistered โ backing snapshot no longer needed |
gp2_migration | HIGH | Volume type = gp2 |
over_provisioned_iops | MEDIUM | Provisioned IOPS underutilized |
no_lifecycle_policy | MEDIUM | S3 bucket without lifecycle rules |
incomplete_multipart | HIGH | Multipart uploads abandoned for more than 7 days |
s3_rapid_growth | LOW | Bucket 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_class | LOW | S3 Standard of at least 50 GB and at least 90% of total storage, no Intelligent-Tiering or lifecycle transitions |
s3_empty_bucket | LOW | Empty bucket (0 objects, 0 bytes) at least 30 days old, excluding infra buckets |
s3_high_request_and_transfer_cost | LOW | Non-storage costs (transfer + API requests) exceed storage costs (CUR-based) |
idle_efs | HIGH | File system with no mount targets |
no_lifecycle_efs | MEDIUM | EFS filesystem without lifecycle policy |
idle_fsx | HIGH | Zero read and write bytes for 7 days |
oversized_fsx | MEDIUM | Storage utilization below 40% of provisioned capacity |
fsx_throughput_overprovisioned | MEDIUM | Busiest hour below 30% of provisioned throughput (Windows/ONTAP/OpenZFS) |
old_fsx_backup | MEDIUM | Manual FSx backup at least 90 days old |
ecr_no_lifecycle_policy | MEDIUM | ECR repo without lifecycle policy |
old_ecr_images | MEDIUM | Images pushed more than 90 days ago, when old plus untagged images exceed 0.5 GB |
untagged_ecr_images | HIGH | Untagged ECR images above 0.1 GB (when Old ECR Images does not already report them) |
old_backup | MEDIUM | Recovery point at least 180 days old, without a compliance hold |
redundant_backup | MEDIUM | Two backup plans backed up the same resource within 1 hour; the point that expires sooner is flagged |
backup_no_lifecycle_tiering | LOW | Backup plan rule that keeps backups 90+ days with no cold storage transition |
stale_backup_plan_assignment | HIGH | Backup plan with explicit resource selections that has not run for 30+ days |
backup_copy_policy_overreach | LOW | More than 10 copy jobs and 100 GB to one destination vault in 90 days |
Database Optimization (37 Waste Types)โ
| Waste Type | Confidence | Detection |
|---|---|---|
idle_rds | HIGH | Zero connections for 14 days |
old_rds_snapshot | MEDIUM | Manual snapshot at least 90 days old |
aurora_io_optimization_opportunity | HIGH | Aurora Standard cluster where I/O charges >25% of total spend over 30 days |
aurora_extended_support_cost | HIGH | Aurora cluster running EOL engine version under Extended Support |
aurora_serverless_opportunity | MEDIUM | Provisioned 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_opportunity | MEDIUM | Aurora 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_cost | HIGH | Non-Aurora RDS MySQL/PostgreSQL instance in warning window or active Extended Support surcharge state |
idle_dynamodb | HIGH | Zero consumed capacity for 14 days, on a table at least 14 days old |
dynamodb_no_autoscaling | MEDIUM | Provisioned mode without auto-scaling |
over_provisioned_dynamodb | MEDIUM | Read and write capacity utilization both < 20% over 14 days, on a table at least 14 days old |
idle_elasticache | HIGH | Zero connections for 7 days |
oversized_elasticache | MEDIUM | ElastiCache node over-provisioned relative to usage |
elasticache_extended_support_cost | HIGH | ElastiCache Redis OSS 4/5/6 cluster in warning window or active Extended Support surcharge state (Valkey and Memcached have no Extended Support) |
elasticache_replication_waste | HIGH | Non-production cluster with unnecessary replicas |
elasticache_engine_migration | HIGH | Redis OSS / Memcached cluster eligible for lower-priced Valkey |
elasticache_serverless_optimization | MEDIUM | Node-based cluster with spiky traffic better suited for Serverless |
elasticache_data_tiering_opportunity | MEDIUM | Memory-only R5/R6g/R7g cluster eligible for R6gd data tiering |
idle_redshift | HIGH | Zero connections for 7 days on a cluster at least that old, costing at least $0.01 a month |
underutilized_redshift | MEDIUM | Average CPU above zero and below 10%, non-zero connections, costing at least $0.01 a month |
oversized_redshift | MEDIUM | Average CPU below 15% on an RA3 or DC2 cluster with a smaller node type, saving at least $0.01 a month |
redshift_no_pause | MEDIUM | No pause schedule, at least 40% zero-connection hours, costing at least $0.01 a month |
redshift_legacy_dc2 | LOW | DC2 node type, costing at least $0.01 a month, recommend RA3 migration |
redshift_wlm_over_provisioned | MEDIUM | WLM queue near-empty, under 50% of 5 concurrency slots used |
redshift_concurrency_scaling_waste | MEDIUM | Concurrency scaling exceeds the free 3,600 seconds a day, costing at least $0.01 a month |
idle_opensearch | MEDIUM | Over 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_cost | HIGH | OpenSearch/legacy Elasticsearch domain in warning window or active legacy support surcharge state |
oversized_opensearch | MEDIUM | Hourly 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_overprovisioned | MEDIUM | Free storage > 60% with flat growth (< 0.1 GB/day) |
ri_opportunity_opensearch | HIGH | On-demand domain eligible for Reserved Instance savings |
idle_neptune | HIGH | Zero requests for 7 days |
neptune_serverless_opportunity | HIGH | Provisioned 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_neptune | MEDIUM | Hourly 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_snapshot | HIGH | Manual snapshot at least 90 days old |
idle_documentdb | HIGH | Zero connections and combined read+write IOPS under 10/s for 7 days |
old_documentdb_snapshot | MEDIUM | Manual snapshot at least 90 days old |
overprovisioned_documentdb | MEDIUM | Hourly 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_cost | HIGH | DocumentDB cluster in warning window or active Extended Support surcharge state |
Network Optimization (19 Waste Types)โ
| Waste Type | Confidence | Detection |
|---|---|---|
unattached_eip | HIGH | No instance or ENI attached |
eip_on_stopped_instance | HIGH | EIP attached to stopped instance |
multiple_eips_per_instance | HIGH | Running 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_gateway | HIGH | Zero bytes transferred for 7 days |
idle_load_balancer | HIGH | Zero healthy targets or zero requests with healthy targets |
low_traffic_alb | MEDIUM | ALB with <100 requests in 14 days but healthy targets |
high_lcu_cost_alb | LOW | ALB where LCU cost exceeds 2ร base fee |
classic_lb_migration | LOW | Classic Load Balancer โ migrate to ALB/NLB |
unused_distribution | LOW | CloudFront with zero requests for 30 days |
unused_hosted_zone | MEDIUM | Route53 zone with only NS/SOA records (2 records or fewer) |
unused_accelerator | HIGH | Global Accelerator with no endpoints configured |
idle_global_accelerator | HIGH | Global 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_accelerator | MEDIUM | Global Accelerator disabled but still incurring fixed hourly charges |
idle_transfer_server | HIGH | Online 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_activity | HIGH | Transfer 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_app | HIGH | Transfer Family web app with zero active sessions for 30 days |
unused_transfer_protocol | MEDIUM | Transfer server with an enabled protocol that had zero transfers for 30 days while another protocol was active |
unused_vpc_endpoint | HIGH | VPC endpoint with zero traffic for 14 days |
orphaned_dns_record | MEDIUM | DNS record pointing to non-existent resource |
Serverless Optimization (10 Waste Types)โ
| Waste Type | Confidence | Detection |
|---|---|---|
unused_lambda | HIGH | Zero invocations for 30 days, on a function last changed at least 30 days ago |
lambda_old_runtime | LOW | Function on deprecated/EOL runtime |
unused_api_gateway | HIGH | Zero requests for 30 days (API at least 30 days old) |
unused_appsync | LOW | Zero requests for 30 days (API at least 30 days old) |
appsync_idle_cache | MEDIUM | Withheld: AWS publishes no cache hit or miss metric under the standard AppSync dimensions, so this check does not fire yet |
appsync_idle_subscriptions | MEDIUM | Active WebSocket connections with < 100 requests in 14 days (API at least 14 days old) |
idle_state_machine | LOW | Zero executions for 14 days |
step_functions_retry_storm | HIGH | Retry ratio โฅ 25% AND failure rate โฅ 20% over 14 days |
step_functions_high_transition_density | MEDIUM | Avg transitions per success > 50 (configurable) |
step_functions_express_duration_waste | MEDIUM | Express p95 duration > 30,000 ms with at least 10,000 executions a month |
Analytics Optimization (22 Waste Types)โ
| Waste Type | Confidence | Detection |
|---|---|---|
idle_emr_cluster | HIGH | Cluster in WAITING state now and running for more than 24 hours |
long_running_emr | MEDIUM | RUNNING for more than 720 hours with no running step and no step finished in the last 7 days |
emr_over_provisioned | MEDIUM | Average 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_termination | HIGH | Keep-alive cluster running more than 24 hours without an auto-termination policy, on an EMR release that supports one |
emr_previous_gen_instances | HIGH | Cluster running more than 24 hours on previous-generation instance types (m3โm5, c3โc5, etc.) |
emr_spot_opportunity | MEDIUM | Task instance group of 2 or more nodes on On-Demand instead of Spot, on a cluster running more than 48 hours |
idle_kinesis_stream | HIGH | Zero records for 14 days, on a stream at least that old |
over_provisioned_kinesis | MEDIUM | Provisioned stream with more than 1 shard and per-shard utilization below 20% over 14 days |
kinesis_on_demand_downgrade | MEDIUM | On-Demand stream at least 30 days old with stable throughput (CV < 0.3) โ switch to Provisioned |
kinesis_extended_retention_waste | HIGH | Extended retention (more than 24 hours) with zero GetRecords in 14 days |
kinesis_enhanced_fan_out_waste | HIGH | Enhanced fan-out consumer, at least 14 days old, that never subscribed in that time |
kinesis_firehose_idle | HIGH | Firehose delivery stream with zero records for 14 days |
idle_msk_cluster | HIGH | No user-owned partition on any broker for 7 days, with only AWS canary-level traffic (cluster at least 7 days old) |
oversized_msk_cluster | MEDIUM | Busiest-broker CPU < 20% AND network < 50% for 7 days |
old_glue_job | LOW | Job not run for 90 days (with no run history: not modified for that long) |
idle_glue_crawler | MEDIUM | Crawler not run for 90 days (never run: created more than that long ago) |
oversized_glue_job | LOW | JVM heap < 30% avg OR short duration with large DPU allocation; at least $5 a month excess |
glue_job_missing_timeout | MEDIUM | Timeout โฅ 10ร average execution duration and at least 60 minutes |
failed_glue_job_retry | HIGH | โฅ 50% failure rate across at least 3 recent runs, wasting at least $5 a month |
glue_catalog_bloat | LOW | Data Catalog objects exceed the 1,000,000-object free tier, or table versions exceed 500,000 |
idle_mq_broker | HIGH | Zero stored messages, consumers and producers for 14 days (broker at least 14 days old) |
oversized_mq_broker | MEDIUM | Average CPU at or below 15% over 14 days โ downsize to smaller instance type |
ML/AI Optimization (6 Waste Types)โ
| Waste Type | Confidence | Detection |
|---|---|---|
idle_sagemaker_notebook | MEDIUM | InService with no start, change or Jupyter log activity in the window |
idle_sagemaker_endpoint | HIGH | Zero invocations for 7 days |
oversized_sagemaker_endpoint | MEDIUM | Low CPU/memory utilization |
stopped_sagemaker_notebook_storage | HIGH | Stopped notebook with an ML storage volume |
previous_gen_sagemaker_instance | HIGH | Previous-gen instance type (ml.m4/c4) |
sp_opportunity_sagemaker | HIGH | AWS Cost Explorer SageMaker SP recommendation, reported only when the estimated saving is at least 10 USD a month |
Management & Operations (7 Waste Types)โ
| Waste Type | Confidence | Detection |
|---|---|---|
no_retention_log_group | MEDIUM/LOW | Log group without retention policy |
old_log_group | MEDIUM | Log group with no recent logs |
excessive_retention_log_group | MEDIUM | Log group with retention exceeding recommended baseline |
empty_log_group | LOW | Log group with zero log streams |
duplicate_cloudtrail | HIGH | Multiple trails logging same events |
cloudtrail_s3_no_lifecycle | MEDIUM | CloudTrail bucket without lifecycle that holds logs over 365 days old |
unused_dashboard | MEDIUM | CloudWatch dashboard with no recent views |
Security & Compliance (12 Waste Types)โ
| Waste Type | Confidence | Detection |
|---|---|---|
unused_secret | MEDIUM/LOW | Secret not accessed for 90 days |
unused_kms_key | LOW | Customer-managed KMS key not referenced by any CloudTrail trail (usage cannot be verified) |
unencrypted_ebs_volume | HIGH | EBS volume without encryption enabled |
unencrypted_rds_instance | HIGH | RDS instance without encryption at rest |
unencrypted_efs_filesystem | HIGH | EFS filesystem without encryption at rest |
s3_no_default_encryption | MEDIUM | S3 bucket without default encryption configuration |
opensearch_no_encryption_at_rest | HIGH | OpenSearch domain without encryption at rest |
unencrypted_documentdb_cluster | HIGH | DocumentDB cluster without encryption at rest |
rds_no_deletion_protection | HIGH | RDS instance without deletion protection enabled |
dynamodb_no_deletion_protection | HIGH | DynamoDB table without deletion protection enabled |
rds_publicly_accessible | HIGH | RDS instance with public accessibility enabled |
resource_without_backup_coverage | MEDIUM | Critical 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 Type | Confidence | Detection |
|---|---|---|
oversized_ec2_optimizer | HIGH | AWS ML-based rightsizing recommendation, reported only when the estimated saving is at least 5 USD a month |
oversized_ebs_optimizer | HIGH | AWS ML-based IOPS/throughput analysis, reported only when the estimated saving is at least 5 USD a month |
oversized_lambda_optimizer | HIGH | AWS ML-based memory optimization, reported only when the estimated saving is at least 5 USD a month |
oversized_rds_optimizer | HIGH | AWS 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 Type | Confidence | Detection |
|---|---|---|
ri_opportunity_ec2 | HIGH | AWS Cost Explorer EC2 RI recommendation, reported only when the estimated saving is at least 10 USD a month |
ri_opportunity_rds | HIGH | AWS Cost Explorer RDS RI recommendation, reported only when the estimated saving is at least 10 USD a month |
ri_opportunity_elasticache | HIGH | AWS Cost Explorer ElastiCache Reserved Node recommendation, reported only when the estimated saving is at least 10 USD a month |
ri_opportunity_opensearch | HIGH | AWS Cost Explorer OpenSearch RI recommendation, reported only when the estimated saving is at least 10 USD a month |
ri_opportunity_redshift | HIGH | AWS Cost Explorer Redshift Reserved Node recommendation, reported only when the estimated saving is at least 10 USD a month |
sp_opportunity_compute | HIGH | AWS Cost Explorer Compute SP recommendation, reported only when the estimated saving is at least 10 USD a month |
sp_opportunity_ec2 | HIGH | AWS Cost Explorer EC2 Instance SP recommendation, reported only when the estimated saving is at least 10 USD a month |
sp_opportunity_sagemaker | HIGH | AWS Cost Explorer SageMaker SP recommendation, reported only when the estimated saving is at least 10 USD a month |
unused_reserved_instance | HIGH | Currently 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_plan | HIGH | SP utilization < 20% over 30 days and unused commitment of at least $10/month, via Cost Explorer |
expiring_reserved_instance | HIGH | Active RI expiring within 90 days (URGENT/WARNING/NOTICE tiers) |
expiring_savings_plan | HIGH | Active SP expiring within 90 days (URGENT/WARNING/NOTICE tiers) |
convertible_ri_exchange_opportunity | MEDIUM | Convertible RI costing at least $10/month on previous-gen instance type eligible for free exchange |
savings_plan_coverage_gap | MEDIUM | SP coverage < 50% with at least $100/mo uncovered on-demand spend |
cur_unused_reservation | MEDIUM | Unused RI hours costing at least $10 in the billing period, detected in CUR billing data (all modes) |
cur_savings_plan_waste | MEDIUM | Unused 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โ
| Mode | Waste Types Available | Coverage |
|---|---|---|
| Online Mode | 186 (all types) | 100% |
| Offline + Full Export | 158 waste types | 85% |
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 Type | Risk | Mode | Remediation |
|---|---|---|---|---|
| 1 | beanstalk_idle_traffic | high | โ | Plan only |
| 2 | beanstalk_orphaned_rds | high | ๐ Online only | Plan only |
| 3 | beanstalk_over_provisioned | medium | โ | Plan only |
| 4 | beanstalk_previous_gen_instances | low | โ | Plan only |
| 5 | beanstalk_unnecessary_alb | low | โ | Plan only |
| 6 | convertible_ri_exchange_opportunity | low | ๐ Online only | Recommendation only |
| 7 | cur_savings_plan_waste | high | โ | Recommendation only |
| 8 | cur_unused_reservation | high | โ | Recommendation only |
| 9 | eks_extended_support_cost | low | โ | Recommendation only |
| 10 | expiring_reserved_instance | medium | ๐ Online only | Recommendation only |
| 11 | expiring_savings_plan | medium | ๐ Online only | Recommendation only |
| 12 | idle_beanstalk | high | โ | Plan only |
| 13 | idle_ec2 | high | โ | Plan only |
| 14 | idle_lightsail | high | โ | Recommendation only |
| 15 | idle_workspace | high | โ | Plan only |
| 16 | lambda_arm64_migration | low | โ | Plan only |
| 17 | lambda_excessive_timeout | low | โ | Fix This |
| 18 | lambda_provisioned_concurrency_idle | medium | โ | Fix This |
| 19 | lightsail_idle_database | high | โ | Plan only |
| 20 | lightsail_idle_load_balancer | medium | โ | Plan only |
| 21 | lightsail_old_snapshot | medium | โ | Plan only |
| 22 | lightsail_unattached_disk | medium | โ | Recommendation only |
| 23 | lightsail_unattached_static_ip | low | โ | Plan only |
| 24 | oversized_ec2_optimizer | low | ๐ Online only | Recommendation only |
| 25 | oversized_workspace | medium | โ | Plan only |
| 26 | ri_opportunity_ec2 | low | ๐ Online only | Recommendation only |
| 27 | savings_plan_coverage_gap | low | ๐ Online only | Recommendation only |
| 28 | sp_opportunity_compute | low | ๐ Online only | Recommendation only |
| 29 | sp_opportunity_ec2 | low | ๐ Online only | Recommendation only |
| 30 | stopped_ec2_with_ebs | medium | โ | Plan only |
| 31 | unused_reserved_instance | high | ๐ Online only | Recommendation only |
| 32 | unused_savings_plan | high | ๐ Online only | Recommendation only |
| 33 | workspaces_autostop_opportunity | medium | โ | Plan only |
| 34 | workspaces_pool_overprovisioned_capacity | medium | โ | Plan only |
| 35 | workspaces_windows_license_optimization | low | โ | Plan only |
Storage Optimization (27 Types)โ
| # | Waste Type | Risk | Mode | Remediation |
|---|---|---|---|---|
| 36 | ami_orphaned_snapshot | low | โ | Fix This |
| 37 | backup_copy_policy_overreach | low | โ | Plan only |
| 38 | backup_no_lifecycle_tiering | low | โ | Fix This |
| 39 | ecr_no_lifecycle_policy | low | โ | Recommendation only |
| 40 | fsx_throughput_overprovisioned | medium | โ | Plan only |
| 41 | gp2_migration | medium | โ | Fix This |
| 42 | idle_efs | high | โ | Recommendation only |
| 43 | idle_fsx | high | โ | Fix This |
| 44 | incomplete_multipart | low | โ | Plan only |
| 45 | no_lifecycle_efs | low | โ | Recommendation only |
| 46 | no_lifecycle_policy | low | โ | Fix This |
| 47 | old_backup | medium | โ | Fix This |
| 48 | old_ebs_snapshot | low | โ | Fix This |
| 49 | old_ecr_images | low | โ | Recommendation only |
| 50 | old_fsx_backup | medium | โ | Fix This |
| 51 | orphaned_ebs_snapshot | low | โ | Fix This |
| 52 | over_provisioned_iops | medium | โ | Fix This |
| 53 | oversized_ebs_optimizer | low | ๐ Online only | Plan only |
| 54 | oversized_fsx | medium | โ | Plan only |
| 55 | redundant_backup | medium | โ | Plan only |
| 56 | s3_empty_bucket | low | โ | Recommendation only |
| 57 | s3_high_request_and_transfer_cost | low | โ | Plan only |
| 58 | s3_rapid_growth | low | โ | Plan only |
| 59 | s3_wrong_storage_class | low | โ | Plan only |
| 60 | stale_backup_plan_assignment | low | โ | Recommendation only |
| 61 | unattached_ebs | low | โ | Fix This |
| 62 | untagged_ecr_images | low | โ | Recommendation only |
Database Optimization (41 Types)โ
| # | Waste Type | Risk | Mode | Remediation |
|---|---|---|---|---|
| 63 | aurora_extended_support_cost | low | โ | Plan only |
| 64 | aurora_io_optimization_opportunity | low | โ | Fix This |
| 65 | aurora_serverless_opportunity | low | โ | Plan only |
| 66 | aurora_to_rds_downgrade_opportunity | medium | โ | Plan only |
| 67 | documentdb_extended_support_cost | low | โ | Plan only |
| 68 | dynamodb_no_autoscaling | medium | โ | Fix This |
| 69 | elasticache_data_tiering_opportunity | low | โ | Plan only |
| 70 | elasticache_engine_migration | low | โ | Plan only |
| 71 | elasticache_extended_support_cost | low | โ | Plan only |
| 72 | elasticache_replication_waste | medium | โ | Plan only |
| 73 | elasticache_serverless_optimization | low | โ | Plan only |
| 74 | idle_documentdb | high | โ | Recommendation only |
| 75 | idle_dynamodb | high | โ | Fix This |
| 76 | idle_elasticache | high | โ | Plan only |
| 77 | idle_neptune | high | โ | Recommendation only |
| 78 | idle_opensearch | high | ๐ Online only | Plan only |
| 79 | idle_rds | high | โ | Plan only |
| 80 | idle_redshift | high | โ | Fix This |
| 81 | neptune_old_snapshot | medium | โ | Fix This |
| 82 | neptune_serverless_opportunity | low | โ | Plan only |
| 83 | old_documentdb_snapshot | medium | โ | Fix This |
| 84 | old_rds_snapshot | medium | โ | Fix This |
| 85 | opensearch_ebs_overprovisioned | medium | โ | Plan only |
| 86 | opensearch_extended_support_cost | low | โ | Plan only |
| 87 | over_provisioned_dynamodb | medium | โ | Fix This |
| 88 | overprovisioned_documentdb | medium | โ | Plan only |
| 89 | oversized_elasticache | medium | โ | Plan only |
| 90 | oversized_neptune | medium | โ | Fix This |
| 91 | oversized_opensearch | medium | โ | Plan only |
| 92 | oversized_rds_optimizer | low | ๐ Online only | Plan only |
| 93 | oversized_redshift | medium | โ | Plan only |
| 94 | rds_extended_support_cost | low | โ | Plan only |
| 95 | redshift_concurrency_scaling_waste | low | โ | Plan only |
| 96 | redshift_legacy_dc2 | low | โ | Plan only |
| 97 | redshift_no_pause | medium | โ | Fix This |
| 98 | redshift_wlm_over_provisioned | medium | โ | Plan only |
| 99 | ri_opportunity_elasticache | low | ๐ Online only | Recommendation only |
| 100 | ri_opportunity_opensearch | low | ๐ Online only | Recommendation only |
| 101 | ri_opportunity_rds | low | ๐ Online only | Recommendation only |
| 102 | ri_opportunity_redshift | low | ๐ Online only | Recommendation only |
| 103 | underutilized_redshift | medium | โ | Plan only |
Network Optimization (19 Types)โ
| # | Waste Type | Risk | Mode | Remediation |
|---|---|---|---|---|
| 104 | classic_lb_migration | low | โ | Plan only |
| 105 | disabled_global_accelerator | medium | โ | Fix This |
| 106 | eip_on_stopped_instance | low | โ | Fix This |
| 107 | high_lcu_cost_alb | low | โ | Plan only |
| 108 | idle_global_accelerator | high | โ | Plan only |
| 109 | idle_load_balancer | medium | โ | Fix This |
| 110 | idle_nat_gateway | medium | โ | Fix This |
| 111 | idle_transfer_no_activity | high | โ | Plan only |
| 112 | idle_transfer_server | high | โ | Plan only |
| 113 | idle_transfer_web_app | high | โ | Plan only |
| 114 | low_traffic_alb | medium | โ | Fix This |
| 115 | multiple_eips_per_instance | medium | โ | Recommendation only |
| 116 | orphaned_dns_record | low | ๐ Online only | Plan only |
| 117 | unattached_eip | low | โ | Fix This |
| 118 | unused_accelerator | high | โ | Plan only |
| 119 | unused_distribution | medium | ๐ Online only | Fix This |
| 120 | unused_hosted_zone | medium | ๐ Online only | Fix This |
| 121 | unused_transfer_protocol | medium | โ | Plan only |
| 122 | unused_vpc_endpoint | medium | โ | Plan only |
Analytics Optimization (22 Types)โ
| # | Waste Type | Risk | Mode | Remediation |
|---|---|---|---|---|
| 123 | emr_missing_auto_termination | low | โ | Plan only |
| 124 | emr_over_provisioned | medium | โ | Plan only |
| 125 | emr_previous_gen_instances | low | โ | Plan only |
| 126 | emr_spot_opportunity | low | โ | Plan only |
| 127 | failed_glue_job_retry | low | โ | Fix This |
| 128 | glue_catalog_bloat | low | โ | Plan only |
| 129 | glue_job_missing_timeout | low | โ | Fix This |
| 130 | idle_emr_cluster | high | โ | Plan only |
| 131 | idle_glue_crawler | medium | โ | Fix This |
| 132 | idle_kinesis_stream | medium | โ | Fix This |
| 133 | idle_mq_broker | high | โ | Fix This |
| 134 | idle_msk_cluster | high | โ | Plan only |
| 135 | kinesis_enhanced_fan_out_waste | medium | ๐ Online only | Fix This |
| 136 | kinesis_extended_retention_waste | low | โ | Fix This |
| 137 | kinesis_firehose_idle | medium | โ | Plan only |
| 138 | kinesis_on_demand_downgrade | low | โ | Plan only |
| 139 | long_running_emr | high | โ | Plan only |
| 140 | old_glue_job | medium | โ | Fix This |
| 141 | over_provisioned_kinesis | medium | โ | Fix This |
| 142 | oversized_glue_job | low | โ | Plan only |
| 143 | oversized_mq_broker | low | โ | Plan only |
| 144 | oversized_msk_cluster | medium | โ | Plan only |
ML/AI Optimization (6 Types)โ
| # | Waste Type | Risk | Mode | Remediation |
|---|---|---|---|---|
| 145 | idle_sagemaker_endpoint | high | โ | Fix This |
| 146 | idle_sagemaker_notebook | medium | โ | Fix This |
| 147 | oversized_sagemaker_endpoint | medium | โ | Plan only |
| 148 | previous_gen_sagemaker_instance | low | โ | Fix This |
| 149 | sp_opportunity_sagemaker | low | ๐ Online only | Recommendation only |
| 150 | stopped_sagemaker_notebook_storage | low | โ | Plan only |
Management & Operations (7 Types)โ
| # | Waste Type | Risk | Mode | Remediation |
|---|---|---|---|---|
| 151 | cloudtrail_s3_no_lifecycle | low | ๐ Online only | Plan only |
| 152 | duplicate_cloudtrail | high | ๐ Online only | Recommendation only |
| 153 | empty_log_group | low | โ | Fix This |
| 154 | excessive_retention_log_group | low | โ | Fix This |
| 155 | no_retention_log_group | low | โ | Fix This |
| 156 | old_log_group | low | โ | Plan only |
| 157 | unused_dashboard | low | โ | Fix This |
Security & Compliance (12 Types)โ
| # | Waste Type | Risk | Mode | Remediation |
|---|---|---|---|---|
| 158 | dynamodb_no_deletion_protection | medium | โ | Recommendation only |
| 159 | opensearch_no_encryption_at_rest | medium | โ | Recommendation only |
| 160 | rds_no_deletion_protection | medium | โ | Recommendation only |
| 161 | rds_publicly_accessible | high | โ | Recommendation only |
| 162 | resource_without_backup_coverage | medium | โ | Recommendation only |
| 163 | s3_no_default_encryption | low | โ | Recommendation only |
| 164 | unencrypted_documentdb_cluster | medium | โ | Recommendation only |
| 165 | unencrypted_ebs_volume | medium | โ | Recommendation only |
| 166 | unencrypted_efs_filesystem | medium | โ | Recommendation only |
| 167 | unencrypted_rds_instance | medium | โ | Recommendation only |
| 168 | unused_kms_key | medium | ๐ Online only | Fix This |
| 169 | unused_secret | medium | โ | Fix This |
Serverless Optimization (12 Types)โ
| # | Waste Type | Risk | Mode | Remediation |
|---|---|---|---|---|
| 170 | appsync_idle_cache | medium | โ | Fix This |
| 171 | appsync_idle_subscriptions | low | โ | Plan only |
| 172 | idle_state_machine | medium | โ | Fix This |
| 173 | lambda_old_runtime | low | โ | Plan only |
| 174 | over_provisioned_lambda | medium | โ | Fix This |
| 175 | oversized_lambda_optimizer | low | ๐ Online only | Plan only |
| 176 | step_functions_express_duration_waste | medium | โ | Plan only |
| 177 | step_functions_high_transition_density | medium | โ | Plan only |
| 178 | step_functions_retry_storm | medium | โ | Plan only |
| 179 | unused_api_gateway | medium | ๐ Online only | Fix This |
| 180 | unused_appsync | medium | โ | Fix This |
| 181 | unused_lambda | high | โ | Fix This |
Container Optimization (5 Types)โ
| # | Waste Type | Risk | Mode | Remediation |
|---|---|---|---|---|
| 182 | ecs_container_insights_waste | low | โ | Plan only |
| 183 | ecs_no_autoscaling | medium | โ | Plan only |
| 184 | idle_ecs_service | high | โ | Plan only |
| 185 | oversized_ecs_memory | medium | โ | Plan only |
| 186 | oversized_ecs_task | medium | โ | 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 Type | Reason |
|---|---|
beanstalk_orphaned_rds | Cross-service EB/RDS correlation (RDS + CloudWatch API) |
cloudtrail_s3_no_lifecycle | Live CloudTrail + S3 API |
convertible_ri_exchange_opportunity | RI inventory + pricing (EC2 API + Pricing API) |
duplicate_cloudtrail | Live CloudTrail API |
expiring_reserved_instance | Real-time RI expiry tracking (EC2 API) |
expiring_savings_plan | Real-time SP expiry tracking (Savings Plans API) |
idle_opensearch | Live OpenSearch cross-cluster connection check |
kinesis_enhanced_fan_out_waste | Live CloudWatch ListMetrics (subscription check) |
orphaned_dns_record | Live Route 53 record cross-reference |
oversized_ebs_optimizer | AWS ML analysis (Compute Optimizer API) |
oversized_ec2_optimizer | AWS ML analysis (Compute Optimizer API) |
oversized_lambda_optimizer | AWS ML analysis (Compute Optimizer API) |
oversized_rds_optimizer | AWS ML analysis (Compute Optimizer API) |
ri_opportunity_ec2 | Purchase recommendation (Cost Explorer API) |
ri_opportunity_elasticache | Purchase recommendation (Cost Explorer API) |
ri_opportunity_opensearch | Purchase recommendation (Cost Explorer API) |
ri_opportunity_rds | Purchase recommendation (Cost Explorer API) |
ri_opportunity_redshift | Purchase recommendation (Cost Explorer API) |
savings_plan_coverage_gap | Coverage analysis (Cost Explorer API) |
sp_opportunity_compute | Purchase recommendation (Cost Explorer API) |
sp_opportunity_ec2 | Purchase recommendation (Cost Explorer API) |
sp_opportunity_sagemaker | Purchase recommendation (Cost Explorer API) |
unused_api_gateway | Live API Gateway + CloudWatch metrics |
unused_distribution | Live CloudFront + CloudWatch metrics |
unused_hosted_zone | Live Route 53 API |
unused_kms_key | Live KMS + CloudTrail API |
unused_reserved_instance | Currently doesn't run. For uploaded CUR files, see cur_unused_reservation. |
unused_savings_plan | Purchase 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:
- 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 - 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
- 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)
- Missing or sparse CPU data is reported as a scan warning (the verdict is missing, not zero), never as an idle instance
- 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:
- We identify stopped instances with BlockDeviceMappings
- EBS volumes continue to charge even when the instance is stopped
- 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
- 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:
- We read each function's configured memory and its average duration from CloudWatch over the last 30 days
- Functions with 512 MB or more allocated that finish in under 500 ms on average are flagged
- 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:
- We list all Provisioned Concurrency configurations for each Lambda function
- CloudWatch
ProvisionedConcurrencyUtilizationis analyzed over 14 days - Configurations with average utilization below 10% are flagged as idle
- 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:
- We compare the configured timeout against actual average execution duration
- Functions where the timeout is 10ร or more of the average duration are flagged
- This helps avoid accidental runaway costs and improves error handling
- 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:
- We check the function's architecture (x86_64) and runtime
- Functions on ARM64-compatible runtimes (Python, Node.js, Java, .NET, Ruby) are flagged
- Only functions with monthly cost โฅ $1 are included (to avoid noise)
- 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:
- 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
- A service whose maximum stays below 20% is flagged
- 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
- 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:
- We identify SageMaker notebook instances in InService status that have not been started or reconfigured within the window (LastModifiedTime)
- 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 - 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
- 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:
- We query CloudWatch for endpoint invocation metrics
- Endpoints with zero invocations over the last 7 days are flagged
- 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:
- We read each WorkSpace's last user connection from the WorkSpaces connection status API
- WorkSpaces with no user connection for more than 30 days are flagged
- 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:
- 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
- Instances with consistently low CPU (or stopped) are flagged
- 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:
- We list all Lightsail static IPs
- IPs where
isAttachedis false are flagged - 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:
- We list all Lightsail block storage disks
- Disks where
isAttachedis false are flagged - 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:
- We list all Lightsail instance snapshots
- Manual snapshots older than 90 days are flagged
- 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:
- We list all Lightsail load balancers
- LBs with zero healthy instances are flagged
- 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:
- We query CloudWatch CPUUtilization and DatabaseConnections metrics
- Databases with consistently near-zero CPU and connections are flagged
- Stopped databases are also detected
- 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:
- We check each Beanstalk environment's health status via the DataProvider
- 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
- 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 - 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:
- 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'sHealthyHostCountmust also have reported on at least 75% of the 14 days - 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 - Anything else is reported as missing data, never as zero traffic
- An environment created within the last 14 days is skipped: the window needs an environment that existed throughout it
- 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:
- We check environment type (LoadBalanced) and Auto Scaling group settings
- If min=max=1, the ALB is serving a single instance with no scaling benefit
- 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:
- We check the instance type configured for each environment
- If the family is in our previous-gen mapping (t2โt3, m4โm5, c4โc5, etc.), we flag it
- 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:
- We find the environment's running instances by Elastic Beanstalk's own
elasticbeanstalk:environment-nametag and read their hourly CPUUtilization (AWS/EC2) - 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
- We recommend the smallest instance count that would run at 50% average CPU, priced region-adjusted
- 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:
- We scan RDS instances for Elastic Beanstalk's own tags:
elasticbeanstalk:environment-id/environment-name, or a CloudFormation stack namedawseb-<environment id>-stack. A database made by any other CloudFormation stack (CDK, SAM) is not Beanstalk's and is not checked - A database whose environment still exists is not orphaned and is skipped
- 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
- 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:
- We check each EKS cluster's Kubernetes version against AWS's EKS release calendar
- 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
- 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:
- We read hourly peak CPUUsage and MemoryUsage for each downgradeable WorkSpace
- 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
- 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:
- We identify AlwaysOn WorkSpaces where the user hasn't connected in over 7 days
- AlwaysOn charges a fixed monthly rate regardless of usage
- AutoStop charges a lower base fee plus hourly usage, saving money for infrequent users
- 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:
- 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
- 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
- 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:
- We count WorkSpaces running Windows with AWS-included licenses
- Organizations with Microsoft Volume Licensing or Software Assurance can bring their own licenses (BYOL)
- 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:
- We identify ECS services where desired tasks are set but no tasks are actually running
- This indicates a failed deployment or an abandoned service
- 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:
- We check ECS services with multiple running tasks
- Services without Application Auto Scaling registered are flagged
- Auto-scaling allows services to scale down during low-traffic periods
- 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:
- We check if Container Insights is enabled on ECS clusters
- Dev/staging/sandbox clusters or clusters with few services don't benefit enough to justify the monitoring cost
- 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:
- We analyze ECS task memory utilization via CloudWatch metrics
- Fargate services whose busiest task peaks below 40% of the task memory are flagged. A service younger than 7 days is skipped
- 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
- 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 Type | Price 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:
- gp2 and gp3 have different per-GB prices (see the AWS EBS pricing page)
- gp3 costs less per GB-month than gp2
- 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-volumesets--iopsand--throughputto 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โ
| Property | Value |
|---|---|
| Waste Type | orphaned_ebs_snapshot |
| Category | Storage Optimization |
| Confidence | HIGH |
| Risk Level | LOW |
| Savings Estimate | volume_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โ
| Property | Value |
|---|---|
| Waste Type | ami_orphaned_snapshot |
| Category | Storage Optimization |
| Confidence | HIGH |
| Risk Level | LOW |
| Savings Estimate | volume_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:
- We check each ECR repository for a lifecycle policy
- Repos without policies accumulate old images indefinitely
- 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:
- We check each S3 bucket for lifecycle configuration
- Buckets without lifecycle rules accumulate objects indefinitely
- 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:
- We list incomplete multipart uploads in S3 buckets
- Uploads started in the last 7 days are skipped: they may still be in progress
- 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 - 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:
- We list each EFS file system and its mount targets
- File systems with no mount targets are flagged, because nothing can mount them
- 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:
- We query CloudWatch for FSx DataReadBytes and DataWriteBytes
- 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
- 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:
- We compare provisioned storage against actual used capacity via CloudWatch FreeStorageCapacity
- 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
- 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:
- We read hourly DataReadBytes and DataWriteBytes over the last 7 days and take the busiest hour
- Filesystems (Windows/ONTAP/OpenZFS) whose busiest hour stays below 30% of capacity are flagged, when the saving is at least $0.01 a month
- 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
- Lustre excluded: its throughput is part of the storage price
- 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:
- We list all FSx backups and filter for manual/user-initiated type
- Backups at least 90 days old are flagged, when the estimated cost is at least $0.01 a month
- Retention-tagged backups (compliance, retain, legal-hold) are excluded
- Automatic backups managed by FSx retention policies are excluded
- 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:
- We list all images in ECR repositories
- Images older than 90 days are flagged for cleanup
- 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:
- 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
- One batched CloudWatch
GetMetricDatarequest reads each volume's one-minuteVolumeReadOps+VolumeWriteOpsas IOPS - 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)
- 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:
- We read each EFS filesystem's lifecycle configuration
- Filesystems with no
TransitionToIAorTransitionToArchiverule keep all data in the Standard storage class (aTransitionToPrimaryStorageClassrule alone moves nothing) - Most EFS data becomes infrequently accessed after 30 days โ significant savings opportunity
- 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:
- We scan ECR repositories for images without tags
- Untagged images are typically intermediate build layers or superseded images
- 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:
- We list all AWS Backup recovery points across vaults
- Completed points at least 180 days old without a
compliance-holdtag are flagged, unless their estimated cost is under $0.01 a month - 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:
- We group recovery points by resource ARN
- 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
- 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
- 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:
- We analyze backup plan lifecycle rules
- Rules that delete after 90 days or more without a cold storage transition are flagged
- 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:
- We read each plan's last execution date and its resource selections
- 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
- 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:
- We group the last 90 days of AWS Backup copy jobs by destination vault
- A destination receiving more than 10 copies and 100 GB is flagged for review, if the destination vault still exists
- 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:
- We identify empty S3 buckets that have existed for at least 30 days
- Infrastructure buckets (CDK, CloudFormation, logging prefixes) are excluded
- 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:
- We analyze S3 storage class distribution per bucket
- Buckets with predominantly Standard storage and no tiering transitions are flagged
- 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:
- 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)
- Buckets doubling in size with significant absolute growth are flagged
- 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:
- We compare S3 request and transfer costs against storage costs
- When requests/transfer cost more than storage, architectural optimization may help
- 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:
- We query CloudWatch for database connection metrics
- Zero connections for 14 days = definitely unused
- 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:
- We identify Aurora clusters using Standard storage (not I/O-Optimized)
- Calculate the I/O-to-total-spend ratio from 30 days of CloudWatch VolumeReadIOPs and VolumeWriteIOPs
- 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
- 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:
- 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)
- If the version has passed its standard support end date, AWS charges Extended Support fees
- Surcharges are per-vCPU per hour, escalating after year 2
- 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:
- 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
- Excludes clusters already running Serverless, Global databases, and multi-writer setups
- Estimates Serverless v2 cost at 2 ACU baseline ร $0.12/ACU-hr
- 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:
- We identify DynamoDB tables using provisioned capacity mode
- Check if Application Auto Scaling is configured for the table
- 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:
- We query CloudWatch for ConsumedReadCapacityUnits and ConsumedWriteCapacityUnits
- Tables with zero consumption for 14 days are flagged
- Provisioned tables charge continuously; on-demand has storage costs
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:
- We total
ConsumedReadCapacityUnits/ConsumedWriteCapacityUnitsfrom 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. - Tables using less than 20% of provisioned read and write capacity are over-provisioned
- Right-sizing or switching to on-demand can reduce costs
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.
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:
- We query CloudWatch for CurrConnections metric
- Clusters with zero connections for 7 days are flagged
- 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:
- We read the cluster's
DatabaseConnectionsfrom CloudWatch - 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
- A cluster younger than 7 days is not flagged, because it cannot show a full window of inactivity
- 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:
- We query CloudWatch for
OpenSearchRequests,InvalidHostHeaderRequests,IndexingRate,SearchRate,ClusterStatus.red,ClusterUsedSpaceandFreeStorageSpace. Client requests areOpenSearchRequestsminusInvalidHostHeaderRequests(internet scanners on a public endpoint do not count) and must be exactly 0 on every day of the 7-day window IndexingRatemust be measured and average 0: a log or observability sink that indexes but is rarely searched is not idleSearchRatemust stay at or below 100 searches per data node per day (the service's own background activity),ClusterStatus.redmust be 0 on every day, andClusterUsedSpacemust be at most 10 MiB- 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 - UltraWarm and cold storage must be known to be off, and a live check must find no cross-cluster connection
- 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
- OpenSearch charges continuously for instance hours
- 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:
- We collect hourly CloudWatch CPUUtilization (average and maximum) over 7 days
- 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
- A step-down instance type map estimates savings from rightsizing
- If the instance type isn't in the step-down map, a conservative 30% savings is estimated
- 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:
- 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
- We calculate the storage growth rate from a 14-day trend
- Domains with >60% free storage and flat growth (<0.1 GB/day) are flagged
- 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:
- We query Cost Explorer for OpenSearch RI purchase recommendations
- Domains running on-demand that would benefit from a 1-year partial-upfront RI are flagged
- 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:
- We query CloudWatch for Neptune request metrics
- 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)
- 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:
- We identify provisioned Neptune clusters (not already Serverless)
- Hourly CloudWatch
CPUUtilizationis analyzed over 14 days; at least 75% of the window's hours must have a reading - 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
- 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
- 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:
- We analyze CloudWatch
CPUUtilizationfor each provisioned Neptune cluster - 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
- Each over-provisioned instance is flagged with a target smaller instance class
- 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.
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:
- We query Neptune for manual cluster snapshots (
DescribeDBClusterSnapshots) - Snapshots at least 90 days old are flagged, provided their storage costs at least $0.01 a month
- 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.
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:
- We query CloudWatch for DocumentDB connection and I/O metrics
- 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
- The cluster must be at least 7 days old, so a recreated cluster is not judged on partial data
- 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:
- Lists all manual DocumentDB cluster snapshots
- Groups snapshots by source cluster
- Protects the newest manual snapshot per cluster (disaster-recovery safety net)
- Skips snapshots tagged with
retention,compliance,legal, ordo-not-delete - 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:
- Fetches CloudWatch metrics for each DocumentDB cluster over 7 days; hourly CPU readings must cover at least 75% of the window's hours
- 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
- If all metrics are below thresholds and the cluster is at least 7 days old, it is a rightsizing candidate
- 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:
- 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
- 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
- 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
- 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:
- We analyze ElastiCache CPU utilization and connection metrics via CloudWatch
- Clusters with consistently low CPU (< 10%) despite having connections are oversized
- 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:
- We read Redshift CPU utilization from CloudWatch over the metrics window; clusters with zero connections are left to
idle_redshift - Clusters below 15% average CPU are matched to the next smaller type (
ra3.16xlargetora3.4xlarge,ra3.4xlargetora3.xlplus,dc2.8xlargetodc2.large); other node types are skipped - 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:
- We read Redshift CPU utilization and database connections from CloudWatch over the metrics window
- Clusters with zero connections are left to
idle_redshift; a CPU average of 0 is treated as no data and skipped - 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:
- We check each RA3 or DC2 cluster (the families that support pause and resume) for a scheduled pause action
- We measure the share of hours with zero connections; clusters at or above 40% are flagged
- 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:
- We check the node type of Redshift clusters
- DC2 nodes use local SSD storage and are a legacy architecture
- RA3 nodes offer managed storage (pay separately) and are typically cheaper overall; we suggest
ra3.xlplusfordc2.largeandra3.4xlargefor 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:
- We read total concurrency scaling seconds from CloudWatch for the metrics window; clusters with zero connections are left to
idle_redshift - We subtract the free credit (3,600 seconds for each day of the window); nothing is flagged while usage stays inside it
- 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:
- 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 - The slot count is the Redshift default for automatic WLM (5), not read from the cluster's configuration
- 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:
- AWS Cost Explorer analyzes 30 days of ElastiCache usage
- Identifies consistent node usage suitable for RI commitment
- 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:
- AWS Cost Explorer analyzes 30 days of Redshift cluster usage
- Identifies consistent node usage suitable for RI commitment
- 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:
- We check RDS engine versions against AWS lifecycle policy
- Versions past standard support incur per-vCPU extended support fees
- 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:
- 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.
- 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
- 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:
- 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.
- End-of-life versions incur extended support surcharges per node; a warning is raised 90 days before a version enters Extended Support
- 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:
- We check ElastiCache replication groups for replica configuration
- Clusters tagged with non-production environment names (dev, staging, test, sandbox) are flagged if they have replicas
- 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:
- We check the engine type of each ElastiCache cluster
- Redis OSS and Memcached clusters are flagged as migration candidates
- 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:
- We analyze CPU utilization and connection metrics via CloudWatch over 30 days
- Clusters with high traffic variance (low average, high peaks) are candidates
- The Serverless cost is estimated from measured data:
BytesUsedForCache(at least the engine's billed minimum) and the node's request volume (ProcessedCommands, orGetTypeCmds+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) - 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:
- We identify clusters using memory-optimized R5/R6g/R7g nodes at xlarge size or larger
- R6gd nodes combine memory + NVMe SSD with ~5ร total storage capacity
- 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:
- We check OpenSearch domain engine versions against the AWS end-of-support schedule
- 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
- 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
- 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:
- NAT Gateways have a fixed hourly charge (see the pricing page below)
- We check CloudWatch for any traffic over 7 days
- 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
RequestCountover 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
ConsumedLCUsmetric 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:
- We query CloudWatch for CloudFront request metrics
- Distributions with zero requests over the last 30 days are flagged
- 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:
- We list all Route53 hosted zones
- Zones with only default NS and SOA records (no custom DNS records, 2 records or fewer) are flagged
- 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:
- We enumerate all deployed, enabled Global Accelerators and their listeners/endpoint groups
- Accelerators with no endpoints in any endpoint group are flagged
- 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:
- We enumerate all Global Accelerators and their listeners/endpoint groups
- For accelerators with endpoints, we check CloudWatch ProcessedBytesIn and ProcessedBytesOut
- Accelerators with zero traffic for 30 days despite having endpoints are flagged
- 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:
- We check the Enabled flag on each deployed Global Accelerator
- Disabled accelerators continue to reserve static anycast IPs and incur hourly charges
- 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:
- We scan Route 53 hosted zones for alias and CNAME records
- Target resources (EC2, ELB, CloudFront) are verified against current inventory
- 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:
- We check VPC endpoint data processing metrics over 14 days
- Interface endpoints with zero bytes processed are unused
- 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:
- We query CloudWatch for AppSync request metrics
- 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
- 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:
- We enumerate all AppSync APIs and check for attached caches via
GetApiCache - AWS does not publish
CacheHitCountorCacheMissCountunderAWS/AppSyncfor any dimension. The only cache metrics are the paid EnhancedCacheHitandCacheMiss, keyed per resolver and emitted only when per-resolver enhanced metrics are on - 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:
- We check CloudWatch
ConnectSuccessmetric to identify APIs with active WebSocket connections - For those APIs, we check
Latency(SampleCount) to see if actual GraphQL operations are occurring - 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
- Monthly cost is estimated from the
ConnectSuccesscount ร 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:
- We query CloudWatch for Step Functions execution metrics
- 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)
- 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:
- We collect CloudWatch metrics: ExecutionsFailed, ExecutionsTimedOut, StateTransition
- We estimate the proportion of transitions wasted on retry/failure paths
- 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:
- We compute average transitions per successful execution from CloudWatch
- Workflows with excessive state granularity are flagged
- 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:
- We identify Express workflows via describe-state-machine
- We collect ExecutionTime CloudWatch metrics (Average, Maximum)
- 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:
- We list Transfer Family servers and keep the ONLINE ones
- 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
- 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
- Servers with zero users are flagged: they cannot receive any file transfer, whatever their traffic history
- 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:
- 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
- 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
- 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:
- We identify servers with multiple protocols enabled (SFTP, FTPS, FTP, AS2)
- We check CloudWatch metrics per protocol for the last 30 days (only when the server as a whole had transfers in its own window)
- 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:
- We query Transfer Family Web Apps and check provisioned units
- 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
- 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:
- We list all Glue jobs, skip any modified within the last 90 days, and read the latest run of the rest
- A job whose latest run started more than 90 days ago is flagged
- 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)
- 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:
- We list all Glue crawlers and check their last run time
- Crawlers that haven't run in more than 90 days are flagged
- 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:
- 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
- Clusters that have been up for more than 24 hours are flagged
- 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:
- We query CloudWatch for IncomingRecords metrics
- Streams with zero records over the 14-day window are flagged; a stream younger than the window is skipped, because its data is partial
- 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:
- We read CloudWatch
AWS/Kafkaper broker (MessagesInPerSec, BytesInPerSec, BytesOutPerSec, UserPartitionExists) - 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
- 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 - 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:
- 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
- Network utilization is calculated as (BytesIn + BytesOut) / instance network capacity โ each instance type has a known throughput ceiling
- 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
- 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:
- We examine each Glue ETL job's DPU allocation (
MaxCapacityorNumberOfWorkers) and skip jobs with 2 DPUs or fewer - We read the job's latest 5 runs and query CloudWatch for
glue.ALL.jvm.heap.usageto measure worker memory utilization - If average heap usage is below 30%, the job has more workers than it needs
- 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
- 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
- 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:
- We compare the job's configured
Timeoutagainst its average execution duration over the latest 5 runs - Jobs where the timeout is at least 10ร the average duration are flagged
- Only jobs with recent successful runs and a timeout of at least 60 minutes are evaluated
- A job with no run history is flagged when its timeout is at or above the 2,880-minute AWS default
- 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.
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:
- We examine the last 10 job runs for each Glue job
- Jobs with at least 3 runs and a failure rate of 50% or more are flagged (the
MaxRetriessetting is shown in the finding but is not a condition) - A job with
MaxRetries > 0and no run history at all is flagged for verification - We calculate the total DPU-hours wasted on failed runs
- Monthly waste is projected from the observed failure frequency
- 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.
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:
- We count total objects in the Data Catalog (databases, tables, table versions, partitions)
- Catalogs exceeding 1,000,000 objects are flagged with the calculated overage cost
- Catalogs still under the free tier with more than 500,000 table versions get an early warning
- The primary bloat source is table versions โ every
UpdateTablecall 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:
- We read CloudWatch
AWS/AmazonMQby broker name (ActiveMQ<name>-1, plus<name>-2for an active/standby pair; RabbitMQ<name>) - ActiveMQ: hourly maximum of
TotalMessageCount,TotalConsumerCountandTotalProducerCount. RabbitMQ:MessageCount,ConsumerCountandConnectionCount - 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
- 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:
- We query Amazon MQ for all running brokers
- CloudWatch
CpuUtilization(RabbitMQ:SystemCpuUtilization) is read by broker name and averaged over 14 days - 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%
- We identify the next smaller instance type as the downsizing target
- 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.
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:
- We identify EMR clusters in RUNNING state that have been up for more than 720 hours
- 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
- 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:
- We pull CloudWatch
YARNMemoryAvailablePercentageand running-application counts for each RUNNING cluster over 14 days - If average available memory exceeds 60%, the CORE and TASK groups with more than one instance are over-provisioned
- 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:
- We check clusters with
KeepJobFlowAliveWhenNoSteps=Truethat have been up for more than 24 hours - If no
AutoTerminationPolicyis 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 - 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:
- We check instance types in all instance groups of clusters up for more than 24 hours
- 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
- 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:
- We identify TASK instance groups with
Market=ON_DEMANDand at least 2 running instances - Task nodes are fault-tolerant โ YARN reschedules work on Spot interruption
- 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:
- We analyze Kinesis stream IncomingBytes relative to shard capacity, on streams at least 14 days old
- Each shard can handle 1 MB/s input and 2 MB/s output
- 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:
- Identifies On-Demand streams at least 30 days old with predictable traffic patterns
- Calculates coefficient of variation (CV) of daily IncomingBytes over 14 days, which needs at least 7 daily datapoints
- CV < 0.3 indicates stable throughput suitable for Provisioned mode
- 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:
- Identifies streams with retention period of more than 24 hours (the default)
- Checks if any consumer is reading data (GetRecords metric)
- 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:
- Lists enhanced fan-out consumers registered on each stream, and skips any consumer younger than 14 days
- Asks CloudWatch which metrics exist for the stream and consumer (
ListMetrics, filtered on bothStreamNameandConsumerName). AWS publishesSubscribeToShard.Successat least every 5 minutes while a consumer is subscribed, andListMetricsreports every metric that had data in the past two weeks - A consumer with no
SubscribeToShardmetric 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 callListMetrics(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:
- Lists all Firehose delivery streams
- Checks IncomingRecords and IncomingBytes metrics over 14 days
- Idle streams are flagged as forgotten infrastructure
- 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:
- We identify SageMaker notebook instances in InService status that have not been started or reconfigured within the window (LastModifiedTime)
- 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 - 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
- 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:
- We query CloudWatch for endpoint invocation metrics
- Endpoints with zero invocations over the last 7 days are flagged
- 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:
- We analyze CloudWatch metrics for endpoint CPU and memory utilization
- Endpoints with consistently low utilization are flagged
- 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:
- We identify SageMaker notebook instances in Stopped status
- Stopped notebooks still incur SageMaker notebook ML storage charges ($0.14/GB-month in us-east-1)
- Notebooks stopped for 7 days or more are flagged
- 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:
- We check instance types against a known previous-generation prefix list
- Each flagged resource includes a specific upgrade recommendation
- Covers both notebook instances and endpoint instances
- 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:
- AWS Cost Explorer analyzes 30 days of SageMaker usage (training, inference, notebooks)
- Identifies consistent usage patterns suitable for a SageMaker Savings Plan commitment
- 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:
- We check the LastAccessedDate for each secret
- Secrets last accessed more than 90 days ago are flagged
- A secret that has never been accessed is flagged (LOW confidence) once it was created more than 30 days ago
- 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:
- We list the customer-managed keys (skipping keys pending deletion) and the KMS keys that CloudTrail trails encrypt with (
KmsKeyId) - Keys no trail references are flagged as a hygiene hint, because per-key usage cannot be verified
- 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:
- We check the
encryptedattribute on all EBS volumes - Volumes in
in-usestate without encryption at rest are flagged - 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:
- We check the
StorageEncryptedattribute on all RDS instances - Unencrypted instances cannot be encrypted in-place โ requires snapshot + restore
- 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:
- We check the
Encryptedattribute on all EFS filesystems - EFS encryption must be enabled at creation โ cannot be added to existing filesystems
- 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:
- We check the
StorageEncryptedattribute on DocumentDB clusters - Unencrypted clusters cannot be encrypted in-place
- 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:
- We check for
ServerSideEncryptionConfigurationon each S3 bucket - Buckets without default encryption rely on individual object-level encryption
- 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:
- We check the
EncryptionAtRestOptionsconfiguration on OpenSearch domains - Domains without encryption at rest are flagged as a compliance issue
- 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:
- We check the
DeletionProtectionattribute on all RDS instances - Production databases without this safeguard risk accidental deletion
- 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:
- We check the
DeletionProtectionEnabledattribute on DynamoDB tables - Tables without protection can be accidentally deleted via API or console
- 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:
- We check the
PubliclyAccessibleattribute on RDS instances - Publicly accessible databases are reachable from the internet (if security groups allow)
- 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:
- We cross-reference RDS instances and DynamoDB tables against AWS Backup inventory
- Resources with zero recovery points across all vaults are flagged
- 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:
- AWS Compute Optimizer analyzes 14 days of CPU, memory, network, and disk metrics
- Returns specific recommendations (e.g., "t3.large โ t3.medium")
- 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:
- Compute Optimizer analyzes EBS IOPS and throughput patterns
- Recommends optimal volume type and size
- 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:
- Compute Optimizer analyzes Lambda memory utilization
- Recommends right-sized memory allocation
- 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:
- AWS Compute Optimizer analyzes RDS instance CPU, memory, and I/O metrics
- Returns specific downsizing recommendations (e.g., "db.r6g.xlarge โ db.r6g.large")
- 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:
- AWS Cost Explorer analyzes 30 days of EC2 usage patterns
- Identifies consistent usage that would benefit from RI commitment
- 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:
- Analyzes RDS instance usage patterns
- Recommends RI purchases for consistent database workloads
- 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:
- Analyzes all compute usage (EC2, Fargate, Lambda)
- Recommends hourly commitment level
- 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:
- CloudWise reads your active EC2 Reserved Instances weekly
- 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
- 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:
- CloudWise identifies active Convertible RIs tied to previous-gen instance families (m4, r4, c5, etc.)
- Maps each to the recommended current-gen equivalent (m7i, r7i, c7i, etc.)
- If the target type has a lower on-demand reference price, the exchange is flagged
- Exchange is free per AWS policy โ no financial outlay required
- 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:
- We check all active Savings Plans for upcoming expiration dates
- 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
- 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:
- We analyze Savings Plan utilization via Cost Explorer over the past 30 days
- 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
- 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:
- We analyze Cost Explorer data for compute spend covered vs. uncovered by Savings Plans
- Accounts with < 50% SP coverage and at least $100/month uncovered spend are flagged (and the estimated saving must reach $10/month)
- Purchasing additional SP commitment could reduce the uncovered portion by 25โ40%
- 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:
- We analyze CUR (Cost and Usage Report) line items for RI amortization
- RI fees without corresponding On-Demand equivalent usage indicate waste
- 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:
- We analyze CUR line items for Savings Plan amortization and coverage
- SP commitment cost without matching covered usage indicates waste
- 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:
- Identifies Elastic IPs attached to stopped instances
- 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
- 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:
- Identifies instances with multiple EIPs attached
- 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
- 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:
- We identify all CloudWatch log groups without a retention policy
- Log groups without retention store logs indefinitely (growing storage costs)
- 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:
- We check each log group's retention policy via the CloudWatch Logs API
- Log groups with retention of 365 days or more and more than 0.1 GB of stored data are flagged
- 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
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:
- We check each log group's stored bytes from the CloudWatch Logs API
- Log groups with exactly 0 bytes are evaluated for staleness
- If the log group was created at least 30 days ago (or last received logs at least 30 days ago), it's flagged
- 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'
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:
- We check the last ingestion timestamp for each log group
- Groups with no new events for more than 90 days and stored volume > 0.5 GB are flagged
- 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:
- AWS provides 3 free CloudWatch dashboards โ additional dashboards cost $3.00/month each
- We identify paid-tier dashboards that haven't been modified in more than 90 days
- 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:
- Analyzes CloudTrail configurations across the account, including multi-region trails homed in other regions
- Skips trails that are not logging and organization trails owned by another account
- Identifies trails with identical event selectors and reports each duplicate once, from its home region's scan
- 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:
- Identifies S3 buckets receiving CloudTrail logs from trails homed in the scanned region
- Checks for lifecycle policy to transition/expire old logs
- 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 - 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
- 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โ
๐ง 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.
| Setting | Default | Range | Description |
|---|---|---|---|
cloudwatch_enabled | true | on/off | Enable CloudWatch metrics for accurate utilization analysis |
min_waste_threshold_usd | $0.01 | minimum 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:
- Go to AWS Accounts page (Settings โ AWS Accounts)
- Click the Edit (pencil) icon on any connected account
- Scroll to Waste Detection Settings section
- 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.
| Setting | Default | Range | Description |
|---|---|---|---|
min_savings_threshold | $10.00 | $0 and up (the form caps the upper end) | Minimum total monthly savings to trigger notification |
frequency | daily | immediate / daily / weekly / disabled | How often to send waste summary notifications |
high_priority_immediate | true | on/off | Send 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:
- Go to Settings โ Notifications
- Find the Waste Detection Alerts card
- Under Slack Channel, turn on Send to Slack and paste an
https://hooks.slack.com/webhook URL - Click Test Connection
See the Slack Integration Guide for full multi-channel setup.
Where to configure:
- Go to Settings โ Notifications
- Find the Waste Detection Alerts card
- Adjust your notification preferences
- Click Save Changes
Impact: Emails are only sent if total waste across all accounts exceeds min_savings_threshold.
Recommended Settings by Use Caseโ
| Use Case | Detection Threshold | Notification Threshold | Notes |
|---|---|---|---|
| Catch Everything (default) | $0.01 | $10.00 | See all waste, get notified for meaningful amounts |
| High-Volume Accounts | $1.00 | $50.00 | Reduce noise from micro-optimizations |
| Enterprise | $5.00 | $100.00 | Focus only on significant waste |
| Cost-Conscious Startup | $0.01 | $1.00 | Every 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:
| Setting | Default | Description |
|---|---|---|
ec2_idle_cpu_threshold | 5% | CPU below this = idle |
ec2_idle_days | 14 | Days to analyze |
rds_idle_days | 14 | Days to analyze connections |
snapshot_age_days | 90 | Snapshots at least this old are flagged |
Contact support to adjust these for your organization.
๐ก Best Practicesโ
- Start with HIGH confidence items - The strongest signals; still review each one
- Review MEDIUM confidence items - Check for exceptions before acting
- Use LOW confidence items for cleanup - Not cost savings, but good hygiene
- Enable CloudWatch metrics - Required for idle detection
- Tag your resources - Helps identify ownership for waste items
- Schedule regular reviews - Check your findings on a regular rhythm