DynamoDB Cost Optimization in 2026: Capacity Modes, GSIs, Standard-IA, and Reserved Capacity

The five levers that trim 40-70% off a DynamoDB bill: capacity modes, reserved capacity, Standard-IA table class, GSI audits, and TTL cleanup. With CLI and Terraform examples.

DynamoDB Cost Optimization Guide (2026)

Updated: July 27, 2026

DynamoDB cost optimization in 2026 comes down to five levers: pick the right capacity mode (Provisioned with autoscaling for predictable traffic, On-Demand for spiky), buy reserved capacity for the steady baseline, move cold tables to the Standard-IA table class, prune or resize Global Secondary Indexes, and enable TTL to delete rows you no longer need. In my consulting work across multi-account setups, applying those five together typically trims 40–70% off a DynamoDB bill without touching application code beyond capacity-mode flips and index cleanup.

  • Provisioned capacity with Application Auto Scaling is roughly 5–7x cheaper per request than On-Demand once utilization sits above ~18%, which is where most steady production tables land.
  • Reserved capacity commits (1-year or 3-year, all-upfront) cut Provisioned pricing another 53–76% for the baseline you know you will always use.
  • The DynamoDB Standard-IA table class is 60% cheaper on storage than Standard but 25% more expensive per request. The break-even sits around 42 GB per WCU per month.
  • Every Global Secondary Index doubles (or worse) your write cost because every base-table write replicates to matching GSIs. Auditing and dropping unused GSIs is often the single biggest quick win.
  • Continuous backups (PITR) cost $0.20 per GB-month on top of table storage. Turn them off on non-production and ephemeral tables and use on-demand backups tiered to cold storage instead.
  • DAX and DynamoDB Accelerator caching pays back only when read amplification is high (over 10x). For low-cardinality hot keys, a small ElastiCache Serverless cluster is usually cheaper.

On-Demand vs Provisioned: which capacity mode is cheaper?

Provisioned capacity is cheaper than On-Demand for any DynamoDB table where sustained utilization exceeds about 18% of peak. On-Demand pricing in us-east-1 as of 2026 is $1.25 per million write request units and $0.25 per million read request units. Provisioned is $0.00013 per WCU-hour and $0.000025 per RCU-hour. Divide the latter by the former and you get the crossover: at roughly 18% average utilization of a WCU, you've paid the equivalent of an On-Demand write. Above that, Provisioned wins. Below it, On-Demand wins.

Honestly, the trap I see most often is teams choosing On-Demand for "unpredictable" workloads that are actually diurnally predictable. A SaaS app with a 9-to-5 traffic pattern isn't unpredictable, it's patterned, and Application Auto Scaling on Provisioned mode handles it fine. On-Demand is genuinely correct for three cases: brand-new tables where you have no baseline yet, workloads with more than 20x peak-to-trough ratios (event-driven, campaign spikes), and tables receiving fewer than ~50 requests per second where the Provisioned minimum feels wasteful.

The good news in 2026: switching modes takes a single API call and can be done once every 24 hours per table. You don't need to migrate data. My default recommendation for existing production tables is to start on Provisioned + autoscaling, then flip to On-Demand only if you observe sustained sub-15% utilization after tuning.

# Switch a table from On-Demand to Provisioned with initial capacity
aws dynamodb update-table \
  --table-name orders \
  --billing-mode PROVISIONED \
  --provisioned-throughput ReadCapacityUnits=200,WriteCapacityUnits=100

# Attach Application Auto Scaling immediately after
aws application-autoscaling register-scalable-target \
  --service-namespace dynamodb \
  --resource-id "table/orders" \
  --scalable-dimension dynamodb:table:WriteCapacityUnits \
  --min-capacity 20 \
  --max-capacity 4000

Application Auto Scaling settings that actually save money

The default DynamoDB autoscaling target of 70% utilization is too conservative for most production workloads and costs you 30–40% more than necessary. In my experience running dozens of high-traffic tables, a target of 80–85% is safe if you also raise the minimum capacity floor above zero and set a MaxCapacity ceiling that keeps runaway scripts from bankrupting you.

