Intermediate9 min read

How to Set Up DynamoDB Auto Scaling

DynamoDB auto scaling adjusts a provisioned table's read and write capacity toward a target utilization you choose, so you stop hand-tuning RCU/WCU and stop paying for a worst-case reservation around the clock. This guide is the hands-on half of the capacity story: the console path, the CLI commands, how to actually pick the numbers, and the timing limits that still throttle a sharp spike. If you haven't picked a capacity mode yet, start with On-Demand vs Provisioned — auto scaling only applies to Provisioned.

How do you enable auto scaling on a DynamoDB table?

In the console: open your table, go to Additional settingsRead/write capacityEdit, choose Provisioned, and set Auto scaling to On for read capacity, write capacity, or both, giving each a minimum, maximum, and target utilization (settable between 20 and 90 percent). From the CLI, register a scalable target and attach a target-tracking scaling policy with aws application-autoscaling. Tables created through the console get auto scaling enabled by default.

What auto scaling actually does

A scaling policy tells Application Auto Scaling to hold a table's consumed-to-provisioned ratio near your target utilization, inside the minimum and maximum capacity bounds you set. Under the hood it creates a pair of CloudWatch alarms for the upper and lower boundaries; when consumption crosses one, Application Auto Scaling issues an UpdateTable call to move the provisioned capacity.

