Difficulty: Medium
Description
We identified DynamoDB tables running in On-Demand capacity mode with stable, predictable throughput. On-Demand bills each request at roughly 3.5× the equivalent Provisioned rate, that premium buys elasticity these tables never use. Switching them to Provisioned capacity with auto scaling typically reduces throughput cost by 40–60%, with no application code changes.
Selection Criteria
The table is in On-Demand capacity mode.
At least 30 days of consumption history (
ConsumedReadCapacityUnits/ConsumedWriteCapacityUnits) to establish a reliable baseline.The traffic is predictable — the peak-to-average ratio of hourly consumed capacity is ≤ 3. Above that, auto scaling either lags behind the spikes (throttling) or has to over-provision (no savings).
The table has no extended idle periods — capacity is consumed in at least 90% of hours over the 30-day window. A table that is idle 20 hours a day is a genuine On-Demand fit and is excluded.
The projected net savings is at least 10% of the total annualized table throughput cost.
Consumption is not dominated by a single short daily burst — a batch-driven table whose entire load lands in a 15-minute window cannot be provisioned efficiently.
⚠️ Note — Sequence with the Reserved Capacity recommendation
This recommendation and Purchase Reserved Capacity are never surfaced on the same table at the same time — the eligibility criterion (current capacity mode) is mutually exclusive. They are sequential: once this conversion is applied and the table has accumulated 30 days of stable Provisioned usage, it becomes a Reserved Capacity candidate.
The two savings must never be added together. When Reserved Capacity is later evaluated on a converted table, its baseline is the post-conversion provisioned cost, not the original On-Demand cost. Stacking the two figures would report savings larger than the table's total cost.
Related recommendations
These three DynamoDB recommendations interact. The two capacity-mode recommendations — this one and Purchase Reserved Capacity — are never eligible on the same table at the same time. The Standard-IA table-class recommendation is independent of both, but constrains what comes next:
Purchase Reserved Capacity — the downstream step. On-Demand tables are not eligible. After this conversion and 30 days of stable Provisioned usage, the table becomes a Reserved Capacity candidate. The two savings are never added together: the Reserved Capacity baseline is recomputed from the post-conversion provisioned cost.
Change Storage Type to Standard-IA — compatible with this recommendation; Standard-IA supports both capacity modes. If the table is storage-dominated, evaluate Standard-IA first. Note that a Standard-IA table cannot subsequently benefit from Reserved Capacity.
Expected Saving
40% to 60% of the table's throughput cost, depending on how closely auto scaling can track the consumption curve. The theoretical maximum is 71% (at 100% utilization, unreachable in practice).
Break-even analysis (us-east-1, STANDARD table class):
On-Demand per write = $0.625 / 1,000,000 = $0.000000625 Provisioned per write = $0.00065 / 3,600 writes per WCU-hour = $0.000000181 Ratio = 3.46x Break-even utilization = 1 / 3.46 = 28.9%
The same 3.46× ratio applies to reads ($0.125 per million RRU vs. $0.00013 per RCU-hour).
Example — orders-events table:
region | usage_type | avg_consumed/s | peak_consumed/s | peak:avg | on_demand_cost/mo |
us-east-1 | WCU | 620 | 1,612 | 2.6 | $1,018.35 |
us-east-1 | RCU | 2,900 | 7,540 | 2.6 | $952.65 |
Net savings calculation (730 h = average hours per month, auto scaling target utilization = 70%):
On-Demand monthly cost Writes = 620 × 3,600 × 730 × $0.000000625 = $1,018.35 Reads = 2,900 × 3,600 × 730 × $0.000000125 = $952.65 Total = $1,971.00 Provisioned monthly cost (auto scaling @ 70% target) Provisioned WCU = 620 / 0.70 = 886 → 886 × 730 × $0.00065 = $420.41 Provisioned RCU = 2,900 / 0.70 = 4,143 → 4,143 × 730 × $0.00013 = $393.17 Total = $813.58 Monthly savings = $1,971.00 − $813.58 = $1,157.42 (58.7%) Annual savings = $1,157.42 × 12 = $13,889.04
The savings rate depends almost entirely on the auto scaling target utilization: at a 70% target the cost ratio is (1 / 0.70) / 3.46 = 41.3% of the On-Demand bill. A more conservative 50% target yields ~42% savings instead of 58.7%. AWS allows targets between 20% and 90%.
⚠️ This example is an upper bound, not a typical outcome. The calculation assumes auto scaling tracks the consumption curve perfectly. In practice scale-up requires the target to be breached for 2 consecutive minutes before UpdateTable is issued, and scale-down requires 15 consecutive datapoints below target — so realized average provisioned capacity sits above avg / 0.70. For a table with a 2.6 peak-to-average ratio, expect a result toward the middle of the 40–60% band rather than at its top.
Operational Impact
Downtime: None — the capacity mode change is applied online. The switch takes several minutes, during which the table continues to serve throughput at its previous level.
Breaking change: None — no application code changes required. The DynamoDB API is identical in both capacity modes.
Throttling risk ⚠️: This is the principal risk of this recommendation. If provisioned capacity is set below actual demand, requests are throttled with ProvisionedThroughputExceededException. AWS SDKs retry these with exponential backoff by default, so the first symptom is added latency rather than outright failure. Note that partition-level (hot key) throttling returns ThrottlingException instead and is not solved by adding capacity. Mitigations:
Configure auto scaling at the same time as the switch — do not leave the table on a flat provisioned value.
Set minimum capacity to the observed baseline, not the AWS console default of 5 units.
Size initial capacity from the CloudWatch peak, not the average, to absorb the transition.
Auto scaling reacts in minutes, not seconds — short spikes can throttle before it catches up.
Global Secondary Indexes ⚠️: The capacity mode change applies to the table and all of its GSIs. Each GSI requires its own provisioned capacity and its own auto scaling policy. A GSI left at default capacity is the most common cause of throttling after a conversion.
Constraint: Switching On-Demand → Provisioned can be done at any time, with no frequency limit. The reverse direction (Provisioned → On-Demand) is limited to 4 times per 24-hour rolling window, which bounds how quickly the change can be rolled back and rolled back again.
Recovery: Fully reversible with no data risk, the table can be switched back to On-Demand at any time, subject to the 4-per-24-hour limit above. Auto scaling settings are preserved when switching via the AWS CLI or SDK; they are deleted when switching via the console.
Lighter-weight alternative: If the objective is only to bound cost exposure rather than reduce the baseline rate, On-Demand tables support a maximum read/write throughput setting (MaxReadRequestUnits / MaxWriteRequestUnits). It carries none of the throttling-management overhead of a full conversion, but it is a guardrail against runaway spend rather than a hard cost ceiling: AWS applies it on a best-effort basis, burst capacity can temporarily exceed it, requests above the limit return ThrottlingException, and it does not reduce the underlying per-request rate.
