About this article: AI-assisted writing, reviewed against the cited sources. This is source research, not a lab validation of the procedures. Check the stated versions and your own recovery requirements before making changes.
Only compliance-mode Object Lock stops every user, including the root user, from permanently deleting an object version before its retain-until date. Versioning, MFA delete, legal holds and governance mode each leave an identity that can undo them.
Compliance mode still protects only the versions that were locked, and only until their dates pass. It does not stop the account being closed or an encryption key being deleted. A design that holds up needs a separate account, a retention period worked out from the backup chain, and guards on the bucket settings.
Commands below come from the AWS references, except where marked as written for this article. None was run in a lab.
Why it matters
Consider a hypothetical attacker who holds an administrator role in the account that stores the backups. Removing the copies would block recovery, so the protection has to assume that the person issuing the delete has valid credentials and broad permissions.
Permissions alone cannot answer that threat, because the attacker can often change the permissions. The useful question for each control is therefore: which identity can remove this protection, and what does it need to do so?
How a delete behaves on a versioned bucket
Object Lock works only in buckets that have S3 Versioning enabled, and the lock is held in the metadata of one object version. A lock protects only the version named in the request. It does not prevent new versions or delete markers being added on top.
A delete marker is a placeholder that makes S3 behave as if the object is deleted. The two kinds of delete give very different results on a locked object.
| Request | Result on a locked version | What the client sees |
|---|---|---|
| Simple delete, no version ID | Marker added, data kept | 200 OK |
| Permanent delete, version ID given | Refused | 403 Access Denied |
| GET, no version ID, marker is current | Nothing returned | 404 and x-amz-delete-marker: true |
| GET naming the marker's version ID | Nothing returned | 405 and x-amz-delete-marker: true |
Two consequences matter for backups.
A 200 on delete does not show that data has gone. A backup catalogue or clean-up script can report success and the bucket can look empty in an ordinary listing while every locked version remains. ListObjectsV2 does not return objects whose current version is a delete marker, and only ListObjectVersions lists the markers.
Delete markers themselves are not WORM-protected, whatever lock sits on the object beneath. WORM means write once, read many. An attacker can add markers freely, but cannot use them to destroy locked data.
What each control stops
| Control | Who can undo it | Holds against a stolen admin role? |
|---|---|---|
| Versioning alone | Anyone allowed s3:DeleteObjectVersion. Versioning can be suspended on a bucket without Object Lock |
No |
| MFA delete | Root user with the MFA device | Against non-root roles, yes. Not against a compromised root user and device |
| Legal hold | Any user with s3:PutObjectLegalHold |
No, if the role has that permission |
| Governance retention | A user with s3:BypassGovernanceRetention who sends the bypass header |
No, if the role has that permission |
| Compliance retention | No user, including root, until the date passes | Yes, for the locked version |
The last column is an inference from the permission rules, not a vendor statement.
Governance bypass is easy to trigger. The console adds the bypass header by default, so a console delete by a user holding the permission succeeds without any extra step. In the CLI, the equivalent is --bypass-governance-retention.
A legal hold is not a retention control. It has no end date and is independent of retention periods. It keeps a version protected after its retention expires, until someone with the permission removes it. Bypassing governance does not remove a legal hold.
MFA delete has operating costs. Only the bucket owner (root account) can enable it, it cannot be enabled from the console, and it cannot be used with lifecycle configurations. A backup bucket that relies on lifecycle rules to remove aged versions cannot use it.
Compliance mode cannot be walked back. A locked version's mode cannot be changed and its period cannot be shortened. AWS states that the only way to delete such an object early is to delete the associated AWS account.
How retention dates are set
Retention is per version, so different versions of one key can carry different modes and dates. The rules that affect design are below.
- Bucket default. You give a duration in days or years, never both. S3 adds it to the version's creation timestamp.
- New objects only. The default rule applies to every new object placed in the bucket. The inference is that versions already in a bucket are not locked by adding a default. S3 Batch Operations can apply retention to many existing objects.
- Explicit settings win. A version written with its own mode and period overrides the bucket default.
- Extending and shortening. Anyone with
s3:PutObjectRetentioncan extend a retention period. A compliance-mode period can't be shortened. Governance-mode retention can be overridden or removed by a user withs3:BypassGovernanceRetention. - Ceiling. The maximum retention period is 100 years.
- Upload header. A request that uploads an object with a retention period needs
Content-MD5orx-amz-sdk-checksum-algorithm. This matters to anyone writing their own upload client.
Variable retention
S3 also offers variable retention, where an event hold defers the date. While the hold is on, S3 computes the retain-until date as the current time plus the duration, so it keeps moving forward. Releasing the hold fixes the date at release time plus the duration. AWS names a configurable recovery window against ransomware as a use.
The duration can be 1 to 36,500 days or 1 to 100 years. Releasing a hold does not end protection, because the version stays WORM-protected for the full duration after release.
Variable retention has a cost trap. A version under an active hold has no end date until something releases it, so the backup process must release holds reliably or the storage grows without limit. That is an inference from the mechanism.
Working out the period
This hypothetical calculation was written for this article.
Suppose weekly full backups and six daily incrementals, with a requirement that any restore point stays restorable for 35 days. The sixth incremental depends on a full backup written six days before it. If that incremental must survive 35 days, the full must be locked for at least 41 days.
| Input | Days |
|---|---|
| Required restore window | 35 |
| Longest dependency from a restore point back to its full | 6 |
| Minimum lock on the full | 41 |
A flat 41-day default covers this chain. The window should also be longer than the time an intrusion could stay undetected, because a backup that expires before the intrusion is found gives no protection.
Deduplicating backup software needs separate treatment. A new backup may refer to an old stored block, so that block must stay locked for as long as any restore point uses it. Check how the software handles Object Lock in its own documentation, and do not assume a flat default suits it.
Build order
The steps below set up one vault bucket. Bucket names and account layout are placeholders.
1. Use a separate account
The editorial recommendation is a dedicated AWS account for the vault, outside the workload account, and not the Organizations management account. Service control policies do not affect users or roles in the management account, so step 5 would not protect a bucket held there.
2. Create the bucket and confirm the settings
Region and name are placeholders. The command combines the Object Lock option and the LocationConstraint required outside us-east-1, and was assembled for this article.
aws s3api create-bucket \
--bucket example-backup-vault \
--region ap-southeast-2 \
--create-bucket-configuration LocationConstraint=ap-southeast-2 \
--object-lock-enabled-for-bucket
aws s3api get-bucket-versioning --bucket example-backup-vault
aws s3api get-object-lock-configuration --bucket example-backup-vault
Expect "Status": "Enabled" from the first check and "ObjectLockEnabled": "Enabled" from the second. The AWS command example shows only the Object Lock flag, while the console steps turn versioning on separately, so check the result and do not assume it. Object Lock can also be enabled on an existing bucket. After enabling versioning for the first time, AWS recommends waiting 15 minutes before writing.
Warning: Once enabled, Object Lock cannot be disabled and versioning cannot be suspended on that bucket. Do this on a new bucket until the design is proven.
3. Rehearse in governance mode
AWS notes that governance mode can be used to test retention settings before a compliance period is applied. Run the whole backup job against a throwaway bucket with a short governance default, one day in this example, and confirm that the software writes, reads and expires correctly.
aws s3api put-object-lock-configuration \
--bucket example-backup-rehearsal \
--object-lock-configuration '{ "ObjectLockEnabled": "Enabled", "Rule": { "DefaultRetention": { "Mode": "GOVERNANCE", "Days": 1 }}}'
4. Review the compliance default and a ceiling
Warning: A version protected in compliance mode is undeletable until its retain-until date passes, and you pay to store it for that whole time. A mistaken value cannot be shortened on versions already written.
For the hypothetical chain above, the proposed compliance default is 41 days. Document that value, any explicit per-object overrides and the expected storage cost. A qualified owner must verify the actual chain and approve the irreversible choice after isolated testing; this article does not provide a production compliance-mode activation command.
A bucket policy can cap explicit retention with the s3:object-lock-remaining-retention-days condition. This is the AWS example with the bucket name and limit changed for this article.
{
"Version": "2012-10-17",
"Statement": [{
"Sid": "SetRetentionPeriod",
"Effect": "Deny",
"Principal": "*",
"Action": ["s3:PutObjectRetention"],
"Resource": "arn:aws:s3:::example-backup-vault/*",
"Condition": {
"NumericGreaterThan": { "s3:object-lock-remaining-retention-days": "400" }
}
}]
}
The cap limits how far a PutObjectRetention call can push a date out, whether by mistake or by an attacker. The example names only that action, so do not read it as a limit on other write paths. If the bucket is a replication destination, AWS notes that the policy must allow s3:ReplicateObject.
The event-hold keys do not apply to values from a bucket default. To restrict defaults, AWS says to deny s3:PutBucketObjectLockConfiguration. Under an active event hold, S3 recalculates the date on the service side, so it can move past a limit enforced at request time.
5. Guard the settings around the lock
The default can be removed by anyone allowed to write the configuration without a rule. New versions would then have no lock, while existing ones keep theirs. A service control policy (SCP) in AWS Organizations can close this gap, because it applies to users and roles in member accounts, including the member account's root user.
This policy was written for this article from the S3 action names and SCP syntax.
{
"Version": "2012-10-17",
"Statement": [{
"Sid": "FreezeBackupVaultSettings",
"Effect": "Deny",
"Action": [
"s3:BypassGovernanceRetention",
"s3:PutBucketObjectLockConfiguration",
"s3:PutLifecycleConfiguration",
"s3:PutBucketVersioning",
"s3:PutBucketPolicy",
"s3:DeleteBucketPolicy",
"s3:PutReplicationConfiguration"
],
"Resource": [
"arn:aws:s3:::example-backup-vault",
"arn:aws:s3:::example-backup-vault/*"
]
}]
}
Treat this policy as an unvalidated design example requiring a security review and isolated test. Applying it after configuration blocks those changes for every member-account identity. A later change means someone in the management account must adjust the policy, which is the separation of duties this design wants. An SCP does not restrict service-linked roles, and it limits only identities in the accounts it covers, so give outside accounts no rights to change the vault.
6. Prove the lock on a real backup object
Check an object written by the backup job, not a hand-made test file.
aws s3api get-object-retention \
--bucket example-backup-vault \
--key path/to/backup-file
The output shows Mode and RetainUntilDate. Confirm the mode is COMPLIANCE and the date meets the intended protection period. With no explicit override or event hold, the bucket default adds its duration to the version creation time; otherwise validate the effective per-version setting. For the whole bucket, S3 Inventory can report retain-until date, mode and legal hold status. Inventory can take up to 48 hours to generate, so for a current value query the version directly.
7. Test the delete paths
Both delete forms are in the delete-object reference.
Warning: Use a test object in the vault that you are content to pay for until its date. Never run a version-specific delete against production backups, because if the lock is missing or expired the version is gone.
The rehearsal must distinguish a simple delete, which creates a marker, from a version-specific delete, which should be refused while the version is protected. Keep the exact test version and its retention evidence in the rehearsal record. Use disposable test data under an approved test procedure; no delete command is supplied here.
The first should return success and create a marker. The second should fail with Access Denied. Any other result means the lock is not working as designed.
Recovering from a mass delete
After a marker flood, the versions are still present. To undelete an object, delete its marker by giving the marker's version ID. A simple delete on a key whose current version is a marker removes nothing and only adds another marker, so a retry loop without version IDs makes things worse.
This query was written for this article, on the list-object-versions output. It lists markers that are currently hiding data under a prefix.
aws s3api list-object-versions \
--bucket example-backup-vault \
--prefix path/to/backups/ \
--query 'DeleteMarkers[?IsLatest==`true`].[Key,VersionId]' \
--output text
Review that list before removing anything, and rotate the compromised credentials first so that the markers do not return.
Lifecycle and replication
Lifecycle rules keep working on protected objects, including placing delete markers, but a lifecycle rule cannot delete a locked version. Once the date passes, NoncurrentVersionExpiration permanently removes noncurrent versions, and those cannot be recovered. The lock also survives transitions between storage classes. A version whose lock has passed stays and is billed until a rule or a person removes it.
Replication adds a second copy with useful default behaviour for this threat.
- With a current replication configuration, delete markers are not replicated by default. Enabling delete marker replication removes that safeguard for markers.
- A version-specific delete on the source is not replicated to the destination.
- Retention on a source version is applied to the replica, overriding the destination default. A replica of an object with no retention takes the destination default.
- If the source has Object Lock, the destination must have it too, and the replication role needs
s3:GetObjectRetentionands3:GetObjectLegalHoldon the source. - Objects in a
FAILEDstate do not retry automatically and need S3 Batch Replication. Failures need monitoring, or the second copy silently falls behind.
What to choose and why
These are editorial recommendations.
Use compliance mode for the vault copy when the requirement is that nobody can delete it. Governance mode suits rehearsal and data where a named administrator must be able to remove objects early.
Keep the retention period as short as the backup chain and the detection window allow. Compliance mistakes are paid for in full.
Hold s3:PutObjectLegalHold tightly, and treat a legal hold as an addition to retention.
Prefer two accounts with replication where the budget allows, and leave delete marker replication off on the vault rule. Alert on s3:ObjectRetention:Put event notifications and on CloudTrail changes to the vault's configuration.
Risks and limits
Account closure. AWS names account deletion as the way compliance objects can be removed early. A closed account can be reopened within 90 days, after which it is permanently closed. Protect the vault account's root user accordingly.
Encryption keys. Object Lock does not protect against losing or deleting encryption keys. If a KMS key is deleted, the objects may become unreadable. KMS requires a waiting period of 7 to 30 days and logs the scheduling to CloudTrail, so alert on that event and keep the key under the same control as the vault.
Read access. Object Lock addresses deletion and overwriting. The inference is that it does not guarantee someone can read the data, since policies and keys can still block access. Test restores.
Cost. Each version is a whole object and is charged as one. A delete marker carries a small storage charge based on its key name.
Scale. Objects with millions of versions can cause 503 responses as S3 throttles the bucket. A backup design that rewrites one fixed key many times should be checked against this.
Scope. Object Lock operations such as PutObjectLockConfiguration are not supported on directory buckets. A locked bucket cannot be a destination for server access logs.
Regulation. AWS reports that Object Lock has been assessed by Cohasset Associates for SEC 17a-4, CFTC and FINRA environments. That assessment does not establish that a particular configuration meets any obligation in Australia, so take advice for your own retention duties.
Sources
Opened 2026-11-10. Sources behind the corrections reopened 10 November 2026.
- Locking objects with Object Lock
- Object Lock considerations
- Configuring S3 Object Lock
- Retaining multiple versions of objects with S3 Versioning
- Enabling versioning on buckets
- Working with delete markers
- Managing delete markers
- Deleting object versions from a versioning-enabled bucket
- Configuring MFA delete
- Deleting an object from an MFA delete-enabled bucket
- What does Amazon S3 replicate?
- Replicating delete markers between buckets
- How S3 Lifecycle interacts with other bucket configurations
- S3 Object Lock retention (Batch Operations)
- Troubleshooting versioning
- Required permissions for Amazon S3 API operations
- PutObjectLockConfiguration API
- Service control policies
- SCP syntax
- Closing an AWS account
- Deleting AWS KMS keys
- AWS CLI: create-bucket, get-object-retention, list-object-versions
This article was originally published on .