Two mistakes I see repeatedly. First, teams leave the default cool-down periods (60 seconds scale-out, 60 seconds scale-in), which causes oscillation. Setting scale-in cool-down to 300 seconds prevents the "sawtooth" pattern where autoscaling drops capacity right before the next spike. Second, they scale reads and writes at the same target when read patterns are much smoother. Reads can safely run at 85% utilization, while writes need 75% headroom for burst tolerance.

Here's a Terraform block I copy into every new table module:

resource "aws_appautoscaling_target" "write_target" {
  service_namespace  = "dynamodb"
  resource_id        = "table/${aws_dynamodb_table.orders.name}"
  scalable_dimension = "dynamodb:table:WriteCapacityUnits"
  min_capacity       = 20
  max_capacity       = 4000
}

resource "aws_appautoscaling_policy" "write_policy" {
  name               = "${aws_dynamodb_table.orders.name}-write-scaling"
  policy_type        = "TargetTrackingScaling"
  service_namespace  = aws_appautoscaling_target.write_target.service_namespace
  resource_id        = aws_appautoscaling_target.write_target.resource_id
  scalable_dimension = aws_appautoscaling_target.write_target.scalable_dimension

  target_tracking_scaling_policy_configuration {
    target_value       = 75.0
    scale_in_cooldown  = 300
    scale_out_cooldown = 60

    predefined_metric_specification {
      predefined_metric_type = "DynamoDBWriteCapacityUtilization"
    }
  }
}

Read the current guidance in the DynamoDB Auto Scaling documentation before pushing to prod. AWS has adjusted the recommended defaults twice since 2024.

Reserved capacity: the commit worth making

DynamoDB reserved capacity is the most under-purchased discount in AWS. A 1-year all-upfront commit gives you 53% off Provisioned pricing, and 3-year all-upfront hits 76%. Unlike EC2 Reserved Instances, DynamoDB reservations are simple. They apply per-region to the total provisioned WCU/RCU across all your tables in that region, so you don't need to worry about instance-family matches or size flexibility.

The catch, and the reason nobody buys them: DynamoDB reservations are sold in 100-WCU and 100-RCU blocks with a minimum of 100 each. On a busy account that's nothing. On a small account with three tables at 20 WCU each, it feels like over-committing. The right way to size a reservation is to look at the 30th percentile of your last 90 days of provisioned capacity across the region. That's your always-on baseline. Reserve to that number and let autoscaling handle everything above it at on-demand-rate Provisioned pricing.

My rule of thumb: any table with more than $500/month in provisioned throughput charges deserves a 1-year reservation on its baseline. Three-year commits are only worth it if you're confident about the traffic pattern staying flat, and most B2B SaaS grows too fast to lock in for that long.

When to use the DynamoDB Standard-IA table class

The Standard-Infrequent Access table class, introduced in 2021 and heavily marketed since 2024, is a niche but powerful cost lever. It cuts storage from $0.25/GB-month to $0.10/GB-month (60% cheaper) but raises request costs by 25%. The break-even math: your table needs at least 42 GB of storage per WCU of throughput before Standard-IA is cheaper than Standard.

What that looks like in practice. A table storing 5 TB of historical order records with only 50 WCU of steady write throughput has 100 GB per WCU, a clear Standard-IA candidate. A table with 100 GB of data pushing 200 WCU is 0.5 GB per WCU. Leave it on Standard. I've seen teams reflexively convert every table to Standard-IA and watch their bill go up because request charges dominated their spend.

DimensionDynamoDB StandardDynamoDB Standard-IA
Storage (us-east-1, per GB-month)$0.25$0.10
Write request unit (per million)$1.25$1.5625
Read request unit (per million)$0.25$0.3125
Provisioned WCU-hour$0.00013$0.0001625
Best forHot transactional dataCold archives, audit logs, historical records
Global Tables supportYesYes (since 2023)
PITR supportYesYes
Break-even threshold< 42 GB per WCU> 42 GB per WCU

Table class conversion is done in-place via UpdateTable, takes minutes to hours depending on size, and doesn't affect availability. If you're doing large-scale historical retention work, the pattern I recommend is to route new writes to a Standard-class "hot" table and use DynamoDB Streams + Lambda to move rows older than 30 days into a Standard-IA "cold" table.

