← Blog
Layered cream and green paper sheets with an orange ribbon folding back over them on a lavender background.
Published on
2026-10-29

S3 versioning: recover overwritten and deleted objects

S3 versioning keeps earlier versions of objects in an Amazon S3 bucket, so an overwritten file or an ordinary deletion may be recoverable. Recovery depends on the required version still being stored and accessible. Start by listing the object's versions, download the candidate for inspection, then copy the chosen version to the same key to make it current again.

This guide covers Amazon S3 general purpose buckets. It does not establish feature support for another S3-compatible service. For the wider service context, see our Amazon cloud storage guide.

What changes when S3 versioning is enabled?

An object key identifies a location such as recovery-demo/config.json. With versioning enabled, uploading replacement content to that key creates another stored version. The most recent version becomes the current object version; the previous version remains available by its version ID.

A version ID is an opaque identifier, not a date or sequence number. Keep the exact value returned by Amazon S3. Use timestamps, sizes, application records, and the actual downloaded content to decide which version you need.

Versioning applies to the whole bucket. It does not selectively protect one folder, and enabling it cannot reconstruct content that was already overwritten or permanently deleted. Existing objects keep their null version ID until later changes create additional versions. AWS explains these boundaries in its versioning overview.

Enable versioning before you need it

In the S3 console, open the general purpose bucket, choose Properties, then edit Bucket Versioning and select Enable. Confirm the correct AWS account and bucket before saving this bucket-wide change. AWS recommends waiting 15 minutes after first enabling versioning before making object writes or deletes; see its activation instructions.

The following read-only command checks the current setting. Replace the example bucket name, and use your intended AWS CLI profile and region.

aws s3api get-bucket-versioning --bucket "amzn-s3-demo-recovery-bucket"

A Status value of Enabled means new writes receive version IDs. Suspended has different behavior, explained below. An absent Status indicates an unversioned bucket. Once enabled, versioning can be suspended, but the bucket cannot return to its original unversioned state.

Why a deleted object can still be recoverable

In a versioning-enabled bucket, a delete request without a version ID normally creates a delete marker. This marker becomes current, so a normal GET request returns 404 even though earlier data versions remain. The marker has a key and version ID but no object data. See AWS's delete marker behavior.

Deleting a specific data version by version ID is different: it permanently removes that version unless a protection mechanism prevents the request. Versioning alone does not make retained data immutable. Do not treat every 404 as a simple deletion either; check the exact key, bucket, and permissions before attempting recovery.

Recover a previous version in four steps

1. List versions of the exact object key

This AWS CLI command lists data versions and delete markers. A prefix can match several keys, so the query filters both result arrays to the exact example key. Replace that key in all three places when adapting the command.

aws s3api list-object-versions --bucket "amzn-s3-demo-recovery-bucket" --prefix "recovery-demo/config.json" --query "{Versions:Versions[?Key=='recovery-demo/config.json'],DeleteMarkers:DeleteMarkers[?Key=='recovery-demo/config.json']}" --output json

The CLI normally handles pagination. Do not add --no-paginate or a restrictive --max-items and assume the result contains all history. Listing versions requires s3:ListBucketVersions; details are in the command reference.

Review VersionId, LastModified, Size, and IsLatest. Select a data version from Versions, not an entry from DeleteMarkers. Listing history does not verify the contents.

2. Download and inspect the candidate

aws s3api get-object --bucket "amzn-s3-demo-recovery-bucket" --key "recovery-demo/config.json" --version-id "EXACT_VERSION_ID" recovered-config-review.json

Replace EXACT_VERSION_ID with the chosen identifier. Use a new local filename: this command can overwrite an existing local file. Inspect the downloaded content, compare it with application records, and check that it represents the state you want to recover.

Retrieving a specific version requires s3:GetObjectVersion. Encryption may require additional key permissions. Versions in archive storage may need restoration before retrieval. AWS documents these conditions for GetObject. Stop if access or restore prerequisites are unresolved.

3. Copy the reviewed version to the same key

Copying a retained version to its original key creates a new current version when versioning is enabled. It preserves the other versions, including the content you are replacing. AWS documents this previous-version recovery method.

