S3 Lifecycle Rules: Stop Paying Standard Prices for Cold Data

Old objects sit in S3 Standard forever unless you move them. Learn the storage classes honestly, how to write a lifecycle rule, and the multipart-upload trap.

Published July 14, 2026 · Last updated July 14, 2026

S3 waste is invisible. The bucket works, nothing is broken, no alarm fires — and the bill quietly grows because an object you wrote three years ago is still sitting in Standard storage, priced as if you might read it any second. You don't. This post covers how S3 pricing actually works, which storage class to move cold data to (and when not to), how to write the lifecycle rule that does it automatically, and the one charge almost nobody knows they're paying.

Why does my S3 bill grow when nothing changed?

Because S3 bills you on what is stored, not what is accessed. An object you haven't touched since 2023 costs exactly the same per gigabyte as one you read a thousand times a day — as long as both sit in the same storage class.

And objects do not migrate themselves. There is no background process that notices "this file hasn't been read in a year" and moves it somewhere cheaper. Every object stays in whatever class it was written to, at that class's price, until you tell S3 to move it. So a bucket that only ever grows — logs, backups, user uploads, exports — accumulates cold data in expensive Standard storage indefinitely. The bill creeps up not because anything changed, but precisely because nothing did.

The pattern is almost always the same shape: a small fraction of a bucket's objects are read regularly, and a large, ever-growing majority are effectively write-once. Application logs written for compliance and never queried. User uploads from accounts that churned two years ago. Nightly export files someone might need "just in case." All of it priced at the hot Standard rate, all of it stone cold. The fix isn't to delete anything (though expiring truly dead data helps) — it's to stop paying premium prices to store things you're not touching.

Which storage class should cold data go to?

This is where most posts mislead you, because they quote the headline per-GB price and stop. The per-GB price is not the whole cost. Retrieval fees and minimum storage durations can make a "cheaper" class more expensive for the wrong data.

Storage class~Storage / GB-moThe catch
Standard~$0.023Baseline; no retrieval fee, no minimum duration
Intelligent-Tiering~$0.023 → lowerSmall per-object monitoring fee; auto-moves data between tiers
Standard-IA~$0.012530-day minimum + per-GB retrieval fee; delete early, pay 30d
Glacier Instant~$0.00490-day minimum; per-GB retrieval fee (retrieval is fast)
Glacier Flexible~$0.003690-day minimum; retrieval takes minutes to hours
Glacier Deep Archive~$0.00099180-day minimum; retrieval takes up to 12 hours

Read the catch column before the price column. Standard-IA looks like a 45% saving — but it has a 30-day minimum duration (delete or move an object sooner and you're billed as if it stayed 30 days) and a per-GB retrieval fee every time you read it. Move hot data to IA and the retrieval fees can cost you more than Standard did. IA is for data you're confident is cold and will stay put. Glacier tiers trade retrieval latency for deep discounts — perfect for compliance archives, wrong for anything you might need this afternoon.

When is Intelligent-Tiering just the right default?

If you can't confidently predict a bucket's access pattern — and most teams can't, because they don't have per-prefix access analytics — Intelligent-Tiering is usually the correct answer, and it's worth recommending plainly.

It monitors each object's access for a small per-object monitoring fee and automatically moves objects to colder tiers as they go untouched (and back to frequent access if they're read again). Crucially, it has no retrieval fees for the standard access tiers, so it removes the risk that makes manual IA/Glacier decisions dangerous: you can't accidentally park hot data in a class that punishes reads. For a team without access data, that safety is worth the monitoring fee. Reach for manual lifecycle transitions only when you know the access pattern; reach for Intelligent-Tiering when you don't.

How do I write a lifecycle rule?

A lifecycle configuration is a JSON document attached to the bucket. This one transitions objects to Standard-IA at 30 days, Glacier Flexible at 90, and expires them at 365 — and, importantly, aborts incomplete multipart uploads after 7 days (more on that next):

{
  "Rules": [
    {
      "ID": "tier-and-expire-cold-data",
      "Status": "Enabled",
      "Filter": { "Prefix": "logs/" },
      "Transitions": [
        { "Days": 30, "StorageClass": "STANDARD_IA" },
        { "Days": 90, "StorageClass": "GLACIER" }
      ],
      "Expiration": { "Days": 365 },
      "AbortIncompleteMultipartUpload": { "DaysAfterInitiation": 7 }
    }
  ]
}

Apply it with aws s3api put-bucket-lifecycle-configuration --bucket my-bucket --lifecycle-configuration file://lifecycle.json.

One cost most people miss: transitions themselves cost money. Each object you move to a colder class incurs a small per-request transition fee. For a bucket full of large objects that fee is trivial relative to the storage you save. But for a bucket with millions of tiny objects, the transition requests — and, for IA and Glacier, the per-object overhead each class adds for metadata — can outweigh the savings entirely. Lifecycle rules pay off for fewer, larger objects; for a firehose of tiny ones, Intelligent-Tiering or leaving them in Standard is often cheaper. Model it before you assume colder is always better.

What's the S3 charge nobody knows about?

Incomplete multipart uploads. This is the most surprising line in your S3 bill, and the most linkable fact in this post.

When a large upload is done in parts and fails partway — a dropped connection, a crashed job, a CLI that was Ctrl-C'd — the parts that did upload don't disappear. They sit in the bucket, billed as stored data indefinitely, and here's the trap: they are invisible in the normal S3 console object listing. You can look at the bucket, see nothing unusual, and still be paying for gigabytes of orphaned upload fragments that have been accumulating for years.

What else should I check?

Invisible, slowly-accumulating storage isn't unique to S3 — the same neglect leaves orphaned EBS volumes and forgotten snapshots billing in the background of your account. If you're hunting for silent storage waste, check block storage too. See how to find and delete orphaned EBS volumes and snapshots.

How do I find this across my whole AWS account?

Auditing one bucket's storage classes, missing lifecycle rules, and hidden multipart uploads is doable by hand. Doing it for every bucket in every region is the tedious part — and the multipart uploads, being invisible in the console, are the part you'd never find manually. That's what a scan is for. Parsivex checks each bucket, flags cold data in the wrong storage class, buckets with no lifecycle policy at all, and orphaned multipart uploads, and estimates the monthly savings for each.

For what these findings mean once they show up in your report, see S3 wrong storage class, S3 bucket missing lifecycle policy, and Incomplete multipart uploads.

Related posts

Related articles

Find wasted AWS spend across your whole account

Connect read-only and run a free waste scan — no credit card required. Parsivex flags idle resources, oversized instances, and networking waste in every region.

Get your free scan