How do I reduce DynamoDB Global Secondary Index costs?

Global Secondary Indexes are the silent budget killer of most DynamoDB workloads. Every GSI has its own provisioned or on-demand capacity, and every write to the base table that touches an indexed attribute costs an additional WCU on each affected GSI. A table with three GSIs where every write touches all indexed attributes pays for 4x write capacity: one for the base plus one per GSI.

So, here's the three-step audit I run on every client engagement:

  1. List every GSI across every table in the account with aws dynamodb list-tables piped into describe-table. Dump this into a spreadsheet with columns for GSI name, projected attributes, WCU, and RCU.
  2. Check query patterns. Enable CloudWatch Contributor Insights on each GSI (it costs $0.30 per million requests analyzed, cheap for a one-week audit). Any GSI receiving less than one query per second on average is a deletion candidate.
  3. Trim projected attributes. A GSI with ProjectionType=ALL stores a full copy of every item. Switch to KEYS_ONLY where possible and add INCLUDE attributes only for what your queries genuinely return.

In my most recent engagement, deleting four abandoned GSIs on a single high-traffic orders table cut the client's monthly DynamoDB bill by $8,400. Nobody had touched those GSIs since 2022 and nobody knew what they were for. This is a very common story. For related index-heavy database work, our managed PostgreSQL cost comparison covers similar principles on RDS and Aurora.

TTL, item size, and the storage nobody watches

DynamoDB storage at $0.25 per GB-month sounds cheap until you look at a 2 TB session-store table and realize you're spending $6,000 a year to keep session tokens that expired in 2022. TTL (Time To Live) is a free feature that automatically deletes items whose TTL attribute is a past epoch timestamp. It costs nothing to enable, uses no provisioned capacity, and typical cleanup lag is under 48 hours.

Turn it on for anything ephemeral: sessions, verification codes, cached lookups, event snapshots, temporary reservation records, feature-flag overrides. The API call is one line:

aws dynamodb update-time-to-live \
  --table-name user_sessions \
  --time-to-live-specification "Enabled=true, AttributeName=expires_at"

# When writing items, set expires_at to a Unix epoch integer
# e.g. now + 86400 for 24-hour expiry

The second storage lever is item size. DynamoDB bills reads in 4 KB blocks and writes in 1 KB blocks. An item at 1.1 KB costs the same as one at 2.0 KB for a write, but doubles the cost of the write compared to 1.0 KB. Compressing large text or JSON attributes with gzip before storing, or splitting large items into a primary record plus overflow items retrieved on demand, can save meaningful money on write-heavy tables. Amazon's own NoSQL design best practices guide covers item-sizing patterns worth reading before you start restructuring tables.

DAX vs ElastiCache: caching DynamoDB reads

DynamoDB Accelerator (DAX) is a write-through cache that sits in front of DynamoDB and returns cached reads in single-digit milliseconds. It bills by node-hour (dax.r5.large is roughly $0.24/hour or $175/month per node, and you need at least a 3-node cluster in prod, so $525/month minimum). It pays back when you have hot keys read far more often than they are written. Typical break-even is around 500 RCUs of eliminated read traffic per DAX node, roughly 400,000 reads per second at peak.

For lower-traffic hot-key workloads, ElastiCache Serverless for Redis (introduced 2023) is often cheaper because you pay only for ECPU and data stored, with no minimum cluster. The tradeoff is you write your own cache-invalidation logic instead of getting write-through behavior for free.

My decision heuristic: if your DynamoDB read spend is under $2,000/month, don't bother with either cache. The operational overhead isn't worth it. Between $2,000 and $10,000, use ElastiCache Serverless. Above $10,000/month with clearly hot keys, DAX starts to be defensible.

Backups, PITR, and Streams line items

Point-in-time recovery is a paid feature at $0.20 per GB-month on top of your table storage. That's a real number on large tables: a 5 TB table costs $1,000/month just for PITR. It's worth every cent on production tables but it's turned on by default for a lot of non-prod tables that will never need to be restored to a random point in time. Turn it off for dev, staging, and any table where a nightly on-demand backup is sufficient.