The Python helper below uses a configured Boto3 S3 client. It checks the bucket owner and versioning state, then copies the specified source version. Calling the function changes the object that ordinary GET requests return. Pause application writes and configuration changes during the recovery window; the preliminary checks do not lock the bucket.

def restore_version(s3, bucket, key, version_id, owner_account_id):
    """Create a new current version from a reviewed, retained version."""
    state = s3.get_bucket_versioning(
        Bucket=bucket, ExpectedBucketOwner=owner_account_id
    )
    if state.get("Status") != "Enabled":
        raise ValueError("Enable versioning before using this recovery helper")
    source = s3.head_object(
        Bucket=bucket, Key=key, VersionId=version_id,
        ExpectedBucketOwner=owner_account_id
    )
    # Conservative single-copy limit; larger objects need multipart copy.
    if source["ContentLength"] > 5_000_000_000:
        raise ValueError("Use a multipart-copy recovery workflow for this object")
    result = s3.copy_object(
        Bucket=bucket, Key=key,
        CopySource={"Bucket": bucket, "Key": key, "VersionId": version_id},
        ExpectedBucketOwner=owner_account_id,
        ExpectedSourceBucketOwner=owner_account_id,
        MetadataDirective="COPY"
    )
    return result["VersionId"]

Pass your S3 client, bucket name, exact key, selected version ID, and 12-digit owner account ID. The helper is defined without being called automatically. Keep credentials in your normal AWS credential configuration rather than in the script.

CopyObject supports a single copy of up to 5 GB; this helper uses a conservative 5,000,000,000-byte limit. Larger objects need multipart copy. The dictionary form of CopySource avoids manually constructing a URL containing the key and version ID. Review destination encryption, storage class, metadata, tags, and access requirements before adapting this minimal helper. Copying content does not guarantee every source property is reproduced.

4. Verify the new current object

Record the returned version ID. Retrieve the object without specifying a version ID, compare it with the reviewed candidate, and test the application that reads it before resuming writes. Another writer can create a newer version during recovery, so a successful copy response alone does not establish the application's final state.

Should you remove the delete marker instead?

Removing the current delete marker by its exact version ID can expose an earlier version. Check the history first: another delete marker may still sit above the data version you want. Sending another ordinary DELETE request does not undo the first one; it can add another marker.

A copy of the chosen data version is often easier to review because it makes that selection explicit and retains intervening history. If you choose marker removal, follow AWS's delete marker instructions and distinguish the marker's ID from a data version's ID. Deleting the latter is permanent.

What happens when versioning is suspended?

Suspension leaves existing numbered versions in the bucket, but new writes use a null version ID. Uploading the same key again can overwrite an existing null version permanently. See AWS's suspended-bucket write behavior.

A simple deletion in a suspended bucket can remove the null data version and insert a null delete marker. Older numbered versions remain unless separately deleted. The suspended-bucket deletion rules are why suspension should not be treated as equivalent protection with fewer stored versions.

Control storage costs without losing recovery history

Amazon S3 stores complete versions, not just their differences. Twenty retained versions of a 10 MB object represent 200 MB of object data before any storage-class billing minimums or additional charges. Account for request, retrieval, and transfer costs as well as storage.

Choose retention around how long it takes to detect a problem and complete recovery. A lifecycle rule using NoncurrentVersionExpiration permanently removes eligible older data versions. A current-version expiration in a versioning-enabled bucket generally adds a delete marker rather than immediately removing that history. Review the lifecycle expiration rules before applying a cleanup policy. There is no universal safe 30-day retention period.

Versioning is one part of recovery planning

Separate routine application access from permissions to delete specific versions or change retention settings. Where you need protection against version deletion, evaluate S3 Object Lock and its retention or legal-hold settings. Enabling versioning alone does not provide those protections.

Object history is also not a coordinated snapshot of a database or several dependent files. Plan application-consistent backups, access to encryption keys, and restore tests alongside versioning. Our cloud backup and recovery planning guide covers those broader decisions.

Your next workload belongs on Hivenet.

Pick one AI, compute, or storage workload and see the difference for yourself. Spin it up in minutes, or let our team map your fastest path to production.