DynamoDB TTL: Auto-Expiring Items with Time to Live

How DynamoDB TTL automatically deletes expired items — setting the TTL attribute (epoch seconds), deletion timing, costs, and combining TTL with Streams.

4 min read· Updated Jun 15, 2026
On this page

DynamoDB TTL (Time to Live) automatically deletes items after a timestamp you store on each item — no cron jobs, no scan-and-delete batch, and no write-capacity cost for the deletions. It’s the cleanest way to expire sessions, caches, and ephemeral records.

This guide covers how to enable TTL, the exact attribute format, when deletion actually happens, and how to combine it with Streams for cleanup workflows.

How TTL works

You designate one attribute per table as the TTL attribute. Its value must be a Number holding a Unix epoch timestamp in seconds. A background process continuously scans for items whose TTL value is in the past and deletes them.

PK = SESSION#abc123
expiresAt = 1750000000      ← Unix seconds; once now() passes this, item is eligible

Key properties:

  • The deletion is asynchronous and free — it does not consume write capacity units.
  • Only items where the TTL attribute exists and is a valid past epoch-seconds value are deleted.
  • Items missing the attribute, or with a non-Number value, are simply never expired.

Enabling TTL

You enable TTL once per table and name the attribute. Via the AWS CLI:

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

Enabling TTL can take up to about an hour to fully propagate. Once active, you just write the chosen attribute on items you want to expire — items without it live forever.

Computing the timestamp

Always use seconds, not milliseconds. A millisecond value (13 digits) sits ~50,000 years in the future and your items will never expire — a classic silent bug.

import time
expires_at = int(time.time()) + 24 * 60 * 60   # 24h from now, in seconds
const expiresAt = Math.floor(Date.now() / 1000) + 86400; // 24h

Because the attribute is just a Number, you set it like any other value — see data types for how the wire format encodes it as {"N": "1750000000"}.

When deletion actually happens

This is the single most misunderstood part of TTL: expiration time is not deletion time.

  • An item becomes eligible for deletion once its TTL value is in the past.
  • The background sweeper typically removes eligible items within a few days, often much sooner, but AWS gives no hard SLA.
  • Until the sweeper gets to it, the expired item is still in the table and will still appear in Query and Scan results.

If your application must never return expired data, add a filter at read time:

FilterExpression = "expiresAt > :now"

This belt-and-suspenders approach hides logically-expired items immediately while TTL handles the physical cleanup eventually. Note a FilterExpression is applied after items are read, so you still pay read capacity for filtered-out rows — see query vs scan tradeoffs.

TTL and DynamoDB Streams

TTL deletions can be captured by DynamoDB Streams, which is what makes TTL useful for more than just discarding data. A TTL delete appears in the stream as a REMOVE record, and you can distinguish it from a user-initiated delete by the userIdentity field:

{
  "eventName": "REMOVE",
  "userIdentity": {
    "type": "Service",
    "principalId": "dynamodb.amazonaws.com"
  }
}

Common patterns built on this:

  • Archival. Stream the expiring item to S3 or a cold table before it’s gone for good.
  • Cascade cleanup. When a parent expires, a Lambda deletes related child items.
  • Audit. Record that an ephemeral record reached end of life.

To capture the full item before deletion, set the stream view type to OLD_IMAGE or NEW_AND_OLD_IMAGES — the REMOVE record carries the item’s last state in OldImage.

Costs

ActionCost
TTL deletionsFree — no WCU consumed
Stream processing of TTL deletesStandard Streams / Lambda cost
Global table replication of deletesReplicated write cost in each Region
Read-time filtering of expired itemsRead capacity for scanned-then-filtered items

The deletions being free is TTL’s headline advantage over a manual delete job, which would burn one WCU per item (or more) plus the read cost of finding them.

Practical patterns

  • Sessions and tokens. Set expiresAt to issue-time plus the lifetime. Login refreshes it.
  • Rate-limit windows. Counter items that auto-vanish after the window closes.
  • Soft caches. Materialized query results that you’re happy to let evaporate.
  • Event de-duplication. Idempotency keys that only need to live as long as your retry window.

A few rules from best practices:

  • Don’t rely on TTL for precise scheduling — it’s eventual, not punctual.
  • Always store epoch seconds; consider a unit test asserting the digit count.
  • If you must guarantee data is gone by a deadline (compliance), TTL alone isn’t enough; pair it with an explicit delete or read-time filter.

Verifying TTL is working

After enabling TTL, it’s worth confirming items actually carry a sane timestamp and that the sweeper is making progress. The deletion lag means you often can’t tell at a glance whether TTL is misconfigured or just hasn’t run yet. A GUI such as Tablyne lets you sort items by the TTL attribute and spot the millisecond-vs-seconds mistake immediately — the future-dated values that would otherwise silently never expire.

TTL is one of DynamoDB’s best-value features: zero-cost cleanup that scales automatically. The two things to get right are the seconds format and the understanding that “expired” means “eligible for deletion,” not “already gone.”

Frequently asked questions

Does DynamoDB TTL delete items exactly at the expiration time?

No. TTL deletes items in the background, typically within a few days of the expiration timestamp — often much sooner, but there's no SLA. If you need items hidden the instant they expire, filter them out at query time as well.

Does DynamoDB TTL cost anything?

The TTL deletions themselves are free — they don't consume write capacity. You only pay if you process the deletes through DynamoDB Streams or if a global table replicates the tombstones.

What format does the TTL attribute need?

A Number containing a Unix epoch timestamp in seconds (not milliseconds). Items with a TTL value in the past are eligible for deletion; non-numeric, missing, or millisecond values are ignored.

Keep reading