Database storage autoscaling: the ratchet nobody budgets for
Storage autoscaling grows your database disk automatically and never shrinks it. A one-off bulk load can leave you paying for terabytes you deleted years ago. Here is how the ratchet works and what it costs.
Quick answer
Managed database storage autoscaling is one-directional: it grows the volume when free space drops below a threshold and never shrinks it back. On RDS the trigger is roughly 10 percent free space or 10 GB, whichever is larger, and each growth event adds at least 10 GB or 10 percent of current size. At gp3's $0.115 per GB-month, a database that briefly ballooned from 500 GB to 3 TB during a migration keeps costing about $345 per month instead of $58, a permanent $287 monthly charge for space that is now empty. Reclaiming it requires a dump and restore or a read-replica promotion, not a settings change.
Storage autoscaling is a genuinely useful feature: it prevents the storage-full outage that used to be a classic 3am page. What is less well understood is that it is a ratchet. The volume goes up and stays up, and the billing follows the allocated size, not the used size. Every temporary spike becomes a permanent line on the invoice.
How the ratchet fires
On RDS, autoscaling triggers when free space falls below the larger of 10 percent of allocated storage or 10 GB, and stays there for at least five minutes. Each event increases the volume by the largest of 10 GB, 10 percent of current allocated storage, or enough to sustain the observed growth rate for the next seven hours. There is a cooldown of six hours between events. The behaviour on Cloud SQL and Azure Flexible Server is broadly similar, and on none of them can you reduce allocated storage afterwards through a console setting.
| Event | Allocated after | gp3 monthly cost |
|---|---|---|
| Initial provision | 500 GB | $57.50 |
| Bulk import starts | 1,000 GB | $115.00 |
| Import continues | 1,600 GB | $184.00 |
| Index rebuild peak | 2,400 GB | $276.00 |
| Final growth event | 3,000 GB | $345.00 |
| Data deleted, 400 GB in use | 3,000 GB | $345.00 |
The last row is the point. Deleting the data does not reduce the bill by a cent. You are now paying $345 per month, $4,140 per year, for a database holding 400 GB. And the damage compounds: RDS gives you backup storage equal to allocated storage at no charge, but snapshots of the volume reflect used blocks rather than allocated size, so the backup side is less affected. IOPS and throughput provisioning on io1 and io2 volumes, by contrast, scale with the allocated size you asked for.
The common triggers
Four patterns cause most unplanned growth. First, bulk imports and backfills, which temporarily double or triple storage during the load and again during index creation. Second, PostgreSQL table and index bloat under weak autovacuum settings, where a high-churn table can hold three times its live data in dead tuples. Third, unrotated log tables, audit trails, and job queues that nobody set a retention policy on. Fourth, failed or long-running transactions that pin WAL or undo segments, which on PostgreSQL can accumulate alarmingly fast when a replication slot is inactive.
That last one deserves emphasis. An orphaned logical replication slot prevents WAL removal indefinitely. A database writing 50 GB of WAL per day with a dead slot will consume 1.5 TB in a month, trigger a dozen autoscaling events, and leave a permanently inflated volume behind even after you drop the slot. At $0.115 per GB-month that single oversight is about $172 per month forever.
How to reclaim the space
| Method | Downtime | Effort |
|---|---|---|
| Dump and restore to a new instance | Minutes to hours | Moderate |
| Create smaller read replica, promote it | Short failover window | Moderate |
| Logical replication to a new instance, cut over | Seconds | High |
| Change a setting in the console | n/a | Not available |
The read-replica route is usually the least disruptive: create a replica with a smaller allocated storage value (which is possible as long as it exceeds current used space), let it catch up, then promote it and retire the original. For a 3 TB volume holding 400 GB, dropping to a 600 GB allocation saves about $276 per month, which makes the few hours of engineering work pay back in under a week.
Preventing the next ratchet
Set the autoscaling maximum deliberately rather than accepting the default ceiling, because the maximum is the only guardrail that exists. Alert on allocated storage changes, not just on free space, so a growth event generates a notification rather than a silent invoice change. Run bulk imports against a temporary instance sized for the job and load the result, rather than letting production absorb the peak. Tune autovacuum on high-churn tables and monitor for inactive replication slots. And set retention on every log, audit, and event table at creation time, because retroactive retention policies are how the 400 GB of useful data ended up on a 3 TB volume in the first place.
Price allocated storage explicitly in your Terraform, and treat a change to allocated_storage as a cost decision that shows up in review. Compare storage classes against the resource catalog, and see how snapshot storage creeps for the backup-side equivalent.
FAQ
Can I shrink database storage after autoscaling grows it?
Not through a setting. RDS, Cloud SQL, and Azure Flexible Server all grow allocated storage automatically and none of them allow reducing it in place. Reclaiming the space requires a dump and restore into a smaller instance, creating a read replica with a smaller allocation and promoting it, or logically replicating to a new instance and cutting over. The console offers no path.
When does RDS storage autoscaling trigger?
When free space falls below the larger of 10 percent of allocated storage or 10 GB and stays there for at least five minutes. Each event grows the volume by the largest of 10 GB, 10 percent of current allocated storage, or enough to sustain the observed growth rate for seven hours, with a six hour cooldown between events. A sustained load can therefore trigger several growth steps in a day.
What does a storage ratchet cost?
At gp3's $0.115 per GB-month, a database that grew from 500 GB to 3 TB during a migration costs about $345 per month instead of $58, a permanent $287 monthly charge, or $3,444 per year, even after the data is deleted. Billing follows allocated size, not used size, so the volume holding 400 GB of live data bills as 3,000 GB indefinitely.
What causes unplanned database storage growth?
Bulk imports and backfills that double or triple storage during load and index creation, PostgreSQL table and index bloat under weak autovacuum settings, unrotated log, audit, and job queue tables with no retention policy, and long-running transactions or inactive replication slots pinning WAL. An orphaned logical replication slot on a database writing 50 GB of WAL per day consumes 1.5 TB in a month.
What is the cheapest way to reclaim inflated storage?
Usually creating a read replica with a smaller allocated storage value, letting it catch up, promoting it, and retiring the original, which needs only a short failover window. For a 3 TB volume holding 400 GB, dropping to a 600 GB allocation saves about $276 per month, so a few hours of engineering work pays back in under a week.
How does C3X help with storage autoscaling cost?
C3X prices allocated storage from your Terraform, so a change to allocated_storage or an autoscaling maximum appears as a cost figure in the pull request rather than as a silent ratchet on the invoice. Since allocated storage is billed regardless of use and cannot be reduced in place, making the number visible at review time is the only cheap moment to get it right.
What to do next
Make allocated storage a review decision, not an invoice surprise. C3X reads your Terraform and prices your resources against a live catalog. Start with the quickstart.
Share this post
Try C3X on your own Terraform
Free and open source. No API key required. One command to install, one command to estimate.