Skip to main content
INS // Insights

AWS Cost Monitoring Setup That Actually Catches Overruns

Updated June 2026 · 6 min read

Most AWS cost monitoring setups only tell you about overruns after the bill arrives. A properly configured monitoring system catches cost anomalies within hours, attributes costs to the teams and services driving them, and enforces budget thresholds before month-end surprises hit finance. Here's how to build monitoring that actually works.

Why Default AWS Billing Alerts Fail

AWS provides basic billing alerts out of the box — you can set a threshold and get an SNS notification when your total spend exceeds it. This is nearly useless in practice because:

  • A single threshold for your entire AWS spend doesn't tell you what drove the overrun
  • Alerts fire after the threshold is already exceeded, not as you're approaching it
  • No context on which service, region, or team caused the spike
  • No trend analysis — a gradual 30% month-over-month increase flies under the radar

Effective cost monitoring requires layered alerts: per-service, per-team (via tags), forecasted monthly spend (not just current), and anomaly detection that catches unusual patterns regardless of total spend level.

Layer 1: Cost Allocation Tags

Meaningful cost monitoring starts with tagging. Without consistent cost allocation tags, all your monitoring tells you is "AWS costs went up" — not who or what caused it.

Required tags minimum:

# In Terraform — enforce at resource creation
locals {
  required_tags = {
    Environment = var.environment          # prod, staging, dev
    Team        = var.team                 # engineering, data, platform
    Service     = var.service_name         # api, worker, ml-pipeline
    CostCenter  = var.cost_center          # billing allocation code
  }
}

resource "aws_instance" "app" {
  # ... instance config
  tags = merge(local.required_tags, {
    Name = "${var.service_name}-${var.environment}"
  })
}

Enable cost allocation tags in the AWS Billing console — newly created tags don't appear in Cost Explorer until activated. Plan for 24-48 hour propagation lag before new tags show up in billing data.

Tag enforcement: Use AWS Config rules or SCP (Service Control Policies) to flag or block resources missing required tags. A tagging policy only works if it's actually enforced — honor-system tagging degrades within weeks.

Layer 2: AWS Budgets with Forecasted Alerts

Unlike basic billing alerts that fire when you exceed a threshold, AWS Budgets supports forecasted alerts — alerting you when your projected month-end spend is on track to exceed the budget, even if you haven't hit the threshold yet.

import boto3

budgets = boto3.client("budgets", region_name="us-east-1")

def create_service_budget(account_id: str, service_name: str, monthly_limit_usd: float):
    budgets.create_budget(
        AccountId=account_id,
        Budget={
            "BudgetName": f"{service_name}-monthly-budget",
            "BudgetLimit": {
                "Amount": str(monthly_limit_usd),
                "Unit": "USD"
            },
            "TimeUnit": "MONTHLY",
            "BudgetType": "COST",
            "CostFilters": {
                "TagKeyValue": [f"Service${service_name}"]
            }
        },
        NotificationsWithSubscribers=[
            {
                "Notification": {
                    "NotificationType": "FORECASTED",   # <-- forecasted, not actual
                    "ComparisonOperator": "GREATER_THAN",
                    "Threshold": 80.0,
                    "ThresholdType": "PERCENTAGE",
                },
                "Subscribers": [{"SubscriptionType": "EMAIL", "Address": "alerts@yourcompany.com"}]
            },
            {
                "Notification": {
                    "NotificationType": "ACTUAL",
                    "ComparisonOperator": "GREATER_THAN",
                    "Threshold": 100.0,
                    "ThresholdType": "PERCENTAGE",
                },
                "Subscribers": [{"SubscriptionType": "SNS", "Address": "arn:aws:sns:..."}]
            }
        ]
    )

Create separate budgets per service/team rather than one global budget. You want to know which service caused the overrun, not just that an overrun happened.

Layer 3: AWS Cost Anomaly Detection

Cost Anomaly Detection uses machine learning to identify unusual spend patterns — a service that normally costs $50/day suddenly costing $800/day, or a new resource type appearing that wasn't there before.

ce = boto3.client("ce", region_name="us-east-1")

# Create a monitor for all services
ce.create_anomaly_monitor(
    AnomalyMonitor={
        "MonitorName": "AllServicesMonitor",
        "MonitorType": "DIMENSIONAL",
        "MonitorDimension": "SERVICE"
    }
)

# Create subscription with threshold
ce.create_anomaly_subscription(
    AnomalySubscription={
        "SubscriptionName": "CostAnomalyAlerts",
        "MonitorArnList": ["arn:aws:ce::..."],
        "Subscribers": [{
            "Address": "arn:aws:sns:...",
            "Type": "SNS"
        }],
        "Threshold": 50.0,    # Alert if anomaly exceeds $50
        "Frequency": "DAILY"
    }
)

Anomaly detection is particularly valuable for catching runaway workloads, forgotten dev environments, and accidental resource deployments that might not cross an absolute budget threshold but represent unusual behavior.

Layer 4: Cost Explorer Dashboards and Reporting

Automated alerts tell you when something's wrong. Cost Explorer dashboards tell you the ongoing story. Build out:

Daily cost by service view: Configure a saved Cost Explorer view grouped by service, filtered to the last 30 days, with a daily granularity. Share the link with engineering leads — 5 minutes of review per week catches gradual cost creep before it becomes a surprise.

Tag-based team dashboards: A Cost Explorer view filtered by Team = engineering with service breakdown shows the engineering team's total spend and which services are driving it. Essential for team-level accountability.

Cost trend views: Month-over-month comparison views catch gradual increases that don't trigger threshold alerts. A 15% month-over-month increase for 3 months is a significant problem even if it never triggered a single alert.

Layer 5: Custom Lambda for Slack Alerts

Getting alerts via email is better than nothing; getting them in Slack where your team already works is 10x more actionable:

import json
import urllib.request
import boto3
from datetime import datetime, timedelta

def lambda_handler(event, context):
    ce = boto3.client("ce", region_name="us-east-1")

    end = datetime.today().strftime("%Y-%m-%d")
    start = (datetime.today() - timedelta(days=1)).strftime("%Y-%m-%d")

    response = ce.get_cost_and_usage(
        TimePeriod={"Start": start, "End": end},
        Granularity="DAILY",
        Metrics=["UnblendedCost"],
        GroupBy=[{"Type": "DIMENSION", "Key": "SERVICE"}]
    )

    results = response["ResultsByTime"][0]["Groups"]
    total = sum(float(r["Metrics"]["UnblendedCost"]["Amount"]) for r in results)
    top_services = sorted(results, key=lambda x: float(x["Metrics"]["UnblendedCost"]["Amount"]), reverse=True)[:5]

    lines = [f"*AWS Daily Spend: ${total:.2f}*\nTop services:"]
    for s in top_services:
        cost = float(s["Metrics"]["UnblendedCost"]["Amount"])
        lines.append(f"  • {s['Keys'][0]}: ${cost:.2f}")

    message = "\n".join(lines)

    # Post to Slack
    payload = json.dumps({"text": message}).encode()
    req = urllib.request.Request(
        SLACK_WEBHOOK_URL,
        data=payload,
        headers={"Content-Type": "application/json"}
    )
    urllib.request.urlopen(req)

A daily Slack digest of AWS spend takes 30 seconds to read and keeps cost awareness as ambient team knowledge rather than something only finance tracks.

Rutagon Builds FinOps Systems That Scale

If your AWS bill has become opaque or you're discovering overruns at the end of each month, Rutagon sets up and operationalizes cost monitoring infrastructure. Talk to us at rutagon.com/contact.

Related reading: AWS Reserved Instances Strategy and FinOps capabilities.

FAQ

How quickly does AWS Cost Explorer data update?

Cost Explorer data is typically 24 hours behind. For near-real-time cost visibility, use AWS Cost Anomaly Detection (which uses the same underlying data but alerts faster) or CloudWatch metrics for specific services. For truly real-time resource cost tracking, AWS-managed services don't provide it — you'd need to correlate resource creation events with pricing data.

What's the minimum AWS spend where this level of monitoring is worth it?

At $1,000+/month, tag-based allocation and Budget alerts start paying for themselves. At $5,000+/month, Cost Anomaly Detection is justified. At $15,000+/month, a dedicated FinOps practice (even part-time) typically saves more than it costs. Below $1,000/month, simpler billing alerts and monthly Cost Explorer reviews are usually sufficient.

How do you handle cost allocation for shared infrastructure (VPCs, load balancers, etc.)?

Shared infrastructure should be tagged to a "platform" or "infrastructure" team and allocated across consuming teams proportionally. Some organizations use showback (showing each team what they'd be charged) without actual chargeback. Others use blended rates to allocate shared costs proportionally. The most important thing is deciding on a consistent method and applying it — imperfect allocation with consistency beats perfect allocation without it.

Can you set hard spending limits in AWS to prevent overruns?

AWS doesn't provide hard spending limits that stop resource creation (unlike some cloud providers). AWS Budgets Action can take automated actions when thresholds are met — like applying an SCP to restrict specific resource creation or sending a Lambda-triggered remediation. These are best-effort guardrails, not hard stops. For strict spending control, Sentinel policies in Terraform or custom approval gates for expensive resources provide more reliable enforcement at the infrastructure-as-code layer.

What's the most common cost overrun cause for growing SaaS companies?

Data transfer costs and unoptimized database queries. Data transfer (especially cross-region and to the internet) is often invisible until it accumulates. RDS or DynamoDB query patterns that worked at 10,000 users can become expensive at 100,000 users without architectural changes. Set specific monitoring for both data transfer costs and database costs as early budget lines.

Ready to discuss your project?

We deliver production-grade software for government, defense, and commercial clients. Let's talk about what you need.

Initiate Contact