DynamoDB TTL, point-in-time recovery and global tables
How DynamoDB TTL deletes expired items (epoch seconds, days of delay, expired items still readable, service deletions in streams, replicated deletes billed), what point-in-time recovery restores and what it leaves for you to re-create on the new table, and how global tables behave in eventual (MREC) and strong (MRSC) consistency modes — conflicts, transactions, synchronized settings and limits.
markdownreferencev4M6B0-D7N1stage: referencetrust: officialservice: dynamodbinterface: anyDynamoDB TTLtime to live epoch secondsexpired items still returnedTTL delayTTL stream service deletionPITRpoint-in-time recoveryRecoveryPeriodInDaysRestoreTableToPointInTimerestored table missing settingsglobal tablesMRECMRSCmulti-Region strong consistencylast writer winsReplicatedWriteConflictExceptionReplicationLatencyglobal table transactionswitness Regionsettings synchronized replicas
**TTL is a cleanup job, not a deadline. PITR restores into a new table that lacks many of the original's settings. Global tables replicate asynchronously by default, with last writer wins.** All three surprise code that assumes otherwise.
Time to live (TTL)
- The attribute must be a Number holding Unix epoch seconds (not milliseconds). Items whose attribute isn't a Number are ignored. Enable TTL on the table and name the attribute (SDK
TimeToLiveSpecification: { Enabled, AttributeName }). - Items are deleted "typically within a few days" after they expire. Until then they are still returned by
GetItem,QueryandScan, and can be updated. Filter them out:
import { DynamoDBDocumentClient, QueryCommand } from "@aws-sdk/lib-dynamodb";
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
const ddb = DynamoDBDocumentClient.from(new DynamoDBClient({}));
const now = Math.floor(Date.now() / 1000);
const res = await ddb.send(new QueryCommand({
TableName: "Sessions",
KeyConditionExpression: "pk = :pk",
FilterExpression: "attribute_not_exists(expiresAt) OR expiresAt > :now",
ExpressionAttributeValues: { ":pk": "user#42", ":now": now },
}));
console.log(res.Items?.length);
- Setting the TTL to a future time, or removing it, before deletion un-expires the item. To refresh an item that may already be gone, add a condition expression such as
attribute_exists(pk). - TTL deletes consume no write capacity in the Region where the item expired. They also remove the item from GSIs and LSIs.
- In DynamoDB Streams, TTL deletes appear as service deletions, not user deletes. A
REMOVE-triggered Lambda function (patterns/dynamodb-streams-to-lambda.md) fires for them too, so tell them apart if the business logic differs. The exact identity fields aren't on the fetched page. - Global tables replicate TTL deletes, and each replica Region bills them as replicated writes, which can throttle provisioned replicas. TTL isn't available on MRSC tables.
Point-in-time recovery (PITR)
- Continuous backups with per-second restore points over a recovery period of 1–35 days (default 35; SDK
PointInTimeRecoverySpecification: { PointInTimeRecoveryEnabled, RecoveryPeriodInDays }). Pricing is by table and LSI size, so shortening the period doesn't lower it. - Disabling and re-enabling PITR resets the window: at first you can restore only to
LatestRestorableDateTime. - A restore always creates a new, independent table. The source keeps serving reads and writes, and restore time depends on size and index count, with no guarantee.
| Restored | Not restored: set again on the new table |
|---|---|
| Data, GSIs, LSIs, provisioned capacity, encryption settings | Auto scaling policies, IAM policies, CloudWatch metrics and alarms, tags, Streams settings, TTL settings, PITR settings (resource policies: not stated) |
aws dynamodb restore-table-to-point-in-time \
--source-table-name Orders --target-table-name Orders-restored \
--restore-date-time 2026-10-18T09:30:00Z \
--billing-mode-override PAY_PER_REQUEST
- Use
--use-latest-restorable-timeinstead of a timestamp to restore to the latest point. --global-secondary-index-override '[]'drops the GSIs, which makes the restore faster and cheaper.--sse-specification-overrideis required for cross-Region restores. You pay for transfer out and for the new table.- To "restore in place", point the application at the restored table, or copy data back. Re-enable Streams, TTL and PITR on it first.
- From a global table, the restored table is not a replica, but you can make a new global table from it.
If the table is managed by CloudFormation or the CDK, the restored table is outside the stack. Import it (reference/cloudformation/change-sets-drift-and-import.md) or treat it as a migration source.
Global tables
The consistency mode is chosen at creation and can't be changed. MREC is the default.
| MREC (multi-Region eventual) | MRSC (multi-Region strong) | |
|---|---|---|
| Replication | Asynchronous, typically ≤ 1 s; ReplicationLatency metric per Region pair | Synchronous to at least one other Region before the write succeeds; RPO 0 |
| Conflicts | Last writer wins per item | Concurrent writes to an item fail with ReplicatedWriteConflictException (retryable) |
| Strong reads | Current only if the item was last written in that Region | Current on every replica |
| Conditional writes | Evaluated against the local copy | Consistent |
| Transactions | Atomic only in the calling Region; other Regions can see partial results | Not supported |
| Regions | Any where DynamoDB runs; add replicas any time | Exactly 3: three replicas, or two plus a witness (no reads or writes, not in your account); table must be empty; no replicas added later |
| Not supported | — | TTL, LSIs, transactions; GSIs only with eventually consistent reads |
| Streams | Always on, can't be disabled; per-item order kept | Off by default; identical across replicas |
Settings across replicas:
- Always the same: capacity mode, write capacity and write auto scaling, key and GSI definitions, SSE type, TTL, warm throughput, on-demand maximum write throughput, and Streams on MREC.
- Overridable per replica: read capacity and read auto scaling, table class, on-demand maximum read. A later change to the synchronized value anywhere overwrites the overrides.
- Per replica, never synchronized: deletion protection, PITR, tags, Contributor Insights, Kinesis streaming, resource policies, and Streams on MRSC.
So enable PITR and deletion protection in each Region.
Design for MREC: make writes idempotent, avoid read-modify-write across Regions (or route each item's writes to a home Region), and don't rely on transactions or conditional writes for cross-Region invariants. Errors are in reference/dynamodb/errors.md.