This check flags DynamoDB tables without point-in-time recovery (PITR), which leaves you with no way to roll back accidental deletes, bad writes, or ransomware-style data corruption. Enable PITR with one command: aws dynamodb update-continuous-backups.
DynamoDB is built for durability. Your data is replicated across multiple Availability Zones, and AWS handles the underlying storage so you rarely think about disk failure. But durability is not the same as recoverability. If an application bug overwrites a few thousand items, or an engineer runs a delete against the wrong table, that replicated data is replicated right out of existence. Point-in-time recovery is the feature that turns "we lost the data" into "we restored to 3 minutes before the incident."
The dynamodb_nopitr check looks for tables that have continuous backups disabled, meaning there is no second-by-second recovery window to fall back on.
What this check detects
Lensix flags any DynamoDB table where point-in-time recovery (PITR) is not enabled. PITR is a continuous backup feature that lets you restore a table to any second within the last 35 days.
Each table carries a ContinuousBackupsDescription with a PointInTimeRecoveryStatus that is either ENABLED or DISABLED. By default, PITR is off on newly created tables, so unless you explicitly turned it on or used IaC that sets it, the check will likely fire.
Note: PITR is different from on-demand backups. On-demand backups are manual snapshots you trigger yourself. PITR maintains a rolling 35-day window automatically, capturing changes continuously so you can restore to a precise timestamp without remembering to take a snapshot.
Why it matters
DynamoDB tables often hold exactly the data you cannot afford to lose: user accounts, session state, order records, payment metadata, IoT telemetry, feature flags. Without PITR, a single bad operation can be unrecoverable. Here are the failure modes that actually happen in production.
Accidental deletion or table drop
An engineer running a cleanup script against staging points at the wrong account and deletes a production table, or a Terraform destroy wipes a table that was not protected by prevent_destroy. Without PITR, the data is gone. With PITR, you can restore the table from the recovery window.
Bad application writes
A deploy ships a bug that overwrites the status field on every order with an empty string, or a migration script mangles records across millions of items. Durability does not help here. The corrupted data is faithfully replicated. PITR lets you restore to the minute before the deploy went out.
Ransomware and malicious actors
An attacker who gains write access to your table can delete or encrypt records to extort you. PITR gives you a clean recovery point that an attacker cannot easily tamper with, because the restore happens to a brand new table rather than overwriting the live one.
Warning: PITR does not retroactively cover the period before you enabled it. If you turn it on today, your earliest recovery point is now, not 35 days ago. Enable it before you need it, not after the incident.
Compliance and audit
Frameworks like SOC 2, PCI DSS, and HIPAA expect demonstrable backup and recovery controls for systems holding sensitive data. A DynamoDB table backing a regulated workload with no recovery capability is a finding waiting to happen during an audit.
How to fix it
Enabling PITR is a single API call, takes effect immediately, and requires no downtime or table reconfiguration.
AWS CLI
Enable PITR on a specific table:
aws dynamodb update-continuous-backups \
--table-name Orders \
--point-in-time-recovery-specification PointInTimeRecoveryEnabled=true
Verify it is on:
aws dynamodb describe-continuous-backups \
--table-name Orders \
--query 'ContinuousBackupsDescription.PointInTimeRecoveryDescription.PointInTimeRecoveryStatus'
You should see "ENABLED" returned.
Find and fix every table at once
If the check fired across many tables, loop over them. This script enables PITR on every table in the current region that does not already have it.
for table in $(aws dynamodb list-tables --query 'TableNames[]' --output text); do
status=$(aws dynamodb describe-continuous-backups \
--table-name "$table" \
--query 'ContinuousBackupsDescription.PointInTimeRecoveryDescription.PointInTimeRecoveryStatus' \
--output text)
if [ "$status" != "ENABLED" ]; then
echo "Enabling PITR on $table"
aws dynamodb update-continuous-backups \
--table-name "$table" \
--point-in-time-recovery-specification PointInTimeRecoveryEnabled=true
fi
done
Tip: Run this per region. DynamoDB is a regional service, so a table named Orders in us-east-1 is separate from one in eu-west-1. Wrap the loop in another loop over aws ec2 describe-regions if you manage many regions.
AWS Console
- Open the DynamoDB console and select your table.
- Go to the Backups tab.
- Under Point-in-time recovery (PITR), choose Edit.
- Toggle Turn on point-in-time recovery and save.
Terraform
Add the point_in_time_recovery block to your table resource:
resource "aws_dynamodb_table" "orders" {
name = "Orders"
billing_mode = "PAY_PER_REQUEST"
hash_key = "order_id"
attribute {
name = "order_id"
type = "S"
}
point_in_time_recovery {
enabled = true
}
}
CloudFormation
Resources:
OrdersTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: Orders
BillingMode: PAY_PER_REQUEST
AttributeDefinitions:
- AttributeName: order_id
AttributeType: S
KeySchema:
- AttributeName: order_id
KeyType: HASH
PointInTimeRecoverySpecification:
PointInTimeRecoveryEnabled: true
Warning: PITR is not free. AWS charges based on the size of your table data, billed per GB-month for the continuous backup. For large tables this adds up, so factor it into cost planning. The price of a single unrecoverable incident almost always dwarfs it, but you should know the number before flipping it on across thousands of tables.
How to restore when you actually need it
Knowing PITR is enabled is only half the story. Practice the restore so you are not learning the workflow during an outage. A restore creates a new table from a point in time; it never overwrites the source.
aws dynamodb restore-table-to-point-in-time \
--source-table-name Orders \
--target-table-name Orders-restored \
--restore-date-time 2024-06-01T14:25:00Z
Once Orders-restored is healthy and validated, you cut traffic over to it or copy the corrected items back into the live table. You can also use --use-latest-restorable-time instead of a specific timestamp to restore to the most recent recoverable point.
Note: A restored table does not automatically carry over auto scaling settings, IAM policies, tags, TTL configuration, or stream settings. Reapply those after the restore, or bake them into your IaC and import the restored table.
How to prevent it from happening again
Turning PITR on once is not enough. New tables get created by new services, by new teams, and by quick experiments that quietly become production. Bake the control in so the default is always "on."
Enforce it in your IaC modules
If everyone provisions DynamoDB through a shared Terraform module, set PITR on by default inside the module so individual callers cannot forget it.
variable "enable_pitr" {
type = bool
default = true
}
resource "aws_dynamodb_table" "this" {
# ...
point_in_time_recovery {
enabled = var.enable_pitr
}
}
Gate it in CI with policy-as-code
Use OPA/Conftest or Checkov to block any plan that creates a table without PITR. A Conftest Rego policy looks like this:
package main
deny[msg] {
resource := input.resource_changes[_]
resource.type == "aws_dynamodb_table"
pitr := resource.change.after.point_in_time_recovery
not pitr[_].enabled
msg := sprintf("DynamoDB table '%s' must have point-in-time recovery enabled", [resource.address])
}
Checkov ships a built-in rule for this, so you can get coverage with no custom policy at all:
checkov -d . --check CKV_AWS_28
Tip: Pair the CI gate with continuous monitoring. CI only catches resources created through your pipeline. Lensix scans the live account, so it catches tables created by hand, by a forgotten Lambda, or by a third-party tool that never touched your repo.
Detect and auto-remediate with AWS Config
The managed AWS Config rule dynamodb-pitr-enabled evaluates every table and marks non-compliant ones. You can attach an automatic remediation action that calls update-continuous-backups whenever a non-compliant table appears.
Best practices
- Enable PITR on every production table, no exceptions. Treat it as the baseline, the same way you treat encryption at rest.
- Combine PITR with on-demand backups for long retention. PITR covers the last 35 days. If you need monthly or yearly recovery points for compliance, schedule on-demand backups or use AWS Backup, which supports DynamoDB and lets you set retention policies and cross-region copies.
- Protect against accidental table deletion. Set
deletion_protection_enabled = truein Terraform (or the equivalentDeletionProtectionEnabledin CloudFormation) so the table cannot be dropped without explicitly turning protection off first. - Tighten IAM around destructive actions. Restrict
dynamodb:DeleteTableand broaddynamodb:PutItem/dynamodb:DeleteItempermissions to the principals that genuinely need them, and require MFA for the most destructive ones. - Use AWS Backup for cross-region and cross-account copies. PITR keeps recovery points within the same region and account. For disaster recovery against a region outage or a compromised account, copy backups elsewhere.
- Run a restore drill at least once. A backup you have never restored is a hypothesis, not a recovery plan. Restore to a throwaway table, validate the data, and document the runbook.
Danger: Disabling PITR deletes your entire continuous backup history immediately and irreversibly. If you ever run update-continuous-backups with PointInTimeRecoveryEnabled=false, you lose the rolling 35-day window the moment it takes effect. Never disable it on production unless you are decommissioning the table.
Point-in-time recovery is one of the cheapest insurance policies in AWS relative to the disaster it prevents. Enable it everywhere, enforce it in your pipeline, monitor for drift, and rehearse the restore. When the bad deploy or the wrong delete eventually lands, the difference between PITR and no PITR is the difference between a 20-minute recovery and an incident you write a postmortem about for years.

