AWS Costs & Cleanup Guide

Critical

Understand AWS pricing, avoid unexpected bills, and clean up resources properly

Read This Before You Start!

AWS charges for resources even when you're not using them. A forgotten EC2 instance or NAT Gateway can cost $30-100+/month. Always clean up resources when done experimenting.

AWS Free Tier Overview

AWS offers a Free Tier for 12 months after account creation. However, many services have limits, and exceeding them results in charges.

ServiceFree Tier LimitAfter Free Tier / Over Limit
S35 GB storage, 20K GET, 2K PUT~$0.023/GB/month
SQS1 million requests/month$0.40 per million requests
Cognito50,000 MAU (always free)$0.0055 per MAU above 50K
CloudWatch Logs5 GB ingestion, 5 GB storage$0.50/GB ingested, $0.03/GB stored
Secrets ManagerNo free tier$0.40/secret/month + $0.05 per 10K API calls
ECS FargateNo free tier~$0.04/hour for 0.25 vCPU, 0.5 GB
ALBNo free tier~$16/month minimum + data transfer
NAT GatewayNo free tier~$32/month + $0.045/GB processed
RDS PostgreSQL750 hours db.t3.micro~$12-25/month for small instance
ECR500 MB storage$0.10/GB/month

Most Expensive Gotchas

  • NAT Gateway: $32/month even with zero traffic - DELETE FIRST!
  • ALB: $16/month minimum - delete when not using
  • RDS: Runs 24/7 unless stopped - stop or delete when done
  • Elastic IP: Free when attached, $3.60/month when unattached
  • ECS Fargate: Charges per second while running

Estimated Monthly Costs

Development/Demo Environment (Minimal)

ResourceConfigurationEst. Monthly Cost
S3 Bucket1 GB storage$0.02
SQS Queue10K messagesFree (under limit)
Cognito100 usersFree (under 50K MAU)
CloudWatch Logs1 GB logsFree (under limit)
Secrets Manager2 secrets$0.80
Total (No Fargate)~$1/month

Production Environment (Small App)

ResourceConfigurationEst. Monthly Cost
ECS Fargate1 task, 0.5 vCPU, 1 GB (24/7)$15-20
ALBBasic usage$16-20
NAT GatewayLow traffic$32-35
RDS PostgreSQLdb.t3.micro$12-15
S310 GB storage$0.25
CloudWatch5 GB logs$2-3
Other (SQS, Cognito, Secrets)Light usage$2-5
Total$80-100/month

Cost Saving: Skip NAT Gateway

For development, deploy your Fargate tasks in public subnets with public IPs instead of using a NAT Gateway. This saves $32+/month but is not recommended for production.

How to Monitor Costs

1. Set Up Billing Alerts (DO THIS FIRST!)

Plain Text
AWS Console → Billing → Budgets → Create budget

1. Choose "Cost budget"
2. Set monthly budget (e.g., $10 for learning)
3. Set alert threshold at 80% ($8)
4. Add your email for notifications
5. Create budget

2. Check Cost Explorer Daily

Plain Text
AWS Console → Cost Management → Cost Explorer

- View costs by service
- Identify unexpected charges
- Check daily trends

3. AWS CLI Cost Check

Terminal
$aws ce get-cost-and-usage --time-period Start=2024-01-01,End=2024-01-31 --granularity MONTHLY --metrics BlendedCost --group-by Type=DIMENSION,Key=SERVICE
{
  "ResultsByTime": [{
    "Groups": [
      {"Keys": ["Amazon S3"], "Metrics": {"BlendedCost": {"Amount": "0.50"}}},
      {"Keys": ["AWS Fargate"], "Metrics": {"BlendedCost": {"Amount": "15.20"}}}
    ]
  }]
}

Cleanup Order Matters!

Delete resources in the correct order to avoid errors. Some resources depend on others and will fail to delete if dependencies exist.
  1. ECS Services (stop tasks first)
  2. ECS Cluster
  3. ALB + Target Groups
  4. NAT Gateway (release Elastic IP after)
  5. RDS Instances (skip final snapshot if not needed)
  6. S3 Buckets (empty bucket first)
  7. SQS Queues
  8. Cognito User Pools
  9. CloudWatch Log Groups
  10. Secrets Manager Secrets
  11. ECR Repositories (delete images first)
  12. VPC (last - after all resources using it)
  13. IAM Users/Roles (if no longer needed)

1. Delete ECS Fargate Resources

Terminal
$# Stop and delete ECS service aws ecs update-service --cluster my-cluster --service my-service --desired-count 0 aws ecs delete-service --cluster my-cluster --service my-service --force # Delete cluster (after all services removed) aws ecs delete-cluster --cluster my-cluster
Service deleted successfully

Console: ECS → Clusters → Select cluster → Services → Delete service → Delete cluster

2. Delete ALB and Target Groups

Terminal
$# Delete load balancer aws elbv2 delete-load-balancer --load-balancer-arn arn:aws:elasticloadbalancing:... # Delete target groups aws elbv2 delete-target-group --target-group-arn arn:aws:elasticloadbalancing:...
Load balancer deletion initiated

Console: EC2 → Load Balancers → Select → Actions → Delete

3. Delete NAT Gateway (Most Expensive!)

Terminal
$# Delete NAT Gateway aws ec2 delete-nat-gateway --nat-gateway-id nat-0123456789abcdef # Wait for deletion, then release Elastic IP aws ec2 release-address --allocation-id eipalloc-0123456789abcdef
NAT Gateway deleted

Don't Forget the Elastic IP!

After deleting a NAT Gateway, you must also release its Elastic IP. Unattached Elastic IPs cost $0.005/hour (~$3.60/month).

4. Delete RDS Instance

Terminal
$# Delete without final snapshot (for dev/test only!) aws rds delete-db-instance --db-instance-identifier my-database --skip-final-snapshot --delete-automated-backups
DB instance deletion initiated

RDS Alternatives

  • Stop (not delete): Pauses billing but keeps data. Auto-restarts after 7 days!
  • Snapshot then delete: Save data, pay only for snapshot storage (~$0.02/GB)

5. Empty and Delete S3 Bucket

Terminal
$# Empty bucket (required before deletion) aws s3 rm s3://my-bucket-name --recursive # Delete bucket aws s3 rb s3://my-bucket-name
Bucket deleted

Versioned Buckets

If versioning is enabled, you must also delete all object versions:aws s3api delete-objects --bucket my-bucket --delete "$(aws s3api list-object-versions --bucket my-bucket --query '{Objects: Versions[].{Key:Key,VersionId:VersionId}}')"

6. Delete SQS Queue

Terminal
$aws sqs delete-queue --queue-url https://sqs.ap-southeast-1.amazonaws.com/123456789012/my-queue
Queue deleted

7. Delete Cognito User Pool

Terminal
$# First delete domain if configured aws cognito-idp delete-user-pool-domain --domain my-app --user-pool-id ap-southeast-1_xxxxx # Then delete user pool aws cognito-idp delete-user-pool --user-pool-id ap-southeast-1_xxxxx
User pool deleted

User Data Loss

Deleting a Cognito User Pool permanently deletes all user accounts. Export user data first if needed.

8. Delete CloudWatch Log Groups

Terminal
$aws logs delete-log-group --log-group-name /my-app/logs
Log group deleted

9. Delete Secrets Manager Secrets

Terminal
$# Schedule deletion (7-30 day recovery window) aws secretsmanager delete-secret --secret-id my-app/secrets --recovery-window-in-days 7 # Force immediate deletion (no recovery!) aws secretsmanager delete-secret --secret-id my-app/secrets --force-delete-without-recovery
Secret scheduled for deletion

10. Delete ECR Repository

Terminal
$# Delete all images and repository aws ecr delete-repository --repository-name my-app --force
Repository deleted

11. Delete VPC (Last!)

Delete VPC only after all resources using it are gone:

Terminal
$# Delete subnets first aws ec2 delete-subnet --subnet-id subnet-xxxxx # Delete internet gateway aws ec2 detach-internet-gateway --internet-gateway-id igw-xxxxx --vpc-id vpc-xxxxx aws ec2 delete-internet-gateway --internet-gateway-id igw-xxxxx # Delete route tables (except main) aws ec2 delete-route-table --route-table-id rtb-xxxxx # Delete security groups (except default) aws ec2 delete-security-group --group-id sg-xxxxx # Finally delete VPC aws ec2 delete-vpc --vpc-id vpc-xxxxx
VPC deleted

AWS Copilot Cleanup (Easiest!)

If you deployed with AWS Copilot, cleanup is much simpler:

Terminal
$# Delete everything in one command! copilot app delete --name my-app
Are you sure you want to delete application my-app? (y/n) y
✔ Deleted service frontend from environment production
✔ Deleted environment production
✔ Deleted environment staging
✔ Deleted application my-app

Copilot Handles Dependencies

copilot app delete automatically deletes in the correct order: services → environments → application. It handles ALB, ECS, VPC, and more.

Terraform Cleanup

Terminal
$cd infra/terraform terraform destroy
Plan: 0 to add, 0 to change, 15 to destroy.

Do you really want to destroy all resources? (yes/no) yes

Destroy complete! Resources: 15 destroyed.

Terraform State

terraform destroy only works if you have the same state file used to create resources. If you lost the state file, you must delete resources manually via Console or CLI.


Quick Reference: Console Cleanup URLs

ServiceConsole URL
ECSconsole.aws.amazon.com/ecs
EC2 (ALB, NAT)console.aws.amazon.com/ec2
RDSconsole.aws.amazon.com/rds
S3s3.console.aws.amazon.com
VPCconsole.aws.amazon.com/vpc
Cost Explorerconsole.aws.amazon.com/cost-management
Billingconsole.aws.amazon.com/billing

Resource Groups

Use AWS Resource Groups to tag all resources for a project, making it easier to find and delete them later:console.aws.amazon.com/resource-groups

Emergency: Unexpected High Bill

  1. Check Cost Explorer: Identify which service is charging
  2. Check running resources: Look for NAT Gateways, RDS, ECS tasks
  3. Delete/stop immediately: Don't wait - charges accumulate hourly
  4. Contact AWS Support: For billing disputes or compromised accounts
  5. Rotate credentials: If you suspect account compromise
Bash
# Quick check for expensive running resources
aws ec2 describe-nat-gateways --query 'NatGateways[?State==`available`]'
aws rds describe-db-instances --query 'DBInstances[?DBInstanceStatus==`available`]'
aws ecs list-clusters
aws elbv2 describe-load-balancers

AWS Deployment Guide — Built with Next.js