AWS Storage Blog
Flexibly control Amazon S3 Object Lock retention based on real business events
Immutability is a foundational data protection control, protecting data in place against unintended changes and deletions by authorized users, and changes by unauthorized users. In many cases, however, it isn’t clear at the time data is written how long it needs to stay immutable, or when that period should begin. A signed contract might need to remain unchanged until the contract ends, or a financial record might need to stay in place until an account closes or a claim resolves. These events can occur years after the data was written, so the end of the retention period can’t be determined up front. Setting it too early risks a record becoming deletable before it’s safe to remove. Setting it too far out means paying to store data after its retention obligation has ended.
Amazon S3 Object Lock helps you enforce retention policies for regulatory compliance and data protection, with two retention types: fixed and variable. Fixed retention pairs a retention mode with a retain-until-date, so you need to know the retention end date when you apply the lock. Variable retention pairs a retention mode with an event hold and a duration in days or years. The object is write-once-read-many (WORM) protected from the moment the hold is set. While the hold remains active, its reported retain-until-date moves forward with time, reflecting the current time plus the configured duration. Releasing the hold fixes the retain-until-date at the release time plus the duration or preserves an existing later date.
This post walks through variable retention in two scenarios, a contract retention workflow and a ransomware recovery window, then covers how to separate hold release from deletion, how the retain-until-date behaves, what to monitor, and a sample that automates hold release. By the end of this post, you will understand how to protect a record the moment it’s written without knowing its retention end date, start the retention period from the event that determines it, and stop paying for storage after the obligation has ended.
Alternative approaches to event-triggered retention
Before variable retention, event-triggered retention requirements left the following options, each with a trade-off:
- Far-future retain-until-date, compliance mode – Storage charges continue long after the retention obligation ends, with no way to shorten the date.
- Far-future retain-until-date, governance mode – Early deletion becomes possible, but so does deletion by anyone holding
s3:BypassGovernanceRetention. - Rolling the retain-until-date forward on a schedule, often every 30 days – A
PutObjectRetentioncall per object per cycle, for the life of the archive. To learn more, refer to Maintaining object immutability by automatically extending S3 Object Lock retention periods. - Legal hold in place of a retention period – No retention period on release, so the object is deletable immediately if the retain-until-date has passed. Removal can be denied with a condition key, but that protection depends on permissions rather than on a retention period.
Variable retention helps you define retention periods accurately, because the period starts from the event that ends the obligation rather than from an estimate made at write time. The event hold keeps the object WORM protected while that end date is unknown. You stop paying to store data past its obligation, and no record becomes deletable before it should. Compliance mode protects the resulting retain-until-date, and no identity can override this protection, including the root user. S3 Object Lock has been assessed by Cohasset Associates for use in environments subject to SEC Rule 17a-4(f), FINRA Rule 4511, and CFTC Regulation 1.31. There is no rolling extension schedule to run, because one call sets the hold and one releases it.
Retaining records until a business event
Consider a company that must retain signed contracts for 5 years after each contract ends. Contracts don’t all end on a schedule the company controls: one might renew, terminate early, or run to its full term, and at signing time the outcome and its timing are both unknown.
As each record is written, the company sets compliance mode, sets the event hold, and configures a 5-year duration:
aws s3api put-object-retention \
--bucket amzn-s3-demo-bucket1 \
--key my-object \
--retention '{
"Mode": "COMPLIANCE",
"EventHold": "ON",
"EventHoldDuration": {"Years": 5}
}'
From this point, the object version is WORM protected, and users, including the root user, can’t delete that version or reduce its protection. The record stays protected for as long as the contract remains active, whether that is weeks or years, with no scheduled job re-locking it and no retention end date estimated in advance.
When the contract ends, the company releases the hold:
aws s3api put-object-retention \
--bucket amzn-s3-demo-bucket1 --key my-object \
--retention '{"Mode":"COMPLIANCE","EventHold":"OFF"}'
Amazon S3 sets the retain-until-date to the release time plus 5 years. The record remains protected for those 5 years, after which it becomes eligible for deletion. If a record needs longer, the same request can also set an explicit retain-until-date, and S3 keeps whichever date is later.
For objects that already exist, S3 Batch Operations applies the same parameters across an entire archive in one job. This lets an archive using a rolling retention-extension schedule move to variable retention without updating each object separately.
The minimum event hold duration is 1 day and the maximum is 100 years. For the AWS Command Line Interface (AWS CLI) and AWS SDK syntax, and the S3 Batch Operations job setup, see Configuring S3 Object Lock and S3 Object Lock retention.
Using the duration as a ransomware recovery window
Consider a system that configures variable retention as its bucket default, applying an event hold and a 30-day duration to every new object version without changing application code. On the Amazon S3 console, open the bucket’s Properties tab and Edit the Object Lock configuration. In Default retention, select Enable. In Default retention mode, select Compliance. In Select Default retention type, select Variable retention with event hold. Enter a Default event hold duration of 30 days and save the configuration.

