Cloud bill spiked after a quiet month: how to find the real cause
For customers who opened a cloud invoice after a slow month and found a surprise increase. This runbook shows how to identify the most common causes using your provider dashboard first, then optional commands, and how to fix each one safely.
TL;DR — If your cloud bill jumped during a month when traffic and team activity seemed normal, the cause is usually one of five things: a resource was left running, storage or backups kept growing, data transfer (bandwidth) increased, autoscaling stayed high, or a paid service/add-on was enabled and forgotten. Start with your provider's billing dashboard grouped by service and filtered to the last 30 days; the biggest line item usually tells you where to look first. Reading time: ~6 min
The scenario
It is a quiet Tuesday afternoon. You open this month's cloud invoice expecting it to be flat, because there were no launches, no big campaigns, and no obvious incidents. Instead, the total is 40% higher than last month. Your app looked normal, the team barely touched production, and now you need to explain the increase before finance asks whether something is broken or compromised.
Symptoms
- Your provider's invoice or billing dashboard shows a higher total than the previous month, often with no matching increase in customer traffic.
- One service category suddenly dominates the bill, such as "Compute", "Storage", "Data transfer", "Managed database", or "Snapshots/Backups".
- Daily cost graphs show a step-up on a specific date rather than a smooth increase.
- Usage reports show one of these common patterns:
- Compute hours increased even though deploy frequency stayed low.
- Storage GB-month increased every day.
- Network egress (outbound traffic) increased sharply.
- Database IOPS/CPU/storage or backup retention increased.
- A new recurring charge appears for a load balancer, NAT gateway, static IP, monitoring add-on, or another managed service.
- In app or infrastructure logs, you may see clues such as:
- "Scaled up from 2 to 8 instances"
- "Created snapshot"
- "Backup retention updated to 30 days"
- Large numbers of requests for images, video, or downloads
- Repeated health checks or bot traffic from many IPs
Likely causes
| Cause | How common | Quick check |
|---|---|---|
| A compute resource was left running or resized up | Very common | Billing dashboard → Costs by service/resource for last 30 days |
| Storage, snapshots, or backups kept growing | Very common | Billing dashboard → Filter to Storage/Backups |
| Data transfer (bandwidth) increased | Common | Billing dashboard → Filter to Network/Data transfer |
| Autoscaling stayed high because of bots, cron jobs, or a bad loop | Common | Compute/App dashboard → Instance count or scaling history |
| A paid managed service or add-on was enabled and forgotten | Common | Billing dashboard → Recurring charges / subscriptions / products |
Step-by-step diagnosis
-
Open your provider's billing dashboard and group costs by service for the last 30 days.
- Typical path: your provider's dashboard → Billing/Costs → Cost breakdown → Group by service.
- This is your problem if one category clearly explains most of the increase, for example Compute, Storage, Data transfer, or Database.
- Jump to the matching fix section below.
-
Change the billing view to group by resource, project, or tag for the same period.
- Typical path: Billing/Costs → Group by resource, project, label, or tag.
- This is your problem if one VM (virtual machine), database, bucket, load balancer, or app service suddenly appears near the top.
- If it is a compute instance, jump to ### Compute resource left running or resized up.
- If it is a bucket, volume, snapshot, or backup item, jump to ### Storage, snapshots, or backups kept growing.
- If it is a network product, jump to ### Data transfer increased.
- If it is a newly visible managed product, jump to ### Paid managed service or add-on enabled.
-
Check for a date where daily cost stepped up.
- Typical path: Billing/Costs → Daily view.
- This is your problem if the graph jumps on one day and stays elevated after that date. That usually means a resource was created, resized, or enabled and then left in place.
- Open your provider's activity/audit log for that date and look for create, resize, scale, enable, snapshot, or retention changes.
- Typical path: Activity log / Audit log / Events.
- If you find a resize or new server, jump to ### Compute resource left running or resized up.
- If you find backup or snapshot changes, jump to ### Storage, snapshots, or backups kept growing.
- If you find a new service subscription, jump to ### Paid managed service or add-on enabled.
-
If Compute is high, check whether instance count or size increased and stayed there.
- Typical path: Compute/App service → Instances or Scaling → History.
- This is your problem if you see more instances than usual, a larger machine size, or scaling events that never came back down.
- Jump to ### Compute resource left running or resized up or ### Autoscaling stayed high.
-
If Storage or Database is high, check growth over time.
- Typical path: Storage/Buckets/Volumes/Database → Metrics or Usage.
- This is your problem if used storage climbs steadily even during a quiet month, or if snapshot/backup counts keep increasing.
- Jump to ### Storage, snapshots, or backups kept growing.
-
If Network/Data transfer is high, check outbound traffic and top paths.
- Typical path: CDN/load balancer/app dashboard → Analytics/Traffic/Logs.
- This is your problem if outbound traffic is much higher than normal, especially for media files, downloads, backups, or repeated bot requests.
- Jump to ### Data transfer increased.
-
If the bill still looks unclear, export the cost report and sort by amount.
- Typical path: Billing → Export CSV/Download usage report.
- This is your problem if the CSV shows a charge category that the summary view hid, such as static IPs, NAT gateways, monitoring ingestion, or cross-region transfer.
- Jump to ### Paid managed service or add-on enabled.
Fixes
Compute resource left running or resized up
Use the billing view to identify the exact server, app service, or node. Then open that resource and either stop it, delete it, or resize it back to the expected level.
- Dashboard path: Compute/Instances → select the resource → Stop, Delete, or Resize.
- If your agency uses tags, add or correct tags so the next bill is easier to read.
Optional CLI examples if you do have access:
# List running Linux servers
ps aux
# Show Docker containers using CPU or memory on a host
docker stats --no-stream
Verify it worked: in the billing dashboard's daily view, the cost for that resource should flatten or drop within the next reporting cycle.
Storage, snapshots, or backups kept growing
Look for buckets, block volumes, database storage, snapshots, and backup retention settings. Quiet months still generate logs, uploads, and backups, so storage often rises even when app traffic does not.
- Dashboard path: Storage/Buckets or Volumes → sort by size.
- Dashboard path: Database → Backups/Snapshots → check retention days and snapshot count.
- Reduce retention to your agreed policy, delete old manual snapshots, and remove unused files or logs.
⚠️ Deleting snapshots, backups, or files can permanently remove recovery points or customer data. Confirm your retention policy and keep at least one recent tested backup before deleting older items.
If you manage files on a Linux host, these commands help find large directories:
du -sh /var/log/* 2>/dev/null | sort -h
du -sh /var/lib/* 2>/dev/null | sort -h
For PostgreSQL (common managed database engine), check database size:
psql "$DATABASE_URL" -c "SELECT datname, pg_size_pretty(pg_database_size(datname)) FROM pg_database ORDER BY pg_database_size(datname) DESC;"
Verify it worked: storage usage or snapshot count stops climbing, and next day's storage/backups cost is lower or flat.
Data transfer increased
Data transfer usually means outbound traffic from your app, server, object storage, or database. Common reasons are large media files, downloads, backup replication, hotlinking, bot traffic, or traffic bypassing a CDN (content delivery network).
- Dashboard path: Network/Data transfer → identify the service with the spike.
- Dashboard path: CDN or DNS provider (e.g. in your provider's dashboard, such as Cloudflare: Analytics or DNS → Records) → confirm your main domain still points through the CDN where expected.
- Dashboard path: App/Load balancer → Access logs or analytics → find top URLs and user agents.
If you have nginx access, find top requested paths:
awk '{print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -nr | head -20
Find top user agents (often bots):
awk -F'"' '{print $6}' /var/log/nginx/access.log | sort | uniq -c | sort -nr | head -20
Common remediations:
- Put large static files behind a CDN.
- Block abusive bots at your CDN, WAF (web application firewall), or load balancer.
- Re-enable caching headers for assets.
Example nginx cache headers for static assets:
location ~* \.(css|js|jpg|jpeg|png|gif|svg|webp|ico|woff2?)$ {
expires 30d;
add_header Cache-Control "public, max-age=2592000, immutable";
}
Verify it worked: outbound traffic in analytics drops within hours, and cache hit rate rises if you use a CDN.
Autoscaling stayed high because of bots, cron jobs, or a bad loop
Autoscaling is useful, but if a bad job, retry loop, or bot traffic keeps CPU or request count high, the platform may keep extra instances running all month.
- Dashboard path: App service/Kubernetes/Compute → Scaling history.
- Dashboard path: Metrics → CPU, memory, requests, queue depth.
- Dashboard path: Scheduled jobs/Cron → check for jobs running too often or failing and retrying.
If you have application logs, look for repeated errors or loops:
grep -Ei "timeout|retry|failed|exception" /var/log/*.log | tail -50
Fixes:
- Lower max instances to the agreed safe limit.
- Correct the cron schedule.
- Block bot traffic or add rate limiting.
- Fix the retry loop in the app or worker.
Example nginx rate limit snippet:
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=5r/s;
server {
location /api/ {
limit_req zone=api_limit burst=20 nodelay;
proxy_pass http://app;
}
}
Verify it worked: instance count returns to normal and stays there for at least one business day.
Paid managed service or add-on enabled and forgotten
Sometimes the bill increase is not from your app at all. It is a new managed database replica, load balancer, NAT gateway, monitoring ingestion, static IP, or another recurring service left enabled after a test or migration.
- Dashboard path: Billing → Recurring charges / Products / Subscriptions.
- Dashboard path: Activity log → filter for create/enable around the date the cost jumped.
- Disable or delete the unused service if it is not part of production.
⚠️ Deleting a managed database, load balancer, or IP can cause downtime. Confirm what is attached before removing it.
If your team tracks infrastructure as code, compare recent changes in Terraform:
git log --since="45 days ago" -- '*.tf'
Verify it worked: the service no longer appears in active resources, and the recurring charge stops on the next billing day.
Prevention
- Add a daily cost alert with a hard threshold and a percentage-change threshold. In your provider's billing area, create one alert for "daily spend over normal baseline" and one for "month-to-date forecast exceeds budget by 20%".
- Tag every production resource and alert on untagged resources. Use a required tag set such as:
{
"environment": "production",
"owner": "agency-name",
"customer": "client-name",
"service": "web"
}
- Put backup retention in code or a written runbook, not in memory. Example policy snippet to document in your repo:
{
"db_backup_retention_days": 7,
"snapshot_retention_days": 14,
"log_retention_days": 30
}
- Add a weekly review of top 10 cost items. Export the billing CSV every Monday and sort by amount and by change from last week.
- Add CDN caching and bot controls for static assets and common abuse paths. Even a basic cache rule and rate limit can prevent quiet-month bandwidth surprises.
- Add a CI check for infrastructure changes that create always-on resources. For example, require review when Terraform adds a database, load balancer, NAT gateway, or increases instance count:
terraform plan -out=tfplan
terraform show -json tfplan | jq '.resource_changes[] | select(.change.actions[] | contains("create") or contains("update")) | .address'
This article was written by an AI system and published pending human review. Verify anything you intend to act on.
Have a project in mind?
Get an instant AI price estimate for it, or talk directly to our team.
One email a month on what we learn building with AI