# DynamoDB On-Demand Billing with Steady Traffic

> ZopNight recommends switching an on-demand DynamoDB table to provisioned capacity only when 30 days of consumed read plus write capacity shows a coefficient of variation under 0.50 and the real rate delta (on-demand request cost minus provisioned capacity sized at a 70% auto-scaling target) clears a $5 monthly floor.

Source: https://zop.dev/integrations/aws/recommendations/dynamodb-on-demand-billing-with-steady-traffic
Updated: 2026-08-19

---

## Steadiness is measured, not assumed

On-demand billing charges a premium per request in exchange for never planning capacity.
That trade is right for spiky workloads and wrong for flat ones. This rule quantifies
flatness instead of guessing at it: it sums the `ConsumedReadCapacityUnits` and
`ConsumedWriteCapacityUnits` series, [both reported per table under the AWS/DynamoDB namespace](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/metrics-dimensions.html), slot by slot across 30 days and computes the
coefficient of variation (standard deviation divided by mean) of the combined series.
Below 0.50 the traffic is classified steady. At or above it, the workload is spiky
enough that on-demand is the correct mode, and the rule abstains.

The 30-day window is sized to span weekly traffic cycles, so a quieter-than-usual day
cannot pass itself off as a steady month.

## The dollar figure is a rate delta, not a percentage

The saving is priced from four real AWS rate SKUs: the on-demand read and write request
rates, and the provisioned RCU and WCU hourly rates, [the four numbers DynamoDB bills capacity on](https://aws.amazon.com/dynamodb/pricing/). Current cost is the measured
average consumption multiplied by the request rates across 730 hours. The provisioned
alternative is sized from the steady peak: consumed units per hour divided by 3,600, since
one provisioned unit supplies one capacity unit per second, then divided by the 0.70
auto-scaling target so the parked capacity carries the load with headroom, rounded up,
with a minimum of one unit per axis. Sizing to the peak with headroom keeps the estimate
conservative rather than best-case.

## Seven ways to abstain

The rule stays silent when the table is not on on-demand billing, when either capacity
series is missing, when coverage is under 7 days, when traffic is not steady, when the
combined mean is zero (a zero-traffic table is a deletion candidate, not a
capacity-mode swap), when any of the four rates is unavailable, or when the computed
saving lands under $5 a month. A finding from this rule always carries a concrete
dollar delta, never an estimate backfilled from a flat fraction.

## Reproduce the shape yourself

```bash
aws cloudwatch get-metric-statistics \
  --namespace AWS/DynamoDB --metric-name ConsumedReadCapacityUnits \
  --dimensions Name=TableName,Value=my-table \
  --start-time "$(date -u -v-30d +%Y-%m-%dT%H:%M:%SZ)" \
  --end-time "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
  --period 3600 --statistics Average Maximum
```

Repeat for `ConsumedWriteCapacityUnits`. A flat Average with a Maximum close to it is
the steady profile this rule prices.

## After switching

Change the capacity mode under the table's additional settings, enable auto-scaling
with the 70% target, and set the min and max to the observed range. Then watch
`ThrottledRequests` for 48 hours; throttling right after the switch means the floor
was sized too tight.
