Skip to content
Linxus Infotech
Product Features How it works Pricing Compare Blog
Sign in Start free scan›
Guide · FinOps

S3 Lifecycle Rules and the 128 KB Floor Nobody Reads

The standard advice is to add a transition rule and watch the bill fall. For a great many real buckets that rule moves nothing at all.

By Linxus Infotech Updated Sep 9, 2026 8 min read
  • The 128 KB rule that inverts the usual advice
  • The rule almost nobody has and everybody needs
  • Measuring a bucket without enumerating it
  • Estimating the saving without overstating it
  • The configuration that undoes the protection
  • A working order
  • Frequently asked questions

The standard advice about S3 lifecycle rules is to add a transition: move objects to Infrequent Access after 30 days, Glacier after 90, and watch the bill fall. For a great many real buckets that advice moves nothing at all and saves exactly zero, and the reason is a default most people have never read.

The 128 KB rule that inverts the usual advice

A lifecycle configuration has a setting called TransitionDefaultMinimumObjectSize. Its default value is all_storage_classes_128K, and under that default objects smaller than 128 KB will not transition to any storage class at all.

The rule is not broken. It exists because transitioning a small object costs more than it saves. Each transition carries a per-object request charge, and the smaller classes add per-object metadata overhead. Below roughly 128 KB the arithmetic goes negative: you pay to move the object and then pay more to store it.

So for a bucket full of small objects, and there are a great many of them, adding a transition rule does not save a little. It moves nothing and saves nothing, while creating the impression that something has been done.

This is not hypothetical. The first production bucket measured during this work held 21,096 objects averaging 2.7 KB. Every transition rule anyone could write for it would have been inert.

The honest recommendation for a small-object bucket is therefore the opposite of the standard one. Skip the transition. Use expiration, which has no size floor, and AbortIncompleteMultipartUpload, which has none either.

The rule almost nobody has and everybody needs

Incomplete multipart uploads are the quietest cost leak in S3. When a large upload fails partway, the parts already uploaded stay in the bucket. They are billed at full storage rates. They do not appear in ListObjectsV2. They do not show in the console's object list. They accumulate silently for years.

{
  "Rules": [{
    "ID": "abort-incomplete-multipart-uploads",
    "Status": "Enabled",
    "Filter": {},
    "AbortIncompleteMultipartUpload": { "DaysAfterInitiation": 7 }
  }]
}

Seven days is a reasonable default: long enough that a genuinely slow upload is not killed, short enough that failed ones do not persist. This rule belongs on essentially every bucket, it has no size floor, and it is the single highest-value lifecycle rule most accounts are missing.

You can see what is currently sitting there with:

aws s3api list-multipart-uploads --bucket my-bucket

Measuring a bucket without enumerating it

The obvious way to analyse a bucket is to list its objects. On a bucket with tens of millions of objects that is hours of wall clock and a genuine request bill, charged to you, to answer questions CloudWatch already answers for nothing.

Two free daily metrics carry everything needed:

MetricDimensionWhat it gives you
BucketSizeBytesper StorageTypeTotal size, and the split across storage classes
NumberOfObjectsAllStorageTypesObject count, and with the above, average object size

Average object size against the 128 KB floor is the whole decision, and it comes from dividing one free metric by another. No listing, no request charges, no hours of waiting.

Two practical notes. These metrics are daily and lag 24 to 48 hours, so a very short lookback window makes a healthy bucket look empty. And S3 Storage Lens, which looks like the natural source for this, is opt-in and its free tier is a console dashboard rather than a queryable API, so CloudWatch is the source that works without anyone switching something on first.

Estimating the saving without overstating it

The temptation is to take the bucket's current size, apply the price difference between Standard and the target class, and quote the result. That number is always too big, because it assumes every byte in the bucket is old enough to transition.

A tighter bound comes from comparing two points in time. Data present both 90 days ago and now has demonstrably been stored throughout, so it is a lower bound on what a 90-day transition rule would actually move:

eligible = min(size 90 days ago, size now)

That has an assumption in it, and the finding states it rather than hiding it: it holds if objects are not being deleted and replaced at a similar rate. A bucket with high churn can show a stable size while none of the individual objects are old. Stating the assumption is what lets you judge whether it applies to your bucket.

Each storage class is also priced at its own rate and its own volume tier. Pricing an entire bucket at the Standard rate overstates anything already sitting in a cheaper class.

Findings below one dollar a month are reported as having no money in them yet. The misconfiguration is still real, and a tiny bucket today can grow, so the finding is not suppressed. It just says plainly that there is nothing to gain right now, rather than padding a total with fractions of a cent.

The configuration that undoes the protection

There is a setting worth knowing about because it is occasionally applied as a fix and is usually a mistake.

