awsdynamodbcost-optimization

DynamoDB on-demand vs provisioned: which mode to choose

On-demand bills per request, provisioned bills per capacity-hour. The break-even is around 25% utilization. Here's the actual math, the decision framework, and how to set up auto-scaling and Reserved Capacity for the lowest steady-state cost.

The C3X Team··10 min read

Quick answer

For unpredictable or low-baseline workloads (dev, new apps, spiky traffic), on-demand mode is right. For steady production workloads above ~25% capacity utilization, provisioned mode with auto-scaling is 2-3x cheaper. For long-term commitments, provisioned + reserved capacity saves another 50-77%. Switch modes at most once per 24 hours per table.

DynamoDB has two capacity modes with fundamentally different billing models. Picking the right one for each table is a meaningful cost lever, often 2-3x difference at sustained usage. This post walks through the actual pricing math, the decision framework, and when to switch between modes.

The two pricing models

On-demand mode

Pay per request. Each read request unit (RRU) and write request unit (WRU) is billed when it happens. Rates in us-east-1:

  • $0.25 per million read request units
  • $1.25 per million write request units (5x more)

No capacity planning. No idle cost. The first 2.5M reads and 2.5M writes per month are free at the account level. Right for unpredictable or low-baseline workloads.

Provisioned mode

Pre-provision capacity in RCU/WCU. Pay per capacity-hour regardless of utilization. Rates:

  • $0.00013 per RCU-hour ($0.095/RCU/month)
  • $0.00065 per WCU-hour ($0.475/WCU/month)

WCUs are 5x more expensive than RCUs (same ratio as on-demand). Capacity bills continuously whether you use it or not.

The break-even math

The crossover point depends on capacity utilization. Consider a workload requiring sustained 100 reads/sec and 50 writes/sec.

On 100% utilization (sustained at provisioned capacity)

On-demand:

  • Reads: 100/sec × 30 days = 259M reads × $0.25/M = $65/month
  • Writes: 50/sec × 30 days = 130M writes × $1.25/M = $162/month
  • Total: $227/month

Provisioned (matched exactly):

  • 100 RCUs × $0.095 = $9.50/month
  • 50 WCUs × $0.475 = $23.75/month
  • Total: $33.25/month

At 100% utilization, provisioned is 7x cheaper. But you never actually run at 100% — capacity has to handle peaks.

At 50% utilization (typical with some headroom)

On-demand: $227/month × 0.5 = $113.50/month

Provisioned (50% utilized, so provisioned 2x peak):$66.50/month

Provisioned still wins by 40%. The discount narrows as utilization drops.

At 10% utilization (high overprovisioning)

On-demand: $22.70/month

Provisioned (10% utilized, provisioned 10x peak):$332.50/month

At low utilization, on-demand is much cheaper because you only pay for actual requests. The crossover is around 18-25% utilization for most workloads.

The right framework: think about utilization, not traffic shape

The decision isn't really about whether traffic is "predictable" or "spiky." It's about average vs peak utilization.

High average utilization (over 30%): provisioned + auto-scaling

Production APIs with steady traffic, batch processing pipelines with known schedules, IoT telemetry with consistent volume. Use provisioned mode with auto-scaling configured to your usage pattern.

Auto-scaling setup: min_capacity at 80% of off-peak demand, max at 120% of peak, target utilization 70%. The scaler adjusts capacity to stay near target.

Low average utilization (less than 25%): on-demand

Dev/staging tables, applications with strong peak/off-peak patterns (e.g., 9-5 business apps), tables that get sporadic traffic. On-demand bills only for actual requests; you don't pay for the off-peak idle.

Unknown utilization: start on-demand

New applications or tables where you don't know the traffic pattern yet. Start on on-demand for a month, look at CloudWatch consumed capacity metrics, then decide whether to switch.

Auto-scaling: getting provisioned mode's benefit without the work

DynamoDB auto-scaling solves the over-provisioning problem. Configure min/max bounds and target utilization, and the scaler adjusts capacity automatically.

resource "aws_appautoscaling_target" "table_read" {
  max_capacity       = 1000
  min_capacity       = 100
  resource_id        = "table/${aws_dynamodb_table.main.name}"
  scalable_dimension = "dynamodb:table:ReadCapacityUnits"
  service_namespace  = "dynamodb"
}

resource "aws_appautoscaling_policy" "table_read" {
  name               = "DynamoDBReadCapacityUtilization"
  policy_type        = "TargetTrackingScaling"
  resource_id        = aws_appautoscaling_target.table_read.resource_id
  scalable_dimension = aws_appautoscaling_target.table_read.scalable_dimension
  service_namespace  = aws_appautoscaling_target.table_read.service_namespace

  target_tracking_scaling_policy_configuration {
    target_value = 70.0
    predefined_metric_specification {
      predefined_metric_type = "DynamoDBReadCapacityUtilization"
    }
  }
}

Same configuration applies to writes via the DynamoDBWriteCapacityUtilization metric.

With auto-scaling, you get provisioned mode's per-unit cheaper pricing while the system right-sizes capacity for you. The scaler responds to demand changes within minutes; for spiky workloads with sub-minute spikes, on-demand might still be a better fit because auto-scaling can lag.

Reserved capacity for the lowest steady-state cost

DynamoDB Reserved Capacity is a separate commitment product (not the same as Compute Savings Plans). Commit to a baseline of RCU/WCU for 1 or 3 years and get 50-77% off:

  • 1-year all-upfront: 50% off
  • 1-year no-upfront: 35% off
  • 3-year all-upfront: 77% off

Reserved Capacity applies to provisioned mode only. Buy it for your baseline; use auto-scaling above the baseline at on-demand provisioned rates.

See the Reserved Instances vs Savings Plans post for the broader commitment strategy. DynamoDB RC works differently from compute commitments but the same baseline- vs-burst principles apply.

The full pricing comparison

For a moderately-sized production table with these dimensions:

  • Sustained 100 reads/sec, 50 writes/sec (260M reads, 130M writes/month)
  • 30 GB storage
  • 1 GSI on a secondary key

On-demand mode:

  • Reads: $65/month
  • Writes (base + GSI = 2x): $325/month
  • Storage: $7.50/month
  • Total: $397.50/month

Provisioned + auto-scaling (60% utilization):

  • RCUs at 60% util: 167 RCUs × $0.095 = $15.86
  • WCUs at 60% util (base + GSI): 166 WCUs × $0.475 = $78.85
  • Storage: $7.50
  • Total: $102.21/month

Provisioned + 1-yr Reserved Capacity:

  • Compute (50% off RC rates): $47.35
  • Storage: $7.50
  • Total: $54.85/month

From on-demand to provisioned + RC: 86% savings. Same workload, same performance, very different bill.

When NOT to switch

Three scenarios where on-demand is the right answer despite the provisioned discount:

  • True sporadic traffic: A table that fires briefly during specific events (cron jobs, monthly reports) and sits idle the rest of the time. Provisioned would pay for idle capacity 95% of the time.
  • Very small workloads in free tier: 2.5M reads and 2.5M writes per month are free in on-demand. Tables under this threshold cost $0 on-demand vs the minimum provisioned cost.
  • Brand-new applications: Before you have telemetry on actual traffic patterns, on-demand is safer than guessing capacity. Switch once you have a month of data.

How to estimate in C3X

For provisioned tables, c3x reads provisioned_throughput from the Terraform resource and computes monthly cost directly.

For on-demand tables, specify expected request volumes in c3x-usage.yml:

# c3x-usage.yml
resource_usage:
  aws_dynamodb_table.events:
    monthly_read_request_units: 260000000
    monthly_write_request_units: 130000000
    storage_gb: 30

c3x estimates each mode and applies the free tier. See the aws_dynamodb_table catalog page for full pricing dimensions.

FAQ

DynamoDB on-demand or provisioned, which is cheaper?

Provisioned is cheaper at sustained high utilization (>25% of provisioned capacity). On-demand is cheaper for low or unpredictable traffic. The break-even depends on your read/write ratio and traffic pattern. For a steady workload at 50% utilization, provisioned can be 2-3x cheaper than on-demand. For sporadic workloads, on-demand wins because you don't pay for unused capacity.

Can I switch between modes?

Yes, with limits. You can switch once every 24 hours per table. The new mode applies immediately. Common pattern: start tables in on-demand for new applications, switch to provisioned + auto-scaling once traffic patterns stabilize.

How do auto-scaling and provisioned mode work together?

DynamoDB auto-scaling adjusts provisioned RCU/WCU within bounds you set (e.g., min 100, max 1000, target utilization 70%). It scales based on consumed capacity. Right for variable workloads where you want provisioned mode's lower per-unit cost without manually tuning capacity.

What about reserved capacity?

DynamoDB Reserved Capacity cuts provisioned WCU/RCU prices by 50% (1-year) to 77% (3-year all-upfront). Only applies to provisioned mode, not on-demand. Right for production tables with stable baseline usage. Combine reserved capacity with auto-scaling for the cheapest sustained-traffic configuration.

Does the on-demand free tier change the math?

AWS gives 2.5M read request units and 2.5M write request units per month free for on-demand mode (plus 25 GB storage). For tables under this threshold, on-demand effectively costs $0. As soon as you exceed the free tier, on-demand starts billing per request.

Is GSI cost different between modes?

GSIs follow the same mode as the base table. A provisioned table's GSIs use provisioned capacity. An on-demand table's GSIs are on-demand. The mode is table-level, not GSI-level. GSI writes are counted separately (each write to the base table also writes to each matching GSI).

The decision summary

  1. New application or unknown traffic: on-demand.
  2. After 1 month of data, check CloudWatch consumed capacity. If average utilization is >25%, switch to provisioned + auto-scaling.
  3. For stable production tables, add Reserved Capacity at the baseline.
  4. Re-evaluate quarterly. Traffic patterns change.

For full estimation workflow including DynamoDB and other services, see how to estimate AWS costs from Terraform.

Try C3X on your own Terraform

Free and open source. No API key required. One command to install, one command to estimate.