S3 Cost Traps: What Nobody Tells You About Lifecycle Policies
Hey, it’s Lefteris 👋 I’m the voice behind the weekly newsletter “The Cloud Engineers.”
You’re running a production workload on AWS. You set up S3, maybe enabled versioning because “best practice,” and moved on. Six months later, your S3 bill is three times what you expected, and you have no idea why.
You’re not alone. S3 pricing is deceptively simple on the surface, but underneath it hides several cost traps that lifecycle policies are supposed to solve. The problem? Most teams either skip lifecycle rules entirely or configure them in ways that barely help.
Let’s talk about what’s silently eating your budget.
The Versioning Tax
Versioning is great for data protection. Every overwrite or delete keeps the old object around, which means you can recover from accidental changes. What nobody emphasizes: every old version is a billable object.
If you’re writing logs, reports, or processed data that gets overwritten frequently, you could be storing 10x or 50x the “visible” data. The S3 console shows you the current objects. The old versions hide underneath, quietly accumulating storage charges.
The fix: Always pair versioning with a lifecycle rule that expires non-current versions. Ask yourself: “Do I really need 90 days of old versions, or would 7 days cover any realistic recovery scenario?”
The Multipart Upload Graveyard
When a large upload fails halfway, the uploaded parts don’t disappear. They sit in your bucket as incomplete multipart uploads, invisible in the console’s normal view, but fully billable.
If you’re running data pipelines, ETL jobs, or any process that writes large objects and occasionally fails, these orphaned parts add up. Some teams discover gigabytes of phantom storage they never knew existed.
The fix: Add a lifecycle rule to abort incomplete multipart uploads after a short window, 7 days is generous for most workloads. Some teams set it to 1 day.
The “I’ll Transition Everything to Glacier” Mistake
A common first move: create a lifecycle rule that transitions all objects to S3 Glacier after 30 days. Sounds sensible, cold storage is cheap.
Here’s what catches people:
Minimum storage duration charges. Glacier has a 90-day minimum. Delete an object on day 45? You still pay for 90 days.
Retrieval costs. If your application or downstream process ever needs those objects back, retrieval fees and restore times can surprise you.
Small object overhead. Glacier adds 32KB of metadata per object. If you’re storing millions of tiny files, the overhead alone can exceed what you’d pay in Standard.
The right question: Before transitioning, map your actual access patterns. If objects are never accessed after 30 days, Glacier Deep Archive might make sense. If they’re occasionally accessed, Infrequent Access (IA) with its simpler retrieval model is a safer bet.
Lifecycle Rules That Don’t Actually Apply
This one is subtle. You create a lifecycle rule, confirm it’s active, and assume it’s working. But lifecycle rules scope by prefix and tags. If your bucket structure changed after you wrote the rule — new prefixes, different naming conventions — your rule might be covering 10% of the bucket while the rest grows unchecked.
The fix: Audit lifecycle rules quarterly. Use S3 Storage Lens to see the actual breakdown of storage classes, current vs. non-current versions, and incomplete multipart uploads across your buckets. If the numbers don’t match your expectations, your rules have gaps.
A Mental Model for Getting This Right
Think of lifecycle policies as a three-layer system:
Expiration layer: What can be deleted, and when? Non-current versions, expired delete markers, incomplete uploads.
Transition layer: What should move to cheaper storage, and based on what access pattern evidence?
Audit layer: How do you verify the rules are actually working as intended?
Most teams only think about layer two and skip layers one and three entirely. That’s where the silent costs hide.
The Takeaway
S3 is not “set and forget.” The defaults are designed for durability, not cost efficiency. If you’re not actively managing object versions, failed uploads, and storage class transitions with lifecycle rules, and validating those rules still match reality, you’re overpaying.
Start with a single bucket. Check its Storage Lens dashboard. You’ll probably find at least one surprise waiting for you.

