How to8 min readOctober 5, 2026

S3 lifecycle rules explained: examples for versioned buckets, delete markers and storage class transitions

How S3 lifecycle rules decide what to move and what to delete, with tested JSON examples for noncurrent versions, expired delete markers, incomplete multipart uploads and size-filtered transitions, and the minimum-duration and 128 KB rules that make some transitions cost more than they save.

JeVaughn Ferguson
Founder, developer
The short version

Start every bucket with two cheap rules, abort incomplete multipart uploads after a week and remove expired delete markers, then add NoncurrentVersionExpiration with a version count and a day count you would be comfortable recovering from. Add transitions last, filtered to files larger than 128 KB and kept at least as long as the target class's minimum duration, and remember that a put replaces the whole configuration.

Picture a small design studio that keeps its project files in S3. Versioning is on, because someone once overwrote a client's final artwork, and a nightly aws s3 sync pushes the working folder up. The folder holds about 40 GB. A year later the bill is for more than a terabyte.

Nothing was wrong with the setup, it just had no end date. Twenty active files of around 200 MB each, saved most working days, leave a new noncurrent version behind every night: roughly 1 TB a year that nobody can see in the folder. Add a few large uploads from hotel Wi-Fi that failed halfway, whose parts sit in the bucket, billed and invisible, and the storage line is about 25 times what the team thinks it stores. This is an illustrative scenario, but each piece of it is ordinary S3 behavior.

Search interest in S3 lifecycle rules runs higher than interest in everyday commands like aws s3 sync, and the related searches are almost all the same two: lifecycle policy and lifecycle rules. Most people arrive after a bill like that one.

A lifecycle configuration is a set of rules S3 applies to your objects once a day. Each rule picks objects with a filter and does one of two things to them: moves them to another storage class, or deletes them. This guide covers the rules that matter for a bucket of business documents, with examples you can adapt.

How a lifecycle rule is built

Every rule has an ID, a Status of Enabled or Disabled, a Filter, and one or more actions. The filter can match a key prefix, object tags, and an object size range; combine more than one inside an And block. An empty filter applies the rule to the whole bucket.

Ages are counted in days from when the object was created, or for noncurrent versions from when they became noncurrent. S3 adds the days to that time and rounds up to the next midnight UTC, then does the work asynchronously, so an object can move or disappear a little after the date you expect. Billing for a transition to Glacier Flexible Retrieval or Deep Archive starts on the date the rule is satisfied, even if the physical move happens later.

{
  "Rules": [
    {
      "ID": "contracts-to-ia-after-90-days",
      "Status": "Enabled",
      "Filter": {
        "And": {
          "Prefix": "contracts/",
          "ObjectSizeGreaterThan": 131072
        }
      },
      "Transitions": [
        { "Days": 90, "StorageClass": "STANDARD_IA" }
      ]
    }
  ]
}
  • Actions: Transitions and NoncurrentVersionTransitions move objects; Expiration and NoncurrentVersionExpiration delete them.
  • Filters: one prefix, any number of tags, and ObjectSizeGreaterThan or ObjectSizeLessThan in bytes.
  • A disabled rule is kept but ignored, which is the safe way to pause one.

Versioned buckets: the copies you are paying for

In a bucket with versioning on, overwriting a file keeps the old one as a noncurrent version, and deleting a file only adds a delete marker on top. That is what makes recovery possible, and it is also why storage grows even when the visible folder does not. A normal Expiration rule does not touch noncurrent versions at all.

NoncurrentVersionExpiration is the rule that controls them. NoncurrentDays sets how long a version is kept after it is replaced, and NewerNoncurrentVersions, from 1 to 100, keeps that many recent versions regardless. A version is only deleted when both limits are exceeded. The example below always keeps the five most recent old versions of every file, deletes older ones once they have been noncurrent for 90 days, and moves noncurrent versions to Standard-IA after 30 days, so each one spends at least the 30-day IA minimum there before it is deleted.

{
  "Rules": [
    {
      "ID": "keep-five-versions-90-days",
      "Status": "Enabled",
      "Filter": {},
      "NoncurrentVersionTransitions": [
        { "NoncurrentDays": 30, "StorageClass": "STANDARD_IA" }
      ],
      "NoncurrentVersionExpiration": {
        "NoncurrentDays": 90,
        "NewerNoncurrentVersions": 5
      }
    }
  ]
}
  • Choose NoncurrentDays from how long it takes your team to notice a bad overwrite, not from the bill.
  • Objects under an Object Lock retention period or legal hold are not removed by lifecycle until the lock ends.

