Most teams set their S3 storage class once, at object creation, and never touch it again. Then eighteen months later someone opens the Cost Explorer, sees a storage line item that's grown 3x faster than actual data volume, and can't figure out why. Here's the actual framework — in the order it should happen, not the order most teams discover it.

Step 1: Sort your objects by access pattern, not by age.

Before touching lifecycle policies, split what you're storing into three buckets: hot (accessed regularly, latency-sensitive — active application data, current logs), warm (accessed occasionally, retrieval time doesn't matter much — monthly reports, older user uploads), and cold (rarely or never accessed again, but you're legally or operationally required to keep it — audit logs, compliance archives, old backups). This sort matters more than the storage-class pricing chart, because the actual cost driver isn't which class you pick — it's how badly that class matches the object's real access pattern.

Step 2: Match the class to the bucket — and watch the retrieval trap.

Hot data → S3 Standard. Don't get clever here. The moment you move actively-accessed data to a cheaper class to save on storage, you start paying retrieval fees every time it's touched — and those add up faster than the storage savings did.

Warm data → S3 Standard-IA or Intelligent-Tiering. This is where most teams actually leave money on the table — leaving data that hasn't been touched in 90 days sitting in Standard because nobody set a lifecycle rule, paying full price for storage nobody's reading.

Cold data → Glacier Flexible Retrieval or Glacier Deep Archive, depending on how fast you'd actually need it back. Deep Archive is dramatically cheaper per GB, but retrieval can take 12+ hours — fine for compliance archives, a real problem if "cold" data turns out to be something engineering needs mid-incident.

Step 3: Don't let Intelligent-Tiering fool you into skipping this entirely.

Intelligent-Tiering sounds like it solves the whole problem automatically — and for genuinely unpredictable access patterns, it's the right call. But it has its own monitoring fee per object, and for buckets with millions of small objects, that per-object fee can quietly outweigh what you'd have paid just picking the right class manually. If you already know an object's access pattern (you usually do), a correctly-set lifecycle rule beats Intelligent-Tiering's automation fee every time.

Step 4: Revisit lifecycle policies when access patterns change, not on a fixed schedule.

The actual failure mode isn't "we never set lifecycle policies" — it's "we set them once, for last year's access pattern, and never revisited them." A dataset that was warm during a migration project is cold six months later. A logging bucket that seemed safe to archive at 90 days turns out to get pulled during incident reviews at 120. Tie lifecycle policy review to actual usage data — S3 Storage Lens or a scheduled Athena query against access logs — not to a calendar reminder that nobody reads.

Running Azure or GCP instead? The same three-bucket logic applies — just a different name on the tin:

Access pattern

AWS S3

Azure Blob

GCP Cloud Storage

Hot

Standard

Hot

Standard

Warm

Standard-IA

Cool

Nearline

Cold

Glacier Flexible Retrieval

Cold / Archive

Coldline

Deep archive

Glacier Deep Archive

Archive

Archive

The actual takeaway: the storage class pricing table is the least important input in this decision. The access-pattern sort in Step 1 is what determines whether lifecycle policies save you money or quietly generate a retrieval-fee bill nobody notices until it's three months of history deep.

Have a bucket you're not sure about — hot, warm, or cold? Reply to this email with the access pattern and I'll help you think through the class match.