AWS costs: DynamoDB on-demand vs provisioned - the crossover math
If your DynamoDB table's provisioned capacity sits above roughly 29% utilized, provisioned pricing beats on-demand; below that, on-demand wins. That's not the number most cost guides quote, and there's a reason: AWS's November 2024 price cut only touched on-demand rates — provisioned pricing didn't move at all. The crossover point actually doubled, from about 14% utilization to about 29%. Teams that switched to provisioned capacity years ago because 15% utilization justified it are now carrying capacity they don't need, because the math that made that call correct stopped being true.
Jake runs a phone repair and trade-in shop, and about eight months ago he built a small web app so customers could check trade-in values and book a repair slot without calling. It's backed by a single DynamoDB table. When traffic was ten bookings a day, on-demand cost him less than a cup of coffee a month. Then he ran a "bring your old phone" trade-in weekend, traffic jumped 40x for 48 hours, and his AWS bill for that table alone came in higher than a full month of his previous on-demand charges — even though the promotion only ran two days.
That's the part nobody warns you about: on-demand and provisioned aren't "expensive" and "cheap" modes. They're two different bets on how predictable your traffic is, and the size of the bet has changed since AWS last touched the pricing. This post walks through the actual numbers from AWS's own pricing documentation, shows you exactly where the crossover sits today, and — more usefully — shows you the traps and adjacent costs that make the textbook math wrong for a real table.
How DynamoDB Actually Bills You in Each Mode
Before the math makes sense, you need the two billing units DynamoDB uses, because they're not interchangeable and half the confusion online comes from people mixing them up.
Provisioned capacity bills you by the hour for a number you set in advance: read capacity units (RCUs) and write capacity units (WCUs). One RCU supports one strongly consistent read per second of an item up to 4 KB. One WCU supports one standard write per second of an item up to 1 KB. AWS charges you for every RCU and WCU you've provisioned, every hour, whether your application uses it or not.
On-demand capacity bills you per request, not per hour. Reads are billed in read request units (RRUs) and writes in write request units (WRUs), consumed as you actually generate traffic. If your table gets zero requests for a week, you pay nothing for throughput — you still pay for storage.
The distinction that trips people up: an RCU and an RRU cover the same amount of data (4 KB per unit for reads, 1 KB per unit for writes), but one is a per-second capacity reservation and the other is a per-request meter. That's what makes a direct dollar comparison possible — you can convert "1 RCU held for a month" into "how many reads would that be on-demand" and compare the two bills directly. That conversion is the entire crossover calculation, and it's what the rest of this post walks through.
♂️ Jake's Reality Check
"I don't even know what a request unit is. Can't someone just tell me which button to click?"
Yes, eventually — see the decision table further down. But the button you click depends entirely on how steady your traffic is, and that's a number only you know about your own application, so there's no way around doing the comparison once.
The Provisioned Side of the Math
In the US East (N. Virginia) Region, on the DynamoDB Standard table class, provisioned pricing is $0.00065 per WCU-hour and $0.00013 per RCU-hour. Those numbers look tiny until you multiply them out for a full month.
Take a table provisioned at 1 WCU, running continuously for a 30-day month (720 hours). The bill for that single WCU is:
$0.00065 × 720 hours = $0.468 per month, for one write per second of sustained capacity.
One RCU, provisioned continuously for the same month, costs:
$0.00013 × 720 hours = $0.0936 per month, for one strongly consistent read per second of sustained capacity.
Here's the part that matters: you're charged for that WCU and RCU whether your application uses one write per second or zero. A 100-WCU table that only ever sees 20 writes per second is still billed for the full 100. That's the entire risk profile of provisioned capacity — you're paying for a reservation, not for usage, and the gap between what you reserved and what you actually consumed is money spent on nothing.
Ethan puts it more bluntly when Jake asks why anyone bothers with provisioned at all: "Because on-demand punishes you for being predictable. If you know your traffic cold, paying a per-request premium for the privilege of not thinking about it is just leaving money on the table. Provisioned is the boring, cheaper answer for boring, steady workloads. It's only the wrong answer when you're guessing."
The On-Demand Side of the Math
On-demand, Standard table class, US East (N. Virginia): AWS's own pricing examples bill writes at $0.625 per million write request units and strongly consistent reads at $0.125 per million read request units. Eventually consistent reads cost half that, since they consume half a request unit each; transactional reads and writes cost double, since they consume two units each.
Converting that same one-write-per-second, one-read-per-second load to on-demand pricing for a 30-day month (2,592,000 seconds):
2,592,000 writes × $0.625 per million = $1.62 per month for a sustained one write/second workload.
2,592,000 strongly consistent reads × $0.125 per million = $0.324 per month for a sustained one read/second workload.
Notice something: at 100% utilization — meaning your table's real traffic exactly matches what a provisioned unit would give you — on-demand costs more than three times as much per unit of throughput ($1.62 vs $0.468 for writes; $0.324 vs $0.0936 for reads). That ratio is the whole story. On-demand is more expensive per request, always. What decides the winner is how much of your provisioned capacity actually goes unused.
What changed in November 2024
- Before: on-demand write pricing was $1.25 per million write request units, and strongly consistent reads were $0.25 per million.
- Now: AWS cut on-demand throughput pricing by 50% effective November 1, 2024, bringing writes to $0.625 per million and strongly consistent reads to $0.125 per million. Provisioned WCU/RCU rates were not part of that cut.
- What that means for the crossover: because only one side of the comparison got cheaper, the utilization threshold at which provisioned becomes the better deal moved up — not down.
The Crossover Point: Doing the Actual Division
The crossover utilization is simply the provisioned monthly cost divided by the on-demand cost for the same sustained load:
Writes: $0.468 ÷ $1.62 = 28.9%
Reads (strongly consistent): $0.0936 ÷ $0.324 = 28.9%
Both land on the same number, which isn't a coincidence — AWS's per-request and per-hour rates for reads and writes maintain the same relative ratio to each other, so the break-even percentage comes out identical for both dimensions. In plain terms: if a WCU (or RCU) on your table is actually used more than about 29% of the hours in a month, provisioned capacity costs less than on-demand would for that same traffic. Below 29%, on-demand is cheaper.
Compare that to the pre-November-2024 numbers, using the same method with the old on-demand rates ($1.25/M writes, $0.25/M reads):
Old writes: $0.468 ÷ $3.24 = 14.4%
Old reads: $0.0936 ÷ $0.648 = 14.4%
That's the counterintuitive part from the top of this post, laid out with the actual numbers: the crossover point roughly doubled. A table sitting at 20% utilization used to clearly favor provisioned. Today, that same table favors on-demand. Nobody changed the provisioned pricing. AWS just made on-demand a better deal across a wider band of traffic patterns than it used to be, which is a genuinely different recommendation than "on-demand got cheaper, so use it more" — it's specifically that the point where provisioned starts winning moved further out.
✅ Why 29%, and not some rule of thumb
Third-party calculators quoting 14%, 40%, or "just switch above 10 million requests a month" are usually using stale pre-cut pricing, a different month-length assumption, or folding in reserved-capacity discounts without saying so. Run the division yourself against your own table class and Region — the two AWS rates above are the only inputs you actually need.
Here's how to run that calculation for your own table, in order:
- Pull your consumed capacity. In CloudWatch, chart
ConsumedWriteCapacityUnitsandConsumedReadCapacityUnitsfor the table over at least the last 30 days, ideally 90 if your traffic has any seasonality. - Find your average consumed units per second for reads and writes separately — sum the period and divide by the number of seconds in it.
- Find your provisioned units for the same period (or the units you'd need to provision to cover your actual peak, if you're comparing a hypothetical move).
- Divide average consumed by provisioned to get your utilization percentage for reads and writes independently — don't average them together, since read and write traffic patterns rarely match.
- Compare each percentage to 29%. If writes are utilized above 29% but reads are below, you may end up on provisioned for one dimension and on-demand isn't actually an option per-dimension — see the note on mixed workloads later in this post.
Why Your Peak, Not Your Average, Decides the Winner
The gap between what you provision and what you use
The 29% number assumes you provision exactly to your average sustained load. In practice, nobody does that — you provision to your peak, because if you provision below peak, DynamoDB throttles you and your application returns errors. That gap between peak and average is where the real-world crossover shifts.
Take Jake's shop app. On a normal day it needs maybe 5 writes per second at the busiest hour. During a trade-in weekend it needs 200 writes per second for two days. If Jake provisions for the weekly peak (200 WCUs) to avoid throttling, his average utilization across the month — most of which runs at 5 WCUs of actual traffic — works out to roughly 3.5%. That's nowhere near 29%. On-demand wins comfortably, not because the per-request math changed, but because a fixed provisioned number sized for a rare peak sits idle almost all month.
This is the single biggest reason on-demand exists at all: it removes the requirement to provision for a peak you might hit once a month. You pay per request regardless of whether that request happens during a quiet Tuesday or a trade-in Saturday.
Where auto scaling changes the picture
Provisioned capacity doesn't have to mean a single fixed number all month. DynamoDB auto scaling adjusts provisioned WCUs and RCUs up and down based on consumed capacity, targeting a utilization percentage you set (the default target is 70%). That closes some of the gap between peak-sized provisioning and actual usage — see the next section for the mechanics and where it still falls short.
What Auto Scaling Actually Does to the Math
Auto scaling on a provisioned table watches your consumed capacity and adjusts the provisioned amount to try to keep actual utilization near a target — 70% by default. When consumed capacity rises toward that target, it scales up; when it falls well below, it scales down. This narrows the gap between "provisioned" and "consumed," which pulls your effective utilization closer to the 29% break-even, sometimes past it.
AWS's documentation walks through a worked example: a table with 100 WCU and 100 RCU provisioned, target utilization 70%, minimum 100 and maximum 400 capacity units. When consumed capacity holds between 1 and 70 units, no scaling triggers and the bill for that period is a flat rate on the 100/100 baseline. When consumed capacity jumps to 100/100 (100% actual utilization against the 100-unit baseline), auto scaling raises provisioned capacity to 143 WCU and 143 RCU, which brings utilization back down to roughly 70%. When consumed capacity later drops to 80/80, auto scaling lowers provisioned capacity to 114/114 to hold that same 70% target.
Two things follow from that mechanism. First, auto scaling never targets 100% utilization by default — it targets 70%, so a table under active auto scaling structurally can't get closer than about 70% consumed-to-provisioned, and that's well above the 29% break-even, meaning provisioned auto-scaled tables tend to land comfortably on the "provisioned wins" side of the line for any workload steady enough that scaling isn't constantly chasing spikes. Second, scaling only happens after consumed capacity moves — it reacts to the traffic pattern rather than anticipating it, so a workload with fast, unpredictable spikes will spend real time either throttled below the new target or over-provisioned above it while scaling catches up.
⚠️ What auto scaling doesn't protect against
Auto scaling adjusts total table-level throughput. It does not fix uneven access across your partition key. If one partition key gets hammered while the rest of the table is idle, that single partition can still throttle even though the table-wide provisioned capacity looks generous on paper — see the traps section further down.
Reserved Capacity and Database Savings Plans
Everything above compares standard, non-discounted provisioned pricing against on-demand. If your workload is steady enough to commit to a term, two AWS discount mechanisms push the crossover point lower still — meaning provisioned wins at even lower utilization percentages than 29%.
Reserved provisioned capacity
Reserved provisioned capacity lets you commit to a fixed amount of read and write capacity for one or three years in exchange for a discounted hourly rate. AWS's published savings are up to 54% off standard provisioned rates for a one-year term and up to 77% off for a three-year term. It's sold in blocks of 100 WCUs or 100 RCUs, purchased through the console under the DynamoDB Reserved Capacity screen, and billed as a one-time upfront fee plus a discounted hourly rate for the term. It applies only to single-Region provisioned capacity on the Standard table class — it doesn't cover replicated write capacity units for global tables or Standard-Infrequent Access tables.
Because the discount applies to the same per-hour provisioned rate used in the crossover calculation, plugging a 77% discount into the same division drops the break-even utilization from ~29% down into the single digits for a three-year commitment. That's a meaningful reason to revisit workloads currently sitting on plain on-demand: if the traffic has been steady for months, even fairly low utilization can justify reserved provisioned capacity once the discount is factored in — you don't need to hit 29% anymore, you need to hit whatever the discounted rate implies.
Database Savings Plans
Database Savings Plans are a separate, newer mechanism: a one-year commitment to a specific dollar-per-hour spend level, in exchange for a discount that applies flexibly across Regions and table classes. AWS publishes savings of up to 18% on on-demand throughput and up to 12% on provisioned throughput under this plan. Unlike reserved provisioned capacity, a savings plan can cover on-demand usage, which is the one lever on-demand tables have for a term-based discount — on-demand itself has no reserved-capacity equivalent.
You can't combine both discounts on the same usage: AWS applies reserved provisioned capacity first, and only unreserved provisioned usage becomes eligible for savings plan coverage on top of that.
| Discount option | Applies to | Term | Published savings |
|---|---|---|---|
| Reserved provisioned capacity | Provisioned RCU/WCU, single-Region, Standard table class only | 1 or 3 years | Up to 54% (1-yr) / 77% (3-yr) |
| Database Savings Plans | On-demand and provisioned throughput, flexible across Regions/classes | 1 year | Up to 18% (on-demand) / 12% (provisioned) |
Jake asked the obvious question here: "So which one do I pick?" Ethan's answer was less obvious than Jake expected: "Neither, yet. You reserve capacity once you trust the number. Guess wrong on a three-year commitment and you're paying for a table that doesn't exist anymore. Get six months of real CloudWatch history first — the discount isn't going anywhere."
Reads, Writes, and the Table Class You Chose
Reads and writes crossed over at the same 28.9% earlier in this post, which is a genuine coincidence of how AWS priced things, not a rule you can assume holds forever. What doesn't change the crossover, at least not materially, is consistency level or table class.
Eventually consistent reads cost half of a strongly consistent read in both provisioned (0.5 RCU per 4 KB) and on-demand (half a request unit per 4 KB) pricing. Since both sides of the comparison get cut by the same factor, the break-even percentage stays at roughly 29%. The same logic holds for transactional reads and writes, which cost double in both modes — again, both sides move together, so the crossover doesn't shift.
Table class is a slightly different story, because Standard-Infrequent Access changes the storage-to-throughput cost ratio, not just the throughput rate. Running the same division on Standard-IA pricing ($0.00081/WCU-hr provisioned, $0.780/million writes on-demand) gives a break-even of about 28.8% — close enough to the Standard table class number that table class choice isn't the lever that moves your crossover. Table class should be decided by whether storage or throughput dominates your bill, which is a separate question from capacity mode.
Global Tables Don't Move the Crossover — They Just Multiply It
If your table replicates to a second Region as a global table, replicated writes are billed in replicated write request units (rWRUs) for on-demand tables and replicated write capacity units (rWCUs) for provisioned tables. Since the November 2024 change, AWS priced replicated writes to match single-Region write pricing — a 67% cut for on-demand replicated writes and a 33% cut for provisioned replicated writes, bringing both roughly in line with the base single-Region rate. That means the crossover percentage itself doesn't change for a global table; what changes is the volume you're multiplying it by, since every write now happens in every replica Region.
A table doing 100 writes per second to one Region becomes 100 writes per second times the number of replicas once global tables are in play — so a two-Region global table with steady traffic doubles both your provisioned rWCU requirement and your on-demand rWRU bill, but doesn't change whether the 29% line favors provisioned or on-demand. Run the crossover calculation on the replicated write volume per Region, not the pre-replication write volume, or you'll underestimate the provisioned capacity you'd need by half.
Reserved provisioned capacity does not cover replicated WCUs — that's an explicit exclusion in AWS's reserved-capacity terms, so a global table's cross-Region replication cost stays at standard provisioned rates (or is eligible for Database Savings Plans instead) even if the base table's local writes are reserved.
Vector Search: On-Demand Only, Different Rules Entirely
If you're adding DynamoDB's vector search feature — vector indexing and similarity search for retrieval-augmented generation or agentic AI workloads on top of an existing table — the on-demand-versus-provisioned question doesn't apply to it at all. Vector search is available only in on-demand capacity mode; there's no provisioned billing option for it, so the crossover math in this whole post is irrelevant to that piece of your bill.
Vector search bills on three separate dimensions, per byte rather than per request-unit-style rounding, with a 1 KB minimum charge per operation: vector write requests (data written into the vector index, in GB), vector search requests (data processed to run a similarity search plus the data returned, in GB), and storage (the vector index size, per GB-month, billed the same way as regular table storage). Search cost grows logarithmically with the number of items in the index rather than linearly — a search examines a small subset of the index, not every vector, so increasing the item count 10,000-fold increases the data processed per search by less than 1.5 times. Standard-IA table class applies to vector search too, at 40% of Standard's storage rate and 125% of its request rate.
Practically: if a table mixes ordinary item access with vector search, you'll run the crossover calculation from this post on the base table's RCU/WCU or RRU/WRU traffic exactly as described, and then account for vector search costs as a separate, always-on-demand line item alongside it.
The Costs That Don't Care Which Mode You Pick
A chunk of a typical DynamoDB bill has nothing to do with capacity mode, and it's worth separating that out so you're not chasing a crossover calculation on charges the crossover doesn't touch.
Data storage is billed the same way regardless of capacity mode: $0.25 per GB-month on the Standard table class, $0.10 per GB-month on Standard-IA, with the first 25 GB included in the free tier per Region per account, every month, ongoing — not a one-time trial allowance.
Point-in-time recovery (PITR) backups bill on the size of the table, continuously monitored, for as long as PITR stays enabled — shortening the retention window from 35 days to 1 day doesn't lower the cost, since the charge is driven by table size, not retention length.
Change Data Capture for Kinesis Data Streams or AWS Glue bills at $0.10 per million CDC units, one unit per write up to 1 KB captured — this is on top of whatever your base table write costs are in either capacity mode, and the rate doesn't change based on whether the underlying table is on-demand or provisioned.
DAX (DynamoDB Accelerator), the in-memory cache layer, is billed per node-hour regardless of what capacity mode the underlying table uses, and it has no reserved-pricing option — every DAX node-hour is billed at the standard on-demand-instance rate, with partial hours rounded up to a full hour.
Warm throughput values are free by default on every table and index, in both capacity modes, and adjust automatically as your provisioned throughput or on-demand consumption changes. You're only charged if you proactively pre-warm a table above its current default value, at a one-time fee equal to the standard regional RCU or WCU rate for each unit of increase.
None of these line items shift the 29% crossover point. They're additive costs that sit next to the capacity-mode decision, not part of it — worth knowing so a bill that looks "wrong" after switching modes doesn't send you back to recheck math that was never broken.
Switching Modes Without Breaking Your Table
Once you know which side of the line your table sits on, changing modes is a table-level setting, but it isn't instant and it isn't unlimited. AWS's documentation on switching capacity modes sets two limits: you can switch a table from provisioned mode to on-demand mode up to four times within a rolling 24-hour window, and you can switch from on-demand mode back to provisioned at any time, with no stated frequency cap in that direction.
When you switch from provisioned to on-demand, DynamoDB restructures the table and its partitions in the background, which can take several minutes. During that window, the table continues to deliver throughput consistent with its previous provisioned WCU and RCU settings rather than immediately jumping to on-demand's scaling behavior.
New on-demand tables — and tables switching to on-demand for the first time — start with a baseline they can sustain immediately: up to 4,000 write request units and 12,000 read request units per second, or a linear combination of both, without any warm-up. Beyond that starting point, on-demand mode instantly accommodates traffic up to double whatever peak the table has previously reached; if you sustain a new higher peak, that becomes the new baseline for future doubling. Push past double your previous peak within a 30-minute window and you risk throttling — spread traffic growth over at least 30 minutes when you know a spike is coming.
If you're moving a table from provisioned to on-demand and want to avoid a rocky first day, here's the safer order of operations:
- Pull your CloudWatch history first. Know your actual peak consumed RCUs/WCUs over the last 90 days before you touch the billing mode.
- Check it against the 4,000 WRU / 12,000 RRU starting baseline. If your historical peak is comfortably under that, the switch is low-risk. If you regularly exceed it, plan for the "double the previous peak" ramp instead of assuming instant unlimited scale.
- Make the switch during a low-traffic window so the several-minute partition restructuring doesn't overlap with your busiest hours.
- Watch for throttled requests in the first 24-48 hours using the account-level and table-level throttling metrics in CloudWatch, particularly if you expect a traffic pattern that could exceed double your prior peak quickly.
- Keep the four-switches-per-day allowance in mind if you're testing. If you're going back and forth between modes for load testing, you have headroom to do that from provisioned to on-demand, but switching back to provisioned has no published rate limit in the documentation, so lean on that direction if you need more iterations.
Automating the switch for time-boxed workloads
A table with a genuinely predictable schedule — heavy during business hours, near-idle overnight — doesn't have to pick one mode and live with it. Because switching from provisioned to on-demand is allowed up to four times in a rolling 24 hours, and switching back to provisioned has no published limit, some teams build a scheduled function (a CloudWatch Events/EventBridge rule triggering a Lambda function, for instance) that raises provisioned capacity ahead of the daily peak and lowers it afterward, rather than relying on auto scaling's reactive adjustments or paying on-demand rates around the clock. This only makes sense when the schedule is genuinely predictable — an irregular spike defeats the purpose, since you'd be manually guessing the same thing auto scaling already reacts to automatically.
The Traps That Make the Textbook Math Wrong
Hot partitions
The crossover math above assumes your provisioned capacity is evenly usable across the table. It isn't, automatically. DynamoDB spreads your provisioned throughput across partitions, and each partition has its own ceiling — up to 3,000 read capacity units or 1,000 write capacity units, or a linear combination, per partition, regardless of the table-level total. If your access pattern concentrates on one partition key — a single popular item, a sequential ID, a "today's date" key that every write hits — that one partition can throttle even while the table's aggregate provisioned capacity sits mostly unused. When that happens, the fix isn't more capacity, it's a better key design, and no amount of provisioned or on-demand math solves it until the key design changes.
Global secondary indexes multiply the bill
Every global secondary index (GSI) you add to a table consumes its own write capacity separately from the base table, on top of whatever RCU/WCU or RRU/WRU cost your base table writes already incur. A table with two GSIs that duplicate every attribute isn't paying "one write's worth" per item change — it's paying for the base table write plus a write to each index. Run your crossover calculation against total consumed capacity across the table and all its indexes, not just the base table, or you'll underestimate both sides of the comparison equally and still land in the wrong mode for indexes with a very different access pattern than the base table.
Item size rounds up, it doesn't average out
Both modes bill in whole-unit increments per 4 KB (reads) or 1 KB (writes) of item size, rounded up. A 1.1 KB item consumes 2 WCUs, not 1.1. If your items cluster just above a rounding boundary, your effective cost per item is higher than the raw per-unit price suggests, in both provisioned and on-demand pricing equally — so it doesn't change which side of the crossover you're on, but it does mean your real bill will run higher than a calculation based on average item size in bytes, and it's worth checking actual item sizes against the 1 KB/4 KB boundaries before trusting a back-of-envelope estimate.
⚠️ What reserved capacity actually locks you into
Reserved provisioned capacity is non-cancellable, non-transferable between Regions or accounts, and the upfront fee is non-refundable. If you commit to a three-year term and your traffic pattern changes six months in — say a workload gets decommissioned or moves to a different table design — you keep paying for the reservation regardless. Only commit capacity you're confident will still exist through the full term.
A Decision Table by Workload Pattern
| Workload pattern | Estimated utilization | Likely cheaper mode |
|---|---|---|
| New app, traffic pattern unknown | Unknowable | On-demand — you can't provision correctly for a pattern you haven't seen yet |
| Steady 24/7 API backend, similar traffic every hour | Often 40–80%+ | Provisioned, likely with auto scaling; reserved capacity if traffic has been stable 6+ months |
| Business-hours-only workload, near zero overnight | Under 29% averaged across 24 hours | On-demand, or scheduled provisioned switching for a genuinely fixed schedule |
| Rare, extreme spikes (promotions, launches) | Very low, if provisioned to cover the spike | On-demand — provisioning for the spike wastes capacity the rest of the month |
| Background batch/ETL jobs, predictable schedule | High during the job window, near zero otherwise | On-demand, unless the job runs long enough hours daily to clear 29% |
| Multi-tenant SaaS, one table per customer | Varies wildly per tenant | On-demand for simplicity and isolation across many small, uneven tables |
| RAG/agentic AI table using vector search | Not applicable to the base crossover | On-demand only — vector search has no provisioned option |
What to Do Right Now
Ethan's take on this, after Jake's trade-in weekend bill showed up: "Don't guess. You have the CloudWatch numbers sitting there already — pull them before you touch the billing mode, not after." He's not wrong. The whole crossover calculation depends on knowing your real consumed capacity, and that data already exists in your account whether you've looked at it or not. Jake pushed back — "So I just leave it on-demand forever and stop worrying about it?" — and Ethan's answer was less tidy than that: "Only if you enjoy paying triple rate for the two hours a day you're actually busy. Do the math once. It's not that hard."
- Run the CloudWatch pull from the crossover section against your actual table, separately for reads and writes.
- Check your GSIs' consumed capacity too, not just the base table — they're billed separately and can sit on a different side of the crossover than the base table does.
- Cross-check against AWS's own Pricing Calculator to sanity-check your manual math before committing to a mode change, particularly if storage, backups, or streams are a meaningful share of the bill.
- If you land clearly above ~29% and your traffic has been stable for months, move to provisioned with auto scaling, and evaluate reserved capacity against your specific commitment tolerance.
- If you land clearly below ~29%, or you can't confidently forecast next quarter's traffic, stay on or move to on-demand and revisit the numbers again in three to six months.
- If reads and writes disagree — one side above the line, one below — remember DynamoDB lets you choose provisioned or on-demand at the table level, not independently per operation type, so you're picking whichever mode wins on balance, factoring in which dimension carries more of your total request volume.
Frequently Asked Questions
What is the exact crossover point between DynamoDB on-demand and provisioned pricing?
Using current US East (N. Virginia) Standard table class rates, the break-even is approximately 28.9% utilization for both reads and writes — if a provisioned unit is actually used more than about 29% of the time, provisioned capacity costs less than the on-demand equivalent for that same traffic.
Does the November 2024 price cut change which mode is cheaper?
Yes, and not in the direction most people assume. The cut only lowered on-demand throughput pricing by 50%; provisioned rates didn't change. That moved the break-even utilization up from roughly 14% to roughly 29%, meaning provisioned needs a higher utilization than it used to in order to still win.
Is on-demand ever cheaper than provisioned even at 100% utilization?
No. At 100% utilization, provisioned is always cheaper per unit of throughput on standard rates, since on-demand's per-request price is more than three times the equivalent provisioned per-hour price. On-demand only wins when utilization is below the crossover point.
How do I calculate my table's actual utilization percentage?
Pull ConsumedReadCapacityUnits and ConsumedWriteCapacityUnits from CloudWatch over at least 30 days, average them per second, and divide by your provisioned RCUs and WCUs for the same period. Calculate reads and writes separately, since their traffic patterns are usually different.
Does provisioned capacity risk throttling if I guess wrong?
Yes. If your consumed capacity exceeds what you've provisioned, requests get throttled and your application sees errors, unless auto scaling is enabled and reacts in time. On-demand removes this specific risk for overall table throughput, though both modes still enforce a per-partition ceiling that a hot key can hit regardless of mode.
What happens if I switch from provisioned to on-demand and back?
Switching to on-demand triggers a background restructuring of the table and its partitions that can take several minutes, during which the table continues serving at its previous provisioned throughput levels. Switching back to provisioned requires you to specify new RCU/WCU settings, ideally based on the consumed-capacity history from the on-demand period.
How many times can I change DynamoDB capacity mode in a day?
You can switch from provisioned to on-demand up to four times within a rolling 24-hour window. You can switch from on-demand back to provisioned at any time, with no stated frequency limit in that direction.
Does auto scaling on provisioned capacity eliminate the throttling risk?
It reduces it but doesn't eliminate it. Auto scaling reacts to consumed capacity crossing your target utilization threshold, so it responds after traffic changes rather than anticipating it — a very fast spike can still throttle before scaling catches up, and it does nothing for a single hot partition regardless of table-wide capacity.
Is reserved provisioned capacity worth it for a smaller workload?
Reserved capacity is sold in blocks of 100 WCUs or RCUs, so it's most practical once you're consistently using capacity in that range. Below that, the block size may force you to reserve more than you need, eroding the discount's value against a smaller, steadier workload.
What are Database Savings Plans and how do they differ from reserved capacity?
Database Savings Plans are a one-year commitment to a dollar-per-hour spend level that applies flexibly across Regions and table classes, covering up to 18% off on-demand throughput and up to 12% off provisioned throughput. Reserved provisioned capacity is a fixed-Region, fixed-table-class commitment to specific RCU/WCU quantities with deeper discounts (up to 77% for three years), but only for provisioned Standard-class capacity.
Does table class (Standard vs Standard-IA) change the crossover point?
Barely. Running the crossover calculation on Standard-Infrequent Access on-demand and provisioned rates yields a break-even around 28.8%, essentially the same as the Standard table class. Table class should be chosen based on whether storage or throughput dominates your bill, not based on capacity mode.
Can hot partitions make provisioned capacity cost more than the math suggests?
Yes. A hot partition can throttle well before your table-wide provisioned capacity is exhausted, since each partition has its own ceiling (up to 3,000 RCUs or 1,000 WCUs). To avoid throttling, teams sometimes over-provision the whole table to compensate for one hot key, which raises the effective cost of provisioned capacity above what the table-wide utilization number implies.
Do global tables change the on-demand vs provisioned crossover math?
No, the crossover percentage stays roughly the same, since AWS priced replicated writes to match single-Region rates. What changes is volume: every write now happens in every replica Region, so a two-Region global table roughly doubles the provisioned capacity or on-demand request volume you're calculating against — run the math on the replicated write count, not the pre-replication count.
Is DynamoDB vector search billed under on-demand or provisioned?
On-demand only — there's no provisioned billing option for vector search. It's billed per byte across three separate dimensions (vector writes, vector search, and index storage), with a 1 KB minimum charge, independent of whatever capacity mode the underlying table uses for regular reads and writes.
Should a brand-new table start on provisioned or on-demand?
On-demand. You have no consumed-capacity history to provision against yet, and on-demand tables start with a sustainable baseline of up to 4,000 write request units and 12,000 read request units per second without any warm-up. Once you've collected a few months of real CloudWatch data, run the crossover calculation and switch to provisioned if the utilization justifies it.
Also Read:
- What Is Amazon DynamoDB? NoSQL in Plain English
- What Is Amazon S3? Cloud Storage Explained in Plain English
- AWS costs: The NAT gateway bill - the $32/month nobody planned
- AWS costs: Data transfer pricing - the map of what is free and what is not
- AWS costs: CloudWatch Logs ingestion - the log bill that outgrew the app
- AWS costs: Cross-AZ traffic - the invisible cent per gigabyte
- AWS costs: RDS vs Aurora pricing - a small app, both ways
- Fix DynamoDB ProvisionedThroughputExceededException
Revision note. Written September 2026, covering DynamoDB on-demand and provisioned pricing for the Standard and Standard-Infrequent Access table classes in US East (N. Virginia), current as of the November 2024 price reduction. Rates and discount percentages are AWS-published figures and can change without notice — check the current DynamoDB pricing page before committing to a multi-year reservation. If a surprise bill is what brought you here, you're not the first person this crossover has caught out, and running the numbers once is usually enough to stop it happening again.Happy learning, see you on next post!