Figure 1: Configuring a 30-day variable-retention default for new object versions

Figure 2: Bucket Properties showing the configuration of the 30-day variable-retention default
Normal operation is unchanged: new versions are written, superseded versions accumulate, and all of them are protected. An unauthorized party using valid credentials who tries to delete a protected version fails.
To destroy the data, that party has to release the hold first. Releasing a hold sets a retain-until-date 30 days out, and in compliance mode, users can’t bring that date closer, including the root user. The version stays for 30 days. That is time to detect the issue, investigate it, and roll back to a known-good version. If 30 days isn’t enough, you can extend the retain-until-date or set a new event hold. Compliance mode has one documented route to early deletion: closing the AWS account that holds the data.
A version stays protected until the system owner decides it’s no longer needed for rollback. Releasing the hold starts the 30-day period, after which S3 Lifecycle can expire the version and stop the storage charge. S3 Batch Operations applies the release across many object versions in one job.
In this example, a bucket-level default retention was used. Be aware that a bucket default is not an enforcement control, because an upload that explicitly specifies S3 Object Lock parameters overrides it. To require that every object is protected with the desired settings, use a bucket policy or a resource control policy (RCP) with the s3:object-lock-event-hold and s3:object-lock-event-hold-duration-days condition keys to deny requests that don’t meet your requirements.
An RCP is the stronger choice because it can’t be overridden by a bucket policy or an account administrator. You can scope it to named buckets, or to buckets that have S3 attribute-based access control enabled and carry a particular tag. For the condition keys and example policies, see Object Lock considerations.
Separating hold release from object deletion
Removing data protected by variable retention takes two distinct operations: releasing the event hold through PutObjectRetention, and deleting the object version through DeleteObjectVersion. Because these are separate actions, the permissions can be held by separate identities, so you can require two identities to remove a protected version, with S3 enforcing the waiting period between the two steps.
Legal hold already supports that split, but releasing a legal hold lets the second identity act the moment the first one does, whereas releasing an event hold starts a retention period that compliance mode doesn’t allow users to shorten. The result is a two-person process with a waiting period enforced by S3 rather than by permissions alone.
Setting a hold and releasing one are the same API action, so the split is expressed with the s3:object-lock-event-hold condition key. The following statement, attached to the identity that holds s3:DeleteObjectVersion, prevents that identity from releasing holds while leaving it able to set them:
{
"Version": "2012-10-17",
"Statement": [{
"Sid": "DenyEventHoldRelease",
"Effect": "Deny",
"Action": "s3:PutObjectRetention",
"Resource": "arn:aws:s3:::amzn-s3-demo-bucket1/*",
"Condition": {"StringEquals": {"s3:object-lock-event-hold": "OFF"}}
}]
}
A second identity, without this statement and without s3:DeleteObjectVersion, performs the release. Neither identity can both start the retention period and delete the version, and the duration determines how long passes between the two steps. You can go further and grant no identity s3:DeleteObjectVersion at all, letting a lifecycle rule expire noncurrent versions after their retain-until-date has passed, so S3 performs the deletion rather than a principal. Lifecycle configuration then becomes the control point, with s3:PutLifecycleConfiguration held away from the release identity. Note that even if someone changes the lifecycle rule, it still can’t delete a version whose hold is active or whose retain-until-date has not passed.
The pattern depends on compliance mode. In governance mode, an identity with s3:BypassGovernanceRetention can release the hold, shorten the date, and delete the version, returning both steps to a single identity. Denying that action in an RCP closes the gap, though a policy an administrator can amend is a weaker control than compliance mode, which users can’t override.
How the retain-until-date behaves
While the event hold is active, the hold keeps the object from being deleted. The retain-until-date is there so you have a machine-readable answer to the question “at the earliest, when could this be deleted?” without joining the hold state, current time, and duration together yourself. S3 computes it as the release-now case: the current time plus the duration. As with retain-until-dates, it advances as time passes and doesn’t move backward.
You can change the duration in either direction while the hold is active. Increasing it pushes the date further out; decreasing it doesn’t pull the date back. The date pauses where it is and resumes moving after the shorter duration would carry it past that point. GetObjectRetention, HeadObject, and GetObject return the computed date, and S3 Inventory reports it as of the time the report was generated.
On release, the date is fixed at the release time plus the duration, or at the object’s existing date if that is later. You can also set an explicit retain-until-date alongside the event hold and duration, which gives a record a fixed minimum plus an event-driven extension. A record could be held for 5 years, or for 90 days after a contract ends, whichever is later. On the S3 console, this is the Add extra retention protection option, and the explicit date can be moved further out as needed.
Monitoring event hold activity
AWS CloudTrail, S3 Event Notifications, and S3 Inventory all report event hold state and duration alongside the existing S3 Object Lock fields, so auditing variable retention works the same way as auditing other retention settings.
The s3:ObjectRetention:Put notification triggers for explicit PutObjectRetention calls, which covers setting a hold, releasing one, and modifying a duration. It doesn’t trigger when retention is applied through PutObject, CopyObject, or CreateMultipartUpload, or when a bucket default is applied at upload; those produce s3:ObjectCreated:* instead. Release produces one, because release is what starts the retention period.
For the CloudTrail fields, the notification event type, and the S3 Inventory fields, see Object Lock considerations, Event notification types and destinations, and S3 Inventory.
For estate-wide coverage rather than per-object detail, S3 Storage Lens reports S3 Object Lock object count and the percentage of objects with S3 Object Lock enabled, plus the equivalent byte metrics. S3 Storage Lens answers how much of an estate is protected; S3 Inventory answers which versions carry which settings.
Replication, lifecycle, and other interactions
S3 replicates the event hold status and duration with the object, and both buckets must have S3 Object Lock enabled. When a hold is released on the source, S3 replicates the release, and the destination computes its own retain-until-date from the time of replication. The replica is therefore protected slightly longer than its source, by however long replication took. S3 Replication Time Control replicates 99.99% of objects within 15 minutes, backed by a service level agreement, which bounds that difference for all but a small fraction of objects.
An active event hold prevents S3 Lifecycle from deleting an object, in the same way a legal hold or a retain-until-date does, so storage charges continue until the hold is released and the resulting date passes. Setting and releasing holds are metadata-only operations that don’t interrupt S3 Intelligent-Tiering or trigger a restore from archive storage classes, so holds can be managed across an archive without retrieving data from archive storage. As with all S3 Object Lock retention, variable retention requires a general purpose bucket, and encryption can’t be changed while an object is protected. S3 Object Lock also doesn’t allow annotation changes: S3 annotations can’t be created, updated, or deleted on a protected version. Refer back to Object Lock considerations for the full list.
Automating hold release at scale
Setting an event hold protects data before the triggering event is known. Some workflows need the release automated too. Consider a nightly database export that overwrites the same key each night. Each superseded export should start its recovery window as soon as it’s replaced, without the export job needing any knowledge of S3 Object Lock. Apache Iceberg presents a similar case, because maintenance operations like snapshot expiration delete data files after they are no longer referenced.
Automatic removal of event hold for S3, an AWS sample, does this. It releases event holds on noncurrent versions after they meet a configured policy, which can target versions hidden by a delete marker, versions replaced by an overwrite, or both. Refer to the GitHub repository for full details.
Pricing and availability
Variable retention is available in all AWS Regions, including AWS GovCloud (US) and AWS China Regions, at no additional charge. Setting and releasing a hold are PutObjectRetention calls, billed as PUT requests at the S3 Standard rate regardless of the storage class the object is in, and storage is charged as usual for as long as the object version is retained. See Amazon S3 pricing for current rates.
The saving relative to the rolling extension pattern is in request volume: one call to set a hold and one to release it, in place of a PutObjectRetention call per object per extension cycle for the life of the archive.
Cleaning up
A version protected in compliance mode can’t be permanently deleted until its hold is released and the resulting retain-until-date has passed, and because the duration has a minimum value, a recently released hold still leaves the version protected for a further period. A regular delete request adds a delete marker rather than removing the protected version beneath it. For objects in governance mode, versioned delete requests that include the x-amz-bypass-governance-retention:true header delete protected versions immediately.
Conclusion
With variable retention, you can protect data without knowing when its retention period will end. You set the duration up front, the event hold protects the object until the triggering event happens, and releasing the hold starts the countdown from that point. If you are extending retain-until-dates on a schedule today, S3 Batch Operations moves that archive onto variable retention in a single job.
To get started, visit Locking objects with Object Lock.