First: stop the deleter
Disable the job, lifecycle rule or user causing the deletes. Check for a lifecycle rule that expires current versions or noncurrent versions — it may keep deleting while you recover.
aws s3api get-bucket-lifecycle-configuration --bucket prod-invoices
1. Is versioning on?
aws s3api get-bucket-versioning --bucket prod-invoices
| Status | Recovery path |
|---|---|
Enabled | Delete markers and previous versions — Steps 2–3 |
Suspended | Versions created before suspension still exist |
| Empty (never enabled) | Only replicas or backups — Step 4 |
2. Deleted objects: remove delete markers
In a versioned bucket, a delete adds a delete marker. Removing the marker brings the object back.
aws s3api list-object-versions --bucket prod-invoices --prefix 2026/10/ \
--query "DeleteMarkers[?IsLatest==\`true\`].[Key,VersionId]" --output text > markers.txt
wc -l markers.txt
while read -r key ver; do
aws s3api delete-object --bucket prod-invoices --key "$key" --version-id "$ver"
done < markers.txt
For millions of objects, generate a manifest (S3 Inventory includes versions and delete markers) and use S3 Batch Operations or a parallel script; a shell loop is too slow.
3. Overwritten objects: restore a previous version
aws s3api list-object-versions --bucket prod-invoices --prefix 2026/10/inv-1001.pdf \
--query "Versions[].{v:VersionId, latest:IsLatest, modified:LastModified, size:Size}" --output table
aws s3api copy-object --bucket prod-invoices --key 2026/10/inv-1001.pdf \
--copy-source "prod-invoices/2026/10/inv-1001.pdf?versionId=<good-version-id>"
Copying the old version on top makes it current again and keeps the bad version in history.
Caution
Don't delete the bad versions until you're sure. Deleting a specific version ID is permanent.
4. No versioning: other sources
- Replication target bucket (CRR/SRR): copy back with
aws s3 sync s3://dr-bucket/prefix s3://prod-invoices/prefix. Deletes are not replicated by default, so the replica often still has the data. - AWS Backup for S3: start a restore job to the original or a new bucket.
- Upstream systems: re-run the producer for the affected period.
5. Who did it
CloudTrail data events for S3 must be enabled to see DeleteObject. Server access logs are an alternative.
SELECT eventtime, useridentity.arn, sourceipaddress, count(*) AS deletes
FROM cloudtrail_logs
WHERE eventsource = 's3.amazonaws.com' AND eventname IN ('DeleteObject', 'DeleteObjects')
AND requestparameters LIKE '%prod-invoices%'
GROUP BY 1, 2, 3 ORDER BY 1 DESC;
Prevent it next time
- Versioning on every production bucket, with a noncurrent-version expiration rule to control cost.
- Object Lock (governance or compliance mode) for data that must not be deleted.
- Replication to another account with a different admin team.
- Least-privilege policies: no
s3:DeleteObjectfor apps that only write.