TransitionDefaultMinimumObjectSize can be set to varies_by_storage_class, which opts out of the 128 KB protection and lets small objects transition to Glacier Flexible Retrieval and Glacier Deep Archive. People reach for it when they notice their transition rules are not moving anything.

It usually makes things worse. The floor exists because moving small objects costs more than it saves, so opting out means paying per-object transition costs and per-object overhead on objects too small to repay either. If you genuinely need it, pair it with an ObjectSizeGreaterThan filter so only objects large enough to pay for their own transition are moved.

A working order

  1. Add AbortIncompleteMultipartUpload to every bucket. No size floor, no downside, and it stops an invisible leak.
  2. Set expiration where retention is actually known. Logs, temporary exports and build artifacts usually have a real answer that nobody has written down.
  3. Check average object size before writing any transition rule. Divide BucketSizeBytes by NumberOfObjects. Below 128 KB, a transition rule is inert.
  4. Add transitions only where the objects are large enough. Then verify against the storage class distribution a month later that objects actually moved.
  5. Consider Intelligent-Tiering for unpredictable access. It has a small per-object monitoring charge, which makes it another poor fit for very small objects, but it removes the need to guess at access patterns.

Why several of these findings ship carrying no savings figure at all is covered in the AWS cost optimization guide.

Frequently asked questions

Why is my S3 lifecycle transition rule not moving any objects?

Almost certainly because the objects are smaller than 128 KB. A lifecycle configuration's TransitionDefaultMinimumObjectSize defaults to all_storage_classes_128K, and under that default objects below 128 KB will not transition to any storage class. For a bucket of small objects a transition rule moves nothing and saves nothing.

What should I use instead of a transition rule for small objects?

Expiration and AbortIncompleteMultipartUpload. Neither has a size floor. Expiration removes objects once they pass a retention age you actually know, and AbortIncompleteMultipartUpload cleans up the parts left behind by failed uploads.

What are incomplete multipart uploads and why do they cost money?

When a large upload fails partway, the parts already uploaded remain in the bucket and are billed at full storage rates. They do not appear in ListObjectsV2 or in the console's object list, so they accumulate silently for years. A rule with AbortIncompleteMultipartUpload set to seven days after initiation is the single highest-value lifecycle rule most accounts are missing.

How can I analyse a large S3 bucket without listing every object?

Use the free daily CloudWatch metrics. BucketSizeBytes per StorageType gives total size and the split across storage classes, and NumberOfObjects with AllStorageTypes gives the count. Dividing one by the other gives average object size, which is the number that decides whether a transition rule will do anything. Listing a bucket with tens of millions of objects costs hours and a real request bill charged to you.

Should I set TransitionDefaultMinimumObjectSize to varies_by_storage_class?

Usually not. It opts out of the 128 KB protection and lets small objects transition to Glacier classes, which means paying per-object transition costs and per-object overhead on objects too small to repay either. If you do need it, pair it with an ObjectSizeGreaterThan filter so only objects large enough to justify the move are transitioned.

How do you estimate S3 lifecycle savings without overstating them?

Take the minimum of the bucket's size 90 days ago and its size now. Data present at both points has demonstrably been stored throughout, so it is a lower bound on what a 90-day transition rule would move, rather than assuming every byte is old enough to transition. The assumption is that objects are not being deleted and replaced at a similar rate, which a high-churn bucket can violate.

Why does a lifecycle finding sometimes say there is no money in it?

Because the bucket currently costs less than a dollar a month. The misconfiguration is still real and a small bucket can grow, so the finding is not suppressed, but padding a savings total with fractions of a cent would make the total meaningless.

Keep reading

Guide · FinOps

AWS Cost Optimization & FinOps: Finding Waste Without Inflating the Number

The waste that is genuinely measurable, and the five rules that keep a savings total honest.

Read the guide ›

Guide · FinOps

Unattached EBS Volumes: What They Cost and How to Find Them

The other storage leak that bills at full rate while doing nothing.

Read the guide ›
Linxus Infotech

Live AWS infrastructure, codified as production-grade Terraform. Maker of InfraSync.

support@linxusinfotech.com
+91 8828 757 008

Product

  • InfraSync app
  • Features
  • How it works
  • Pricing
  • Compare
  • Blog

Legal

  • Privacy policy
  • Terms & conditions
  • Acceptable use policy
  • Security
  • Cookie policy
  • Cancellation & refunds
  • Service level agreement
  • Shipping & delivery
  • Contact us

Company

  • Try InfraSync
  • Contact sales
  • Support
  • Sitemap

© 2026 Linxus Infotech Pvt. Ltd. All rights reserved.

Made for engineers who refuse to click things in production.