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.

It only applies to provisioned capacity. If you haven't picked a capacity mode yet, start with On-Demand vs Provisioned.

How do you enable auto scaling on a DynamoDB table?

In the console: open your table, go to Additional settings → Read/write capacity → Edit, choose Provisioned, and set Auto scaling to On for read capacity, write capacity, or both.

Give 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. If you create a provisioned table in the console, auto scaling is on by default.

Auto scaling is two CloudWatch alarms and an UpdateTable

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.
  • A GSI you add to an existing table doesn't scale while it builds. AWS says you manage its capacity by hand until the backfill finishes. Set ProvisionedThroughput on the index's Create action in UpdateTable high enough for the backfill plus live writes. Once the index is ACTIVE, check it has a scaling policy, and register one for table/<table>/index/<index> if not.

The timing you're signing up for

Auto scaling is reactive, and 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 capacity are throttled while it does.
DynamoDBApplicationAuto ScalingCloudWatchTrafficDynamoDBApplicationAuto ScalingCloudWatchTrafficrequests above the old ceiling throttle until the update landsconsumed > target, minute1consumed > target, minute2alarm firesUpdateTable (severalminutes)

So new capacity arrives two minutes after the breach at the earliest, plus several more minutes that AWS doesn't pin to a number. Auto scaling follows gradual growth; a sudden step outruns it.

Short spikes are usually absorbed by the table's best-effort burst capacity (up to 300 s of unused capacity), but don't plan on it. A flash sale that triples load and holds it will throttle once that burst runs out, unless your minimum capacity already covers the step.

For a step you can see coming, raise the floor before it arrives with scheduled scaling, and keep target tracking for the rest of the day. Set both bounds, or the new minimum can exceed the target's registered maximum (10 in the CLI setup below) and the action fails:

aws application-autoscaling put-scheduled-action \
    --service-namespace dynamodb \
    --scheduled-action-name pre-sale \
    --resource-id "table/TestTable" \
    --scalable-dimension "dynamodb:table:WriteCapacityUnits" \
    --schedule "at(2026-11-27T07:00:00)" \
    --scalable-target-action MinCapacity=3000,MaxCapacity=6000

A second scheduled action after the event restores both values (MinCapacity=5,MaxCapacity=10 here). If the table has never served that rate, also pre-warm it by raising its warm throughput (the rate a table can serve instantly), which works in both capacity modes.

Switching to on-demand doesn't remove the problem: on-demand can throttle when traffic passes double its previous peak within 30 minutes. The on-demand throughput measurements show that limit and the --warm-throughput command.

The console sets bounds and target, not cooldowns

For an existing table (AWS steps):

  1. DynamoDB console → Tables → choose the table.
  2. Additional settings tab → Read/write capacity → Edit.
  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.

The console doesn't expose cooldowns. AWS points you to the CLI "for more advanced features like setting scale-in and scale-out cooldown times".

The CLI adds cooldowns, which default to 0

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 needs eight target/policy pairs, so script it.

The two cooldowns default to 0 for DynamoDB and are CLI-only settings. ScaleOutCooldown is the minimum seconds between capacity increases (a larger scale-out still goes through immediately).

ScaleInCooldown blocks the next decrease, though a scale-out interrupts a scale-in cooldown rather than waiting for it.

Picking the numbers

Target utilization trades headroom against cost. At a target of T percent, you pay 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; higher targets waste less capacity.

Your target also sets your discount over on-demand. At us-east-1 list price, provisioned capacity held at 70% utilization runs ~2.4× cheaper per request, at 50% ~1.7×, and below ~29% on-demand is cheaper.

The derivation is in On-Demand vs Provisioned, and the pricing calculator prices your own workload.

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

Maximum capacity caps what a bug 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.

So capacity ratchets down slowly after a spike, and traffic that oscillates several times a day ends the day provisioned above its average. GSI quotas are separate, but one request that decreases both table and index is rejected whole if either side lacks quota.

If your traffic has that shape, move the table to on-demand, which has no decrease quota. If you stay provisioned, schedule the known daily peaks so auto scaling isn't spending decreases on reactive scale-ins.

Do it in DynoTable

Sizing the minimum starts from how large the items are and how many there are. In DynoTable, a table toolbar's Settings button opens the Table settings dialog.

Its Overview shows DynamoDB's item-count and size estimates (refreshed about every six hours), and its Settings section changes the capacity mode and RCU/WCU.

To size a single item, the free item size calculator computes its RCU/WCU footprint. For a query, the Workbench's cost preview shows Scan versus Query and, when table metadata allows, a pre-run RCU estimate.

Hot keys and unscaled GSIs still throttle

  • Auto scaling doesn't lift per-partition limits. A hot key throttles even with table capacity to spare. Find it with CloudWatch Contributor Insights and spread it; 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 (the Sum per minute ÷ 60) against the provisioned line in CloudWatch tells you quickly whether the target is holding or oscillating. The Average statistic is per request, so it won't line up.

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 size and item-count estimates, and see whether a query will Scan, with a pre-run RCU hint where table metadata allows.

By Dmitriy · Updated