On-demand backups are $0.10 per GB-month when stored in the standard tier and drop to $0.03 per GB-month in the DynamoDB backup cold storage tier (introduced 2024, retention minimum 90 days). A common pattern is to script a nightly on-demand backup that ages to cold storage after 30 days, then delete it after your compliance window.

DynamoDB Streams charges $0.02 per 100,000 stream read requests. That sounds trivial and usually is, but Lambda triggers reading a hot stream at very short poll intervals can rack up surprisingly. If you're using Streams to fan out to multiple consumers, evaluate switching to Kinesis Data Streams for DynamoDB. The per-shard-hour billing model is cheaper above roughly 5 consumers.

For patterns on catching this class of surprise line-item early, see our companion piece on cloud cost anomaly detection. The official DynamoDB pricing page is worth bookmarking. AWS updates the region-specific rates two or three times a year and the numbers in this article are us-east-1 as of July 2026.

Monitoring and tagging for cost allocation

Everything above only works if you can attribute costs back to tables, teams, and services. Enable AWS-generated cost allocation tags in the Billing console (they don't apply retroactively, so flip them on before you need them). At minimum, tag every DynamoDB table with Owner, Environment, and CostCenter. My preferred additional tag is DataClass because it lets you filter the bill by hot/warm/cold classification when it's time to argue for a Standard-IA migration.

The CloudWatch metrics that matter for cost tuning are ConsumedWriteCapacityUnits, ConsumedReadCapacityUnits, ProvisionedWriteCapacityUnits, ThrottledRequests, and SuccessfulRequestLatency. Compute the ratio of consumed to provisioned at 5-minute granularity over 30 days and you have a utilization histogram that tells you exactly where you're over-provisioned. I run this as a monthly report in every account I manage, the same discipline covered in our AWS Compute Optimizer guide for EC2 rightsizing.

Finally, if you operate multiple AWS accounts through Organizations, don't skip AWS Cost and Usage Reports (CUR) piped into Athena or the new FOCUS 1.2 export. Table-level DynamoDB spend rollups need queryable data, not the Cost Explorer UI (which caps out at daily granularity).

Frequently Asked Questions

How much does DynamoDB cost per month for a typical production workload?

A production DynamoDB table serving roughly 500 requests per second with 100 GB of storage and one GSI typically costs $180–$400 per month in us-east-1, depending on read/write ratio and capacity mode. Enabling PITR adds ~$20/month per 100 GB. Costs scale near-linearly with request volume, so 5,000 RPS lands closer to $1,800–$4,000 per month.

Is DynamoDB cheaper than RDS?

DynamoDB is cheaper than RDS for very high-throughput, simple key-value or single-table access patterns because you don't pay for idle instance-hours. For low-throughput or complex relational workloads, a small RDS instance (t4g.small, ~$15/month) beats DynamoDB's baseline overhead. The break-even is roughly 100 requests per second, and above that DynamoDB usually wins.

What is the difference between DynamoDB On-Demand and Provisioned capacity?

On-Demand bills per request with no capacity planning required and scales instantly to any traffic level. Provisioned charges per hour for a fixed capacity you set (or that Application Auto Scaling manages). Provisioned is 5–7x cheaper per request at sustained utilization above 18% but throws throttling errors if traffic exceeds your capacity ceiling.

Does DynamoDB have a free tier in 2026?

Yes. The perpetual free tier includes 25 GB of storage and 25 provisioned WCUs and RCUs (enough for ~2 million writes and ~10 million strongly-consistent reads per month) at zero cost. On-Demand tables get 2.5 million read request units and 1 million write request units per month free for the first 12 months of an account's life.

When should I use DynamoDB Standard-IA instead of Standard?

Switch to Standard-IA when your table's storage-to-throughput ratio exceeds 42 GB per WCU, typically historical logs, archived orders, audit records, and cold user data. Standard-IA cuts storage 60% but raises request costs 25%, so tables with heavy read/write traffic and small storage stay on Standard.

Sara Al-Mahmoud
About the Author Sara Al-Mahmoud

Cloud cost architect specialising in the gnarly multi-account, multi-region setups. Spreadsheet enthusiast.