Delete markers and unfinished uploads

Once old versions are expired, a deleted file can leave behind a delete marker with nothing under it, called an expired object delete marker. They cost almost nothing to store but slow down listings in buckets with many deletes. Setting ExpiredObjectDeleteMarker to true removes them. It cannot sit in the same Expiration block as Days, and the rule cannot use a tag filter.

Multipart uploads that never complete are the other invisible cost: the parts are stored and billed but never appear as a file. AbortIncompleteMultipartUpload removes them a set number of days after the upload started. Every bucket that accepts large uploads should have it.

{
  "Rules": [
    {
      "ID": "cleanup",
      "Status": "Enabled",
      "Filter": {},
      "Expiration": { "ExpiredObjectDeleteMarker": true },
      "AbortIncompleteMultipartUpload": { "DaysAfterInitiation": 7 }
    }
  ]
}

When a transition costs more than it saves

Transitions are not free. Each object moved is one transition request, and several storage classes bill a minimum duration: 30 days for Standard-IA and One Zone-IA, 90 days for Glacier Instant and Flexible Retrieval, 180 days for Deep Archive. Lifecycle also will not move objects into Standard-IA or One Zone-IA until they are at least 30 days old.

Since September 2024, S3 by default skips objects smaller than 128 KB in every transition, because the request charge on a small file can exceed what it saves. Configurations created before then keep the old behavior until you edit them. You can override the default with a size filter, but for a folder of small scans or JSON files it is usually better left alone. Archiving to Glacier Flexible Retrieval or Deep Archive also adds 40 KB of metadata per object.

  • Transitions only run downhill: Glacier Flexible Retrieval can go to Deep Archive, and nothing comes back out of Deep Archive without a restore and copy.
  • If you do not know how often files are opened, Intelligent-Tiering may be cheaper than guessing with fixed days.

When rules overlap

An object can match several rules on the same day. S3 resolves it with fixed precedence: permanent deletion wins over a transition, a transition wins over adding a delete marker, and if an object qualifies for both Glacier Flexible Retrieval and Standard-IA, Glacier wins. It does not combine the rules or pick the most specific prefix.

That makes a broad cleanup rule dangerous next to narrow keep rules. An Expiration on the whole bucket after 365 days will delete files in a legal/ prefix even if another rule only transitions them. Keep deletion rules as narrow as the data they are meant for.

Applying and checking a configuration

The put command replaces the bucket's entire lifecycle configuration, so always fetch the current one first and edit it, rather than sending a single new rule. Put all rules in one file, apply it, then read it back. Changes can take some time to take effect across S3.

# Save what is there now before changing anything
aws s3api get-bucket-lifecycle-configuration \
  --bucket example-docs > lifecycle-before.json

# Replace the whole configuration with your edited file
aws s3api put-bucket-lifecycle-configuration \
  --bucket example-docs \
  --lifecycle-configuration file://lifecycle.json

# Check which rule applies to a given object
aws s3api head-object --bucket example-docs \
  --key contracts/2025/acme-msa.pdf
  • head-object returns an x-amz-expiration header naming the rule that will delete the object, and when.
  • Turn on S3 Lifecycle event notifications or Storage Lens if you need to see what was moved or deleted.

Seeing the result from the file side

Lifecycle rules work on prefixes and dates; the people affected see files. After a transition, a contract that opened instantly yesterday can need a restore today. BucketDesk shows each file's storage class next to its size and modified date, and on the Business plan you can restore an archived file from Glacier Flexible Retrieval or Deep Archive with a chosen retrieval tier, billed by AWS to your own account. BucketDesk does not create or edit lifecycle rules; they stay in your AWS account where you set them.

Try it in BucketDesk

Starter is free. Deploy a scoped role with CloudFormation, sign in, and browse, without handing anyone an access key.

Connect a bucket

Primary sources

Discussion

0 comments · open to guests · moderated
Comments appear after a quick review.

Liked this? Get the next article by email. No schedule, no filler, one click to leave.

Keep reading

All writing →