All Detectors
Complete reference for all 40+ cost optimization and security posture checks in StackSage. Every check runs entirely on your machine or in CI — no data leaves your environment.
Cost optimization and security posture, in one scan
Runs on your machine or in CI — data never leaves your environment
Uses real CloudWatch metrics — not just inventory snapshots
💻Compute
EC2 Idle Instances
HighInstances with <5% average CPU utilization over 14 days. Likely candidates for downsizing or termination.
idle_ec2$50–500/mo per instanceEC2 Generation Upgrade
MediumOld-generation instance types (t2→t3, m4→m5/m6i, c4→c5/c6i, r4→r5/r6i). Newer generations are 5–15% cheaper with better performance.
ec2_generation_upgrade$10–100/mo per instanceStopped EC2 Instances
MediumEC2 instances in stopped state for >14 days. Stopped instances still incur EBS storage costs and block Elastic IP allocations.
stopped_ec2$5–50/mo per instanceLambda Memory Overprovisioning
MediumLambda functions using <30% of their timeout duration while configured with >256 MB memory. Reducing memory allocation saves ~20% in compute costs.
overprovisioned_lambda$10–100/mo per functionLambda Graviton Migration (arm64)
Lowx86_64 Lambda functions that can migrate to arm64 (Graviton2) for ~20% compute savings with no code changes in most runtimes.
lambda_graviton_migration$1–50/mo per functionEC2 → Serverless Migration Opportunities
MediumAlways-on small EC2 instances (<2 vCPU, <4 GB RAM) with sustained low CPU — candidates for Lambda + API Gateway migration.
architecture_opportunity$5–200/mo per instance📦Containers
ECS (EC2) → Fargate Migration
LowECS services on the EC2 launch type with low task utilization. Moving to Fargate eliminates idle EC2 capacity costs.
ecs_to_fargate$5–200/mo per serviceFargate Spot Opportunities
LowFargate services running on standard capacity in environments tagged dev/test/staging. Fargate Spot cuts costs up to 70% for fault-tolerant workloads.
fargate_spot_opportunity$5–200/mo per service💾Storage
EBS Unattached Volumes
HighVolumes not attached to any running instance for >7 days. Likely forgotten after instance termination.
unattached_ebs$10–200/mo per volumeEBS Overprovisioned Performance
Mediumio1/io2 or gp3 volumes with <20% IOPS or throughput utilization over 14 days. Lowering provisioned IOPS reduces cost without affecting performance.
ebs_overprovisioned_performance$20–500/mo per volumeGP2 to GP3 Migration
Mediumgp2 volumes that can be migrated to gp3 for a straight 20% savings at equivalent baseline IOPS, with the option to provision more IOPS/throughput separately.
gp2_to_gp3_migration$5–100/mo per volumeOld EBS Snapshots
LowSnapshots older than 90 days with no associated volume. Often leftover from deleted instances or manual backup workflows.
old_snapshot$5–50/mo per snapshotEBS Snapshot Consolidation
LowMultiple recent snapshots of the same volume in a short window — suggests redundant manual backups that could be replaced with a lifecycle policy.
snapshot_consolidation$5–30/mo per volume🗄️Database
RDS Underutilized (CPU + Storage)
HighRDS instances with <10% average CPU and >70% free storage over 14 days. Suggests the instance is significantly oversized and can be downsized.
underutilized_rds$50–500/mo per instanceRDS Low Connections
MediumRDS instances averaging <10 connections over 14 days — may indicate an orphaned or forgotten database.
rds_low_connections$50–500/mo per instanceDynamoDB Unused Tables
MediumTables with zero consumed read/write capacity for 30+ days. Likely test or abandoned tables still incurring provisioned capacity costs.
dynamodb_unused_table$5–200/mo per tableRDS → Aurora Serverless v2 Candidates
MediumSpiky RDS workloads (high peak CPU, low average CPU) that may benefit from Aurora Serverless v2's auto-scaling capacity units rather than a fixed instance size.
rds_to_aurora_serverless_v2$5–200/mo per instance⚡Caching
ElastiCache Idle Clusters
MediumRedis/Memcached clusters with <5% cache hit rate and <2 active connections over 14 days. Likely unused or orphaned.
elasticache_idle_cluster$20–300/mo per cluster🌐Network
NAT Gateway Advisory
LowAll active NAT gateways are reported as a cost-awareness advisory. NAT gateways incur a fixed hourly charge (~$32/mo) plus per-GB data processing fees.
nat_gatewayVaries by data volumeNAT Gateway Idle
HighNAT gateways processing <1 GB/day over 14 days — likely serving no active traffic and can be deleted.
idle_nat_gateway$30–45/mo per NATNAT Gateway Egress Cost
MediumActive NAT gateways with quantified data-processing costs (CloudWatch BytesOut + BytesIn, extrapolated to 30 days at $0.045/GB). Cross-references missing S3 and DynamoDB VPC endpoints in the same VPC as the primary remediation path.
nat_gateway_egress_costVaries — $0.045/GB eliminated by VPC endpointsMissing S3 VPC Endpoint
LowNo S3 gateway VPC endpoint detected. Private instances accessing S3 through NAT incur unnecessary data processing charges. Gateway endpoints are free.
missing_s3_vpc_endpoint$5–50/moMissing DynamoDB VPC Endpoint
LowNo DynamoDB Gateway VPC endpoint detected. Lambda and ECS workloads in private subnets hitting DynamoDB through NAT incur $0.045/GB data-processing charges. Gateway endpoints are free and eliminate this cost entirely.
missing_dynamodb_vpc_endpoint$5–100/moElastic IP Unattached
LowElastic IPs not associated with any running instance. AWS charges $0.005/hr (~$3.60/mo) per idle EIP.
unused_eip$3–5/mo per EIPLoad Balancer Idle
MediumClassic ELBs, ALBs, and NLBs with <10 requests/day over 14 days. Likely leftover from old deployments.
idle_elb$20–30/mo per LBLoad Balancer Empty Target Groups
HighALB/NLB load balancers where all target groups have zero healthy targets for >7 days — the load balancer is running but serving no traffic.
lb_empty_target_groups$15–40/mo per load balancer🌍Content Delivery & DNS
CloudFront Unused Distributions
LowDistributions receiving <100 total requests over the last 30 days. Often forgotten CDN endpoints from old deployments.
cloudfront_unused_distribution$1–10/mo per distributionRoute 53 Unused Hosted Zones
MediumHosted zones with zero DNS queries over the last 90 days. AWS charges $0.50/mo per zone plus per-query fees.
route53_unused_hosted_zone$0.50–50/mo per zone📋CloudWatch Logs
Log Group Retention Not Set
MediumCloudWatch Log Groups with 'Never Expire' retention that are storing significant data. Setting a 30/90/365-day retention policy typically saves ~60% of log storage costs.
cloudwatch_logs_retentionVaries by log volumeLog Group: No Metric Filters
Low/MediumLog groups storing ≥1 GB with zero metric filters defined — stored but never analysed. This is the strongest signal that retention can be reduced aggressively with low operational risk. Pairs with the retention finding to confirm it is safe to act. Reports $0 savings to avoid double-counting with the retention detector.
cloudwatch_logs_no_metric_filtersCorroborating signal — see Log Group Retention finding🪣S3
S3 Lifecycle Suggestions
LowS3 buckets with no lifecycle configuration. Adding tiering rules (Standard → Infrequent Access → Glacier) reduces storage costs for buckets with older objects.
s3_lifecycle_suggestionVaries by bucket sizeS3 Access Logging Disabled
LowBuckets without server access logging enabled. Access logs are the prerequisite for confident lifecycle and Intelligent-Tiering decisions — without them, you cannot know object access frequency and any optimisation recommendation is a guess.
s3_access_logging_disabledEnabler — unlocks confident lifecycle and IT decisionsS3 Intelligent-Tiering Not Configured
LowBuckets with no S3 Intelligent-Tiering configuration. IT automatically moves objects between Frequent and Infrequent Access tiers — no lifecycle rules to manage. Typical savings: 40–60% on infrequently accessed data. Enable access logging first to validate access patterns.
s3_intelligent_tiering_not_configuredVaries — typically 40–60% on IA-eligible data🛡️Cost Guardrails
AWS Budgets Not Configured
MediumNo AWS Budgets found in the account. Budgets alert you when spend approaches or exceeds a threshold, preventing surprise bills.
cost_guardrail_missing_budgetsPrevents cost overrunsCost Anomaly Detection Not Enabled
MediumNo AWS Cost Anomaly Detection monitors configured. Anomaly detection uses ML to alert on unexpected spend spikes within hours of them occurring.
cost_guardrail_missing_anomaly_detectionPrevents cost overruns🔒Security & Posture
Security posture checks run automatically alongside cost checks when a live AWS session is available. They are privacy-first: only aggregate counts and boolean flags are recorded — never raw resource lists or policy documents.
IAM Root MFA & Access Keys
CriticalChecks that root MFA is enabled and root access keys do not exist. Root account compromise is catastrophic — these two checks are non-negotiable baselines.
iam_root_mfa_disabled / iam_root_access_keys_presentSecurity postureIAM Password Policy
MediumVerifies a password policy exists and meets baseline requirements: minimum length ≥14, complexity enabled, reuse prevention ≥24, max age ≤90 days.
iam_password_policy_missing / iam_password_policy_weakSecurity postureIAM Access Key Hygiene
MediumDetects IAM access keys that are active and older than 90 days (should be rotated) or that have never been used (should be deleted).
iam_access_key_hygieneSecurity postureSecurity Groups Open to Internet
HighIdentifies security groups with inbound rules allowing 0.0.0.0/0 or ::/0 on sensitive ports (SSH 22, RDP 3389, database ports, Redis 6379, etc.).
sg_open_to_worldSecurity postureRDS Security Basics
HighChecks each RDS instance for three issues: publicly accessible flag enabled, storage encryption disabled, and backup retention period below 7 days.
rds_publicly_accessible / rds_storage_not_encrypted / rds_backup_retention_lowSecurity postureS3 Block Public Access
HighVerifies all four S3 account-level Block Public Access settings are enabled, and checks individual bucket policy status for any buckets that appear publicly accessible.
s3_account_public_access_block_disabled / s3_buckets_publicSecurity postureS3 Default Encryption
MediumIdentifies S3 buckets missing a default encryption configuration (SSE-S3 or SSE-KMS). Older buckets created before AWS made encryption the default may still be missing it.
s3_bucket_default_encryption_missingSecurity postureCloudTrail Audit Logging
HighConfirms a CloudTrail trail exists, is configured as multi-region, and is actively logging. Without CloudTrail there is no audit record of AWS API calls.
cloudtrail_not_configuredSecurity postureEBS Encryption by Default
MediumChecks that the EBS encryption-by-default setting is enabled in the scanned region, ensuring all new volumes and snapshots are automatically encrypted with KMS.
ebs_encryption_by_default_disabledSecurity postureAWS Config
MediumVerifies AWS Config is enabled and its recorder is actively recording. Config provides a continuous record of resource configuration changes and is foundational for compliance.
aws_config_not_enabled / aws_config_not_recordingSecurity postureAmazon GuardDuty
MediumChecks GuardDuty is enabled in all scanned regions. GuardDuty provides continuous threat detection using ML on CloudTrail logs, VPC Flow Logs, and DNS query logs.
guardduty_not_enabledSecurity postureCloudWatch Alarms Presence
LowFlags accounts with no CloudWatch alarms found in any scanned region. Alarms on billing thresholds, error rates, and CPU are basic operational hygiene.
cloudwatch_alarms_missingSecurity postureAWS Security Hub
LowChecks Security Hub is enabled in scanned regions. Security Hub aggregates findings from GuardDuty, Inspector, Macie, and third-party tools into a single dashboard.
securityhub_not_enabledSecurity postureIAM Access Analyzer External Access
HighIf IAM Access Analyzer is enabled, reports the count of active external access findings (resources shared outside the account). Only the count is recorded — no policy details.
iam_access_analyzer_external_accessSecurity postureIncident Response Readiness
MediumMeta-check: if CloudTrail, GuardDuty, AWS Config, or Security Hub findings are detected above, this finding summarises the combined incident response gap.
incident_response_readiness_lowSecurity posture🏷️Tagging Compliance
The tagging check is opt-in and must be explicitly enabled in stacksage.yml:
detectors:
check_tagging_compliance: trueUntagged Resources
LowEC2 instances, EBS volumes (≥50 GB), and RDS instances with zero user-defined tags. Detects complete absence of tags rather than checking for specific required keys — less opinionated and universally applicable.
untagged_resourceOpt-inDisabling Detectors
You can suppress specific finding types using the exclusions.detectors key in stacksage.yml. The list accepts the finding type strings shown in each card above:
exclusions:
detectors:
- ec2_generation_upgrade # skip generation upgrade suggestions
- gp2_to_gp3_migration # skip gp2→gp3 migration hints
- nat_gateway # skip NAT advisory findingsDetector Confidence Levels
Each finding includes a confidence score (0.0–1.0) indicating estimated accuracy:
- 0.90–1.00: Very high — deterministic checks (e.g., unattached EBS, unused EIP)
- 0.75–0.89: High — metric-based with clear signal (e.g., low CloudWatch utilization)
- 0.60–0.74: Medium — heuristic or indirect evidence; verify before acting