AWS Builder Center

How to Put a Hard Spending Limit on Your AWS Account (Before It's Too Late)

AWS has no built-in billing cap, but you can build one. A practical 5-layer guardrail using Budgets, Budget Actions, a Lambda kill switch, Cost Anomaly Detection, and SCPs with copy-paste code and the honest caveats.

Why AWS doesn't have a billing cap

Every few weeks, someone posts a screenshot of an unexpected AWS bill. The usual causes are boring: an EC2 instance nobody stopped, a Lambda function recursively invoking itself, a public S3 bucket getting scraped, or a compromised key spinning up GPU instances for crypto mining.
AWS's official position is that a hard cap could take down production workloads mid-transaction, so they give you alerts and actions instead of a cap. That's a reasonable stance for a bank and a terrible default for a student, a hobbyist, or a startup running on a credit card.
The good news: you can build a cap yourself. Not a perfect one (more on that below), but one that turns a $2,000 mistake into a $60 mistake.

Layer 1 AWS Budgets with forecasted alerts

Start with the basics. In Billing and Cost Management → Budgets, create a monthly cost budget and add at least three alerts:
Threshold
Type
Why
50%
Actual
Early heads-up
80%
Forecasted
Catches spikes before they land
100%
Actual
Something is wrong right now
The forecasted alert is the one most people skip, and it's the most valuable: AWS extrapolates your current run rate and warns you while there's still time to react.
Send alerts to an email and an SNS topic the SNS topic is what makes the automation in Layer 3 possible.

Layer 2 Budget Actions: let AWS pull the plug for you

This is the feature most people don't know exists. A Budget Action runs automatically when a threshold is crossed. It can:
  • Attach a deny-all IAM policy to a user, group, or role
  • Attach an SCP to an OU or account (if you use Organizations)
  • Stop specific EC2 or RDS instances
A practical setup for a personal or dev account:
  1. Create a customer-managed policy called  BudgetLockdown  that denies  ec2:RunInstances ,  lambda:InvokeFunction ,  sagemaker:* ,  rds:CreateDBInstance , and anything else that costs money to start.
  1. Create a Budget Action at 100% actual spend that attaches  BudgetLockdown  to your main IAM role.
  1. Set the action to run automatically (not "require approval") approval emails at 3 AM don't help.
Existing data is untouched. You just can't create new spend until you remove the policy.

Layer 3 A Lambda kill switch for what Budget Actions can't reach

Budget Actions only stop EC2 and RDS directly. For everything else, subscribe a small Lambda function to the SNS topic from Layer 1 and let it clean up. A minimal example that stops every running EC2 instance not tagged  Protected=true :
python
Python
import boto3

ec2 = boto3.client("ec2")

def handler(event, context):
reservations = ec2.describe_instances(
Filters=[{"Name": "instance-state-name", "Values": ["running"]}]
)["Reservations"]

to_stop = []
for r in reservations:
for i in r["Instances"]:
tags = {t["Key"]: t["Value"] for t in i.get("Tags", [])}
if tags.get("Protected") != "true":
to_stop.append(i["InstanceId"])

if to_stop:
ec2.stop_instances(InstanceIds=to_stop)
return {"stopped": to_stop}
Extend the same pattern for what actually bites you:
  • Lambda: set reserved concurrency to  0  on non-protected functions ( put_function_concurrency )
  • ECS/EKS: scale services to zero desired tasks
  • SageMaker: delete endpoints (they're the classic "I forgot it was on" cost)
  • NAT Gateways: delete them; recreating takes a minute
Tag anything critical with  Protected=true  so the kill switch skips it.

Layer 4 Cost Anomaly Detection

Budgets are about totals. Cost Anomaly Detection is about shape: it uses machine learning to notice when a service that normally costs $3/day suddenly costs $40/day, even if you're still under budget. It's free, takes two minutes to enable, and catches leaked keys faster than any threshold alert.
Set the alert threshold low (e.g. $10 impact) for a personal account.

Layer 5 SCPs for sandbox accounts (if you use Organizations)

If you manage dev or learning accounts for a team, class, or community, don't rely on people remembering to stop things. Put an SCP on the sandbox OU that blocks expensive things from ever starting:
json
JSON
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "OnlyCheapInstances",
"Effect": "Deny",
"Action": "ec2:RunInstances",
"Resource": "arn:aws:ec2:*:*:instance/*",
"Condition": {
"StringNotLike": {
"ec2:InstanceType": ["t3.*", "t4g.*", "t3a.*"]
}
}
},
{
"Sid": "NoExpensiveServices",
"Effect": "Deny",
"Action": [
"sagemaker:*",
"redshift:*",
"emr:*",
"es:*"
],
"Resource": "*"
}
]
}
Adjust the list to your needs. The point is that a GPU instance simply cannot be launched in a sandbox, no matter whose key was leaked.

The honest caveats

A DIY cap is a big improvement, but it's not airtight:
  • Billing data lags. AWS cost data can be 8–24 hours behind. A fast spike can blow past your threshold before Budgets notices. Layers 4 and 5 exist precisely because of this.
  • Some costs keep running. Storage (S3, EBS, snapshots), Elastic IPs, and data transfer already in flight aren't "stopped" by anything above. That's a feature you don't want your data deleted but it's not zero.
  • Root account safety. Budget Actions can't restrict the root user. Lock it down with MFA and never use it day-to-day.
  • Test it. Trigger the SNS topic manually once and confirm the Lambda actually stops what you expect. A kill switch you've never tested is a wish, not a guardrail.

Quick checklist

  • Monthly budget with 50% / 80% forecasted / 100% alerts → email + SNS
  • Budget Action that attaches a deny policy at 100%, set to auto-run
  • Lambda kill switch subscribed to the SNS topic, with  Protected=true  tag support
  • Cost Anomaly Detection enabled with a low threshold
  • SCP on sandbox accounts blocking expensive instance types and services
  • Root MFA on, access keys rotated,  git-secrets  or similar in your repos

One more thing

All of this is a workaround for a feature AWS could ship natively. If you'd rather have an official opt-in hard spending cap one that pauses compute at a dollar limit without deleting data there's a wish for it on the AWS Builder Center Wishlist. Upvote it and add your own surprise-bill story; the more votes it gets, the more likely it lands on a roadmap.
Have a cost guardrail trick I missed? Drop it in the comments I'll add the best ones to this post.
Any opinions in this article are those of the individual author and may not reflect the opinions of AWS.
Enjoyed reading this content? Let the author know!

Your likes, comments, shares, and saves help creators reach more builders.

Loading recommendations

Loading article