DynamoDB Capacity & Pricing: RCU, WCU, On-Demand vs Provisioned
How DynamoDB pricing works — read/write capacity units (RCU/WCU), on-demand vs provisioned, auto scaling, and how to estimate and cut your bill.
On this page
DynamoDB billing confuses people because you don’t pay for storage and CPU the way you do with a relational database — you pay for throughput, measured in capacity units. Understanding how those units are consumed is the difference between a $30 bill and a $3,000 one.
This guide covers how reads and writes are metered, the two capacity modes, and the levers that actually move your bill.
Capacity units: the unit of billing
Every read and write is converted into capacity units based on item size, rounded up.
| Unit | What one unit buys |
|---|---|
| RCU (read capacity unit) | One strongly consistent read of up to 4 KB, or two eventually consistent reads of 4 KB |
| WCU (write capacity unit) | One write of up to 1 KB |
The math rounds up per item and per 4 KB / 1 KB block:
- Reading a 5 KB item strongly consistent →
ceil(5/4)= 2 RCU. - Reading the same item eventually consistent → 2 × 0.5 = 1 RCU.
- Reading it in a transaction → 2 × 2 = 4 RCU.
- Writing a 1.5 KB item →
ceil(1.5/1)= 2 WCU. A transactional write doubles that to 4 WCU.
Item size includes attribute names plus values, so verbose attribute names quietly inflate cost. This is one reason single-table designs use short keys like PK and SK — see single-table design.
Reads bill on data scanned, not returned
This is the trap that surprises everyone. A Query or Scan consumes RCUs for every item read from the table, and FilterExpression is applied after the read. If you Scan a 1 GB table and filter down to three items, you pay for the full gigabyte. Always answer questions with key conditions, not filters — the difference is explained in query vs scan.
On-demand vs provisioned
DynamoDB offers two capacity modes per table, switchable once every 24 hours.
On-demand (pay-per-request)
You pay per million read and write request units, with zero capacity planning. DynamoDB scales instantly to whatever traffic arrives.
- Good for: new apps, spiky traffic, unknown load, dev/test, anything you don’t want to babysit.
- Cost shape: linear with traffic. You never pay for idle.
aws dynamodb create-table \
--table-name Orders \
--attribute-definitions AttributeName=PK,AttributeType=S \
--key-schema AttributeName=PK,KeyType=HASH \
--billing-mode PAY_PER_REQUEST
Provisioned
You reserve a fixed number of RCUs and WCUs per second. Requests beyond your reserved capacity get throttled (ProvisionedThroughputExceededException) unless you’ve enabled burst capacity or auto scaling.
- Good for: steady, predictable traffic where you can keep utilization high.
- Cost shape: you pay for what you reserve, used or not.
aws dynamodb create-table \
--table-name Orders \
--attribute-definitions AttributeName=PK,AttributeType=S \
--key-schema AttributeName=PK,KeyType=HASH \
--billing-mode PROVISIONED \
--provisioned-throughput ReadCapacityUnits=25,WriteCapacityUnits=25
The break-even rule of thumb
On-demand request units cost roughly 6–7x a provisioned unit running at full utilization. So provisioned wins only when your sustained utilization stays above ~15–20%. If your traffic is bursty and your average utilization is low, on-demand is both cheaper and less work. Many teams launch on on-demand, measure for a month, and switch to provisioned only if the numbers clearly justify it.
Auto scaling
Provisioned tables can use Application Auto Scaling to track a target utilization (default 70%). It raises and lowers capacity within bounds you set.
aws application-autoscaling register-scalable-target \
--service-namespace dynamodb \
--resource-id "table/Orders" \
--scalable-dimension "dynamodb:table:ReadCapacityUnits" \
--min-capacity 5 --max-capacity 500
The catch: auto scaling reacts on a multi-minute timescale via CloudWatch alarms. It smooths gradual trends well but cannot absorb a sudden spike — by the time it scales up, the spike may be over and your users throttled. For genuinely spiky workloads, on-demand handles the spike instantly; auto scaling does not.
What else shows up on the bill
Throughput is usually the biggest line, but not the only one:
- Storage — billed per GB-month of table data plus index data.
- Global Secondary Indexes — every write that touches an indexed attribute incurs additional WCUs on the index. A table with three GSIs can cost 4x the write capacity of a bare table. Don’t over-index; see secondary indexes.
- DynamoDB Streams — billed per million read requests against the stream.
- Backups — on-demand and continuous (PITR) backups are billed by storage size.
- Data transfer — egress out of AWS, standard rates.
- Global Tables — replicated writes (rWCU) are billed in each replica Region.
Estimating before you build
Work it out from access patterns, not guesswork:
- Estimate average item size in KB (include attribute names).
- Estimate reads/sec and writes/sec at peak.
- RCUs = reads/sec ×
ceil(size/4)(halve for eventually consistent). - WCUs = writes/sec ×
ceil(size/1). - Add GSI write cost for any write that modifies an indexed attribute.
Then compare the provisioned monthly cost against the on-demand cost at the same volume. If you’re under ~20% projected utilization, on-demand almost always wins.
Cutting an existing bill
| Lever | Effect |
|---|---|
| Use eventually consistent reads where correctness allows | Halves read cost |
Replace Scan + filter with Query on a key | Often 10–100x less data read |
| Drop unused GSIs | Removes their storage and write amplification |
| Shorten attribute names / compress large blobs | Smaller items = fewer RCU/WCU |
| Move cold data to S3, keep a pointer in DynamoDB | Less storage and smaller items |
| Set TTL on ephemeral data | Free deletes, lower storage |
| Switch idle provisioned tables to on-demand | Stop paying for unused capacity |
Inspect ConsumedCapacity on your responses (pass ReturnConsumedCapacity=TOTAL or INDEXES) to see exactly what each call costs, and watch the ConsumedReadCapacityUnits / ThrottledRequests CloudWatch metrics in production.
When you’re tuning capacity, it helps to see consumed units per operation as you run queries rather than parsing raw CloudWatch graphs. A native DynamoDB GUI like Tablyne surfaces the ConsumedCapacity of each query and scan inline, which makes it obvious when a filter is quietly reading the whole table. That feedback loop is hard to get from the AWS console alone.
For deeper guidance on key design and avoiding the hot-partition problems that wreck capacity planning, see best practices and consistency.
Frequently asked questions
How many RCUs does a strongly consistent read use?
A strongly consistent read consumes one full RCU per 4 KB read (rounded up). An eventually consistent read of the same item costs half an RCU, so it's twice as cheap. Transactional reads cost two RCUs per 4 KB.
Is DynamoDB on-demand more expensive than provisioned?
Per request, yes — on-demand pricing is roughly 6–7x the cost of a fully-utilized provisioned unit. But provisioned only wins if your utilization is consistently high. For spiky or unpredictable traffic, on-demand is usually cheaper because you don't pay for idle capacity.
Does a Query cost more than a GetItem?
A Query is billed on the total size of items it reads before any FilterExpression is applied, not the number of items returned. A GetItem bills only for the single item fetched, so a filtered Query that scans many items can cost far more than it appears.