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.
| Service | Free Tier Limit | After Free Tier / Over Limit |
|---|---|---|
| S3 | 5 GB storage, 20K GET, 2K PUT | ~$0.023/GB/month |
| SQS | 1 million requests/month | $0.40 per million requests |
| Cognito | 50,000 MAU (always free) | $0.0055 per MAU above 50K |
| CloudWatch Logs | 5 GB ingestion, 5 GB storage | $0.50/GB ingested, $0.03/GB stored |
| Secrets Manager | No free tier | $0.40/secret/month + $0.05 per 10K API calls |
| ECS Fargate | No free tier | ~$0.04/hour for 0.25 vCPU, 0.5 GB |
| ALB | No free tier | ~$16/month minimum + data transfer |
| NAT Gateway | No free tier | ~$32/month + $0.045/GB processed |
| RDS PostgreSQL | 750 hours db.t3.micro | ~$12-25/month for small instance |
| ECR | 500 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)
| Resource | Configuration | Est. Monthly Cost |
|---|---|---|
| S3 Bucket | 1 GB storage | $0.02 |
| SQS Queue | 10K messages | Free (under limit) |
| Cognito | 100 users | Free (under 50K MAU) |
| CloudWatch Logs | 1 GB logs | Free (under limit) |
| Secrets Manager | 2 secrets | $0.80 |
| Total (No Fargate) | ~$1/month |
Production Environment (Small App)
| Resource | Configuration | Est. Monthly Cost |
|---|---|---|
| ECS Fargate | 1 task, 0.5 vCPU, 1 GB (24/7) | $15-20 |
| ALB | Basic usage | $16-20 |
| NAT Gateway | Low traffic | $32-35 |
| RDS PostgreSQL | db.t3.micro | $12-15 |
| S3 | 10 GB storage | $0.25 |
| CloudWatch | 5 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 budget2. Check Cost Explorer Daily
Plain Text
AWS Console → Cost Management → Cost Explorer
- View costs by service
- Identify unexpected charges
- Check daily trends3. 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.
Recommended Cleanup Order
- ECS Services (stop tasks first)
- ECS Cluster
- ALB + Target Groups
- NAT Gateway (release Elastic IP after)
- RDS Instances (skip final snapshot if not needed)
- S3 Buckets (empty bucket first)
- SQS Queues
- Cognito User Pools
- CloudWatch Log Groups
- Secrets Manager Secrets
- ECR Repositories (delete images first)
- VPC (last - after all resources using it)
- 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
| Service | Console URL |
|---|---|
| ECS | console.aws.amazon.com/ecs |
| EC2 (ALB, NAT) | console.aws.amazon.com/ec2 |
| RDS | console.aws.amazon.com/rds |
| S3 | s3.console.aws.amazon.com |
| VPC | console.aws.amazon.com/vpc |
| Cost Explorer | console.aws.amazon.com/cost-management |
| Billing | console.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-groupsEmergency: Unexpected High Bill
- Check Cost Explorer: Identify which service is charging
- Check running resources: Look for NAT Gateways, RDS, ECS tasks
- Delete/stop immediately: Don't wait - charges accumulate hourly
- Contact AWS Support: For billing disputes or compromised accounts
- 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