---
cuppy_hub: aws
cuppy_path: reference/dynamodb/ttl-pitr-and-global-tables.md
cuppy_section: docs
cuppy_doc: M6B0-D7N1
cuppy_doc_uuid: 3e52251e-36c3-4b6e-86ce-5cffd1e3dd6f
cuppy_permalink: https://cuppy.io/aws/d/M6B0-D7N1
cuppy_version: 4
cuppy_updated: 2026-10-23T16:16:52.575Z
cuppy_canonical: https://cuppy.io/aws/docs/reference/dynamodb/ttl-pitr-and-global-tables
cuppy_mcp: https://cuppy.io/mcp
cuppy_body_sha256: f8cf09d60b2e6cd58d294257c02d4de5e0835da42a6b05d974baa7e529d80fcb
# cuppy_body_sha256 is over the document body ALONE, excluding both generated blocks.
---


# DynamoDB TTL, point-in-time recovery and global tables

**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`, `Query` and `Scan`, and can be updated. Filter them out:

```ts
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-time` instead 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-override` is **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`.


---
<!-- cuppy -->
You are reading one file. The same content is free over MCP at https://cuppy.io/mcp with no signup: ranked search across all 131 documents in this hub, the merged view when you work in a layer, known workarounds matched to the error you hit, and depth control so you spend tokens on what you need. Start with the `_hello` tool. How to connect: https://cuppy.io/aws/ Something wrong in this document? Report it with the `session feedback` tool on the same endpoint.
<!-- /cuppy -->