Two structural facts matter before setup:

  • Policies are per table and per GSI. Every global secondary index has its own provisioned throughput, so each one needs its own policy (or the console's "same settings for all GSIs" checkbox). An under-scaled GSI can throttle base-table writes — see why a GSI throttles the base table.
  • Console-created tables opt in by default; GSIs added later don't scale while building. A new GSI on an existing table starts with manual capacity during its backfill — watch it until the policy attaches.

The timing you're signing up for

Auto scaling is reactive, and its reaction times are fixed — AWS documents that the alarm data-point counts are not adjustable:

  • Scale-up triggers after consumed capacity breaches the target for two consecutive minutes (plus up to a few minutes of CloudWatch alarm delay).
  • Scale-down waits for 15 consecutive one-minute data points below the target.
  • After either trigger, the UpdateTable call takes several minutes to apply — and requests above the old ceiling are throttled while it does.
DynamoDBApplication AutoScalingCloudWatchTrafficDynamoDBApplication AutoScalingCloudWatchTrafficrequests above the old ceiling throttle until the update landsconsumed > target, minute 1consumed > target, minute 2alarm firesUpdateTable (several minutes)

That ~5-minute floor from breach to new capacity is the honest limit of the feature: auto scaling absorbs traffic that grows, not traffic that steps. A flash sale that triples load in one minute will throttle on provisioned capacity regardless of your policy; that shape wants On-Demand, which instantly accommodates up to double your previous peak (and throttles beyond double within 30 minutes — its own version of the same physics).

Console setup

For an existing table (AWS steps):

  1. DynamoDB console → Tables → choose the table.
  2. Additional settings tab → Read/write capacityEdit.
  3. Capacity mode: Provisioned.
  4. Under Table capacity, switch Auto scaling to On for read, write, or both, then set Minimum capacity units, Maximum capacity units, and Target utilization for each.
  5. Optionally apply the same settings to every GSI, and Save.

One console limitation worth knowing: cooldowns aren't exposed there. AWS's own docs point you at the CLI "for more advanced features like setting scale-in and scale-out cooldown times".

CLI setup

Two calls per dimension: register the scalable target (the min/max bounds), then attach the target-tracking policy. Verbatim from AWS's CLI walkthrough, for write capacity on a table:

aws application-autoscaling register-scalable-target \
    --service-namespace dynamodb \
    --resource-id "table/TestTable" \
    --scalable-dimension "dynamodb:table:WriteCapacityUnits" \
    --min-capacity 5 \
    --max-capacity 10

The policy configuration lives in a JSON file:

{
  "PredefinedMetricSpecification": {
    "PredefinedMetricType": "DynamoDBWriteCapacityUtilization"
  },
  "ScaleOutCooldown": 60,
  "ScaleInCooldown": 60,
  "TargetValue": 50.0
}
aws application-autoscaling put-scaling-policy \
    --service-namespace dynamodb \
    --resource-id "table/TestTable" \
    --scalable-dimension "dynamodb:table:WriteCapacityUnits" \
    --policy-name "MyScalingPolicy" \
    --policy-type "TargetTrackingScaling" \
    --target-tracking-scaling-policy-configuration file://scaling-policy.json

For reads, swap the dimension to dynamodb:table:ReadCapacityUnits and the metric to DynamoDBReadCapacityUtilization. For a GSI, the resource id becomes table/TestTable/index/test-index with the dynamodb:index:* dimensions. A table with three GSIs scaling both dimensions therefore needs eight target/policy pairs — script it.

The two cooldowns default to 0 for DynamoDB and are the CLI-only knobs: ScaleOutCooldown is the minimum seconds between capacity increases (a larger scale-out still goes through immediately), and ScaleInCooldown blocks the next decrease — though a scale-out interrupts a scale-in cooldown rather than waiting for it.

Picking the numbers

Target utilization is a headroom-versus-cost dial. At a target of T percent, you're paying for roughly 100/T times your consumed capacity: a 70% target buys ~1.4× headroom above steady traffic, a 50% target buys 2×. Lower targets ride out sharper growth without throttling; higher targets waste less reservation. The range is 20–90%.

The dial connects straight to the bill. From the current us-east-1 rates (same derivation as Can DynamoDB auto scale?): provisioned capacity at 100% utilization is ~3.46× cheaper per request than on-demand, and the break-even sits at ~29% utilization. Auto scaling's job is to hold real utilization near your target, so the target is effectively choosing your discount: held at 70%, provisioned runs ~2.4× cheaper than on-demand; at 50%, ~1.7×; below ~29%, you should be on on-demand instead. Check your own workload's numbers in the pricing calculator.

Minimum capacity is your spike floor: it's the capacity that is already there during the ~5 minutes auto scaling needs to react. Set it from the sharpest burst you must absorb without throttling, not from average traffic.

Maximum capacity is runaway protection — the cap on what a bug, a hot Lambda loop, or a load test can bill you. Set it above your realistic peak and treat hitting it as an alert, not normal operation.

Scale-down is quota-limited. Provisioned decreases come from a token bucket: you start each UTC day with 4 available, one more accrues per hour (max 4 held), for at most 27 decreases per table per day — GSI limits are separate, but a single request that decreases both table and index is rejected whole if either side lacks quota. Auto scaling's conservative 15-minute scale-down already respects this in practice, but it's why capacity ratchets down slowly after a spike — and why oscillating traffic ends the day pinned higher than its average.

Do it in DynoTable

Sizing the minimum and validating the target both start from real numbers, not guesses: how large the items are, how many there are, and what a representative read or write actually consumes. DynoTable's table view surfaces the live item count and table size, and its query cost preview shows the RCU estimate of a statement before it runs — the same numbers a capacity plan is made of. To size a single item, the free item size calculator computes its RCU/WCU footprint.

Pitfalls and next steps

  • Auto scaling doesn't beat per-partition physics. A hot key throttles even with capacity to spare — see hot partitions and adaptive capacity.
  • Don't forget the GSIs. Each index scales (or throttles) on its own.
  • Reserved capacity stacks only on provisioned. If a workload is steady enough that auto scaling barely moves, reserved capacity (Standard table class, provisioned mode only) is the next discount — on-demand tables can't use it.
  • Watch the first day. ConsumedReadCapacityUnits / ConsumedWriteCapacityUnits against the provisioned line in CloudWatch tells you quickly whether the target is holding or oscillating.

Capacity is one axis of the cost model; what your queries consume is the other — Scan vs Query and the SQL-scan cost model cover that half.

Download DynoTable to read your table's real size, item count and per-query cost before you commit capacity numbers to it.

Updated