RDS is easy to provision and hard to reason about. You pick an instance class, a storage type, and whether you want Multi-AZ, click create, and a few weeks later the RDS line on your bill is a single number that used to be four or five decisions. Nobody itemizes it for you afterward, and the console page for an instance shows its class and its status, not what each of those choices is actually charging you per hour. This post breaks the bill back into its meters, with the current rate for each one, so "RDS costs more than I expected" turns into "this specific setting is why."
What am I actually paying for on an RDS instance?
RDS charges for four things separately, and they do not turn off together:
- Compute — an hourly rate for the instance class, billed whether the database is busy or idle.
- Storage — a per-GB-month rate for whatever you provisioned, independent of how much data is actually in the tables.
- Provisioned performance — on
io1/io2storage, and ongp3above its included baseline, IOPS and throughput are billed as their own line items. - Backup storage — automated backups and manual snapshots, free up to a point and then billed per GB-month.
Multi-AZ multiplies two of these, not all four. Turning on a standby exactly doubles the compute rate and the storage rate — you are paying for a second copy of both. It does not touch the backup-storage meter, because RDS only backs up the primary.
How much does the instance itself cost per hour?
Instance pricing is linear within a family — every step up or down the same instance family changes the hourly rate by a fixed ratio — but the starting point varies a lot by class. These are on-demand, Single-AZ, us-east-1 rates for RDS for PostgreSQL with no license required:
| Instance class | Single-AZ ($/hr) | Multi-AZ ($/hr) |
|---|---|---|
db.t4g.micro | 0.016 | 0.032 |
db.t3.micro | 0.018 | 0.036 |
db.t3.medium | 0.072 | 0.144 |
db.m6g.large | 0.159 | 0.318 |
db.r6g.large | 0.225 | 0.450 |
Multi-AZ is not a discounted or partial add-on — every row above is exactly 2× its Single-AZ neighbor, because you are running a full second instance as a synchronous standby that AWS fails over to on a problem. The instance-class name tells you nothing about whether that standby is switched on; you have to check the deployment setting.
These are RDS for PostgreSQL rates at the time of writing. MySQL and MariaDB are close but not identical, and Oracle and SQL Server run substantially higher once licensing is added. Prices vary by Region and AWS changes them — check the RDS pricing page with your Region and engine selected before you size a migration around any of these numbers.
What does storage actually cost — gp3 vs Provisioned IOPS?
Storage is its own meter, billed on what you provisioned, not what your tables hold. At us-east-1 on-demand rates for PostgreSQL:
| Storage type | Single-AZ | Multi-AZ |
|---|---|---|
| General Purpose (gp3) | $0.115 per GB-month | $0.23 per GB-month |
| gp3 provisioned IOPS above baseline | $0.02 per IOPS-month | $0.04 per IOPS-month |
| gp3 provisioned throughput above baseline | $0.08 per MiBps-month | $0.16 per MiBps-month |
| Provisioned IOPS (io1 / io2) | $0.125 per GB-month | $0.25 per GB-month |
gp3 includes a baseline of 3,000 IOPS and 125 MiB/s at no extra charge for volumes under 400 GiB — most instances never need to pay the provisioned-IOPS row at all. io1/io2 has no included baseline: every IOPS you provision is billed from the first one, which is why a database moved to Provisioned IOPS storage for a spike two years ago and never turned back down is a common place to find money. Storage bills on what you provisioned, not on used bytes, so a 500 GB volume holding 40 GB of data still bills as 500 GB — and RDS storage cannot be shrunk once increased, only grown, so an over-provisioned volume stays that size until someone migrates the data to a smaller one.
Is backup storage really free?
Up to a point. AWS states it plainly: "There is no additional charge for backup storage up to 100% of your total database storage for a region." A 200 GB database gets 200 GB-month of backup storage — automated backups and manual snapshots combined — at no cost. Past that allowance, backup storage bills at $0.095 per GB-month.
That allowance resets to zero savings the moment retention or manual snapshots push total backup storage past your provisioned size, and neither shrinks on its own: automated backups follow your retention window, but manual snapshots persist until someone deletes them. A database with a long retention period and a habit of manual snapshots before every migration can be paying the overage rate on backup storage without anyone having changed the instance or the data.
How do I confirm what my own instances are costing?
Start with an inventory across the account — class, engine, Multi-AZ setting, storage type and size, in one call:
aws rds describe-db-instances \
--query 'DBInstances[].[DBInstanceIdentifier,DBInstanceClass,Engine,MultiAZ,StorageType,AllocatedStorage,Iops]' \
--output table \
--region us-east-1
Two things to check in that output. Any instance with MultiAZ: true is billing 2× its listed instance-class rate for compute and storage — confirm that is a deliberate choice for a production workload, not a default nobody revisited on a dev database. Any row on io1/io2 with a high Iops value is worth comparing against its actual ReadIOPS/WriteIOPS CloudWatch metrics — provisioned performance the workload never approaches is pure overpay.
For the account-wide total, Cost Explorer grouped by Usage Type with the service filtered to RDS splits the meters apart by name — InstanceUsage for compute, RDS:GP3-Storage and RDS:PIOPS-Storage for the two storage types, RDS:ChargedBackupUsage for backup overage — so you can see in one screen which meter is actually driving the bill before you start changing anything.
Where does RDS pricing actually leak?
Two patterns account for most of the gap between what a database needs and what it bills.
The instance class gets picked once, at launch, and never revisited. A db.r6g.large chosen for a launch-week traffic spike two years ago is still billing $0.225 an hour whether or not the workload ever came close to using it — see Over-provisioned RDS instance for how to tell a legitimately sized database from an oversized one, and right-sizing an RDS instance without downtime for how to move down safely with Multi-AZ in place.
A database stops mattering, but nobody stops the instance. A dev or staging database with near-zero connections for weeks bills its full instance and storage rate exactly as if it were serving production traffic — see Idle RDS instance for how to confirm it before deleting anything.
What else should I check?
RDS is one line in a data-tier bill that usually has company. A Redis cluster left running in front of a database that stopped mattering charges its own hourly rate regardless of whether anything queries it — see are you paying for an idle ElastiCache (Redis) cluster? for how to tell an abandoned cache from a quiet one. And a data warehouse spun up for a dashboard prototype bills the same 24/7 node-hours whether four people query it a month or four hundred a day — how to cut Redshift costs by pausing idle clusters covers the fix that most teams never touch.
If RDS is simply the first meter you have looked at closely this week, it is unlikely to be the only one worth an afternoon — see how the rest of an AWS bill breaks down puts the whole account in order of savings per minute.
How do I find this across my whole AWS account?
Pricing out one database by hand, from its instance class through its storage type and its backup overage, takes a few minutes. Doing it for every RDS instance in every Region — and catching the ones that drift from right-sized to oversized, or from active to idle, after the first pass — is the part that stops happening. That is what a scan is for. Parsivex checks every Region for over-provisioned and idle RDS instances against real 14-day metrics, and then keeps watching, so a database does not quietly drift back into the same waste next quarter.
For what these findings mean once they show up in your report, see Over-provisioned RDS instance and Idle RDS instance, or read how scans work before you connect an account.