The AWS line items that surprise small teams
A client forwarded us their first AWS invoice after migration: ₹1,47,000. The architecture diagram said it should be ₹38,000. The gap was not a misconfiguration — it was a set of services that started charging the moment they were used, without a dashboard widget or an alarm.
We have seen this pattern enough times that we now check five specific line items before the invoice does.
**NAT Gateway data processing.** A NAT Gateway costs ₹0.23/hour just to exist, which is reasonable. The surprise is ₹0.023 per GB processed. A single ECS task pulling a 2 GB Docker image from Docker Hub through the NAT Gateway costs roughly ₹0.05. Do that across a fleet of twenty containers redeploying twice a day, and you have ₹60/day in NAT processing alone. We found one client spending ₹18,000/month this way before anyone noticed.
The fix is usually a VPC endpoint for ECR (`com.amazonaws.region.ecr.api` and `ecr.dkr`), which routes container pulls through the AWS network instead of through the NAT Gateway. The endpoint itself costs ₹0.052/hour, but the per-GB processing drops to zero.
**CloudWatch Logs ingestion.** Every `console.log` in a Lambda function is a log event. CloudWatch charges ₹0.35 per GB ingested. A noisy Lambda logging 1 KB per invocation, invoked 2 million times a month, generates 2 GB of logs — ₹700. We found a client whose Node.js Lambda was logging the full event payload on every invocation because the framework defaulted to it. Three lines of code removed ₹2,100/month.
**EBS snapshots without lifecycle policies.** EBS volumes are cheap. Snapshots are cheaper — ₹0.095/GB-month. But a daily snapshot of a 100 GB volume, with no deletion policy, accumulates 3 GB of snapshot storage per month (snapshots are incremental). After a year, that is 36 GB of snapshots for a single volume. Multiply by twenty volumes across staging and production, and the line item is ₹6,840/month for backups no one ever restores from.
We set `DeleteSnaphotAfterDays` in a DLM lifecycle policy. Seven days is usually enough for most teams. Thirty days for production databases.
**Load Balancer idle time.** An Application Load Balancer costs ₹0.0224/hour — ₹16,410/year — plus ₹0.0075 per LCU-hour. The idle cost is the base rate. A staging environment with an ALB running 24/7 that receives traffic for two hours during QA is paying for 22 hours of idle ALB per day. We move staging ALBs behind a scheduled stop/start using EventBridge and Lambda. The ALB itself cannot be stopped, but the EC2 targets can, and if staging is truly unused overnight, we tear down the ALB and recreate it with Terraform in the morning.
**S3 multipart upload leftovers.** When a multipart upload is initiated but not completed or aborted, the uploaded parts persist in S3 and are billed at standard storage rates. We found 340 GB of orphaned multipart parts in a client's media bucket — ₹2,380/month for files that do not exist in any listing. The fix is a bucket lifecycle rule:
{
"Rules": [
{
"ID": "AbortIncompleteMultipartUploads",
"Status": "Enabled",
"AbortIncompleteMultipartUpload": {
"DaysAfterInitiation": 7
}
}
]
}We add this rule to every S3 bucket on day one now.
To find these before the invoice arrives, we enable AWS Cost Anomaly Detection on all accounts. It is free. Alerts go to Slack via SNS. The detection is not perfect — it missed the NAT Gateway charges because they grew gradually — but it catches the sharp spikes, which are usually the expensive mistakes.
For gradual creep, we use Cost Explorer with a daily granularity view filtered by service. A seven-day lookback compared to the previous seven days catches most drift. We check this every Monday morning before standup. It takes four minutes and has paid for itself more times than we can count.