Most enterprises are not overspending on S3 due to a single bad storage-class choice. They are overspending because lifecycle coverage drifts, old versions accumulate, and too much data gets kept as if it all has the same business value. Here’s how to fix that and keep S3 costs under control.
Why S3 cost optimization is harder than it looks
Most S3 environments I review have the same three problems: versioning without cleanup rules, data sitting in the wrong storage class, and lifecycle policies that stopped working months ago.
Here's how each one inflates your bill. The common thread is not bad intent. It is drift. S3 cost controls tend to break quietly as buckets, prefixes, and retention needs change faster than the rules managing them.
Versioning without expiration rules
This is the biggest one.
S3 versioning protects against accidental overwrites, but every version of every object incurs storage charges, including versions that are invisible in the standard bucket view.
I worked with a team running daily data pipelines that accumulated 40 TB of noncurrent versions each month. None of it was visible in the standard bucket view. By the time we caught it, six months of version history had accumulated with no expiration rule in sight.
Wrong storage class for the access pattern
Every S3 bucket starts in S3 Standard by default. That's fine for active data. The problem is that data access patterns change over time, and most teams never move old objects to cheaper tiers.
A file untouched for six months still costs $0.023/GB in Standard. The same file in Glacier Deep Archive costs $0.00099/GB. That's a 23x cost difference for data nobody's touching.
The fix isn't complicated. It's lifecycle rules and access audits. But without either, you're paying premium rates to warehouse cold data across every bucket in your account.
Lifecycle rules that miss things
Stale prefix filters. Rules that cover current objects but skip noncurrent versions. Policies nobody's reviewed in two years.
The rules exist. They're not doing the work they should be.
This happens because infrastructure changes faster than policies do. New prefixes get added, workloads shift, and retention requirements change, but the lifecycle rules written 18 months ago do not adapt with them.
How S3 storage class pricing compares
To see why these gaps are so costly, here's what each storage class charges at 10TB per month (United States per AWS pricing):
The gap between $9.90 and $230 is a 23x difference in storage alone. Versioning bloat, abandoned multipart uploads, and early deletion penalties push the real bill even higher.
How to cut S3 costs with lifecycle policies
Lifecycle policies are the right place to start, but they are not a durable cost-control strategy on their own once S3 usage spreads across teams, accounts, and environments. Use them to cut obvious waste first, then scroll down for how to keep costs under control at scale.
Step 1: Audit your buckets before writing a single rule
Here’s the thing: rules written before checking access patterns backfire.
S3 Storage Lens gives you real data on object age, access frequency, noncurrent version bytes, and incomplete multipart uploads. You can break it all down by prefix and account.
Answer four questions before writing any lifecycle rule:
- How old are the objects in this bucket on average?
- Which prefixes are accessed frequently vs. rarely?
- How much are noncurrent versions costing you?
- Are incomplete multipart uploads sitting undetected in your buckets?
Noncurrent version bytes show up in S3 Storage Lens as a specific metric. Check that number first if versioning is on. It’s where the biggest surprises hide.
Step 2: Tag your data by function and criticality
Broad rules without tagging over-apply or miss the edge cases that cost the most.
That is the bigger issue at scale: too much data ends up on one-size-fits-all retention. When low-value data gets protected like production-critical data, storage waste builds quietly and fast.
Lifecycle rules target objects by prefix, tag, or both. Here's a simple tagging schema that works:
Tag new resources at creation time. S3 Inventory reports help you map existing buckets before you start writing rules.
Step 3: Set transition rules based on access patterns
Match transition rules to usage data, not assumptions.
Logs and application data:
- Standard → Standard-IA after 30 days
- Standard-IA → Glacier Flexible Retrieval after 90 days
- Glacier → expire after 365 days
Backup data (rarely accessed):
- Standard → Glacier Instant Retrieval after 30 days
- Glacier Instant → Glacier Deep Archive after 90 days
Compliance archives:
- Standard → Glacier Deep Archive after 90 days
- Expire after your required retention period
Two things most teams get wrong here:
First, small objects under 128KB don't transition by default. Buckets with many small metadata files need an explicit size filter on the lifecycle rule. AWS silently skips the transition otherwise, and those objects stay in Standard forever.
Second, AWS enforces minimum storage durations before transitions happen. 30 days for Standard-IA. 90 days for Glacier Instant and Flexible Retrieval. 180 days for Deep Archive. Rules that violate those floors charge you for the full minimum duration, even if you delete the object early.
Step 4: Implement rules via Console or IaC
In the AWS Console:
- Open S3, select your bucket, and go to the Management tab
- Click Create lifecycle rule
- Name it clearly (e.g.,
archive-logs-90d, notrule1) - Set scope by prefix or tag
- Add transition and expiration actions in sequence
Terraform or CDK environments should define lifecycle rules in the bucket resource block during provisioning. New buckets deployed without a lifecycle policy accumulate cost from day one.
Step 5: Clean up incomplete multipart uploads
Incomplete multipart uploads are one of the most common sources of silent S3 waste. A failed or interrupted large upload leaves parts behind. Those parts accrue charges indefinitely. They never show up as normal objects in your bucket view.
I’ve seen this one catch teams off guard more than almost anything else on this list.
Add this rule to every bucket:
{
"Rules": [{
"ID": "abort-incomplete-multipart-uploads",
"Filter": {},
"Status": "Enabled",
"AbortIncompleteMultipartUpload": {
"DaysAfterInitiation": 7
}
}
Seven days covers most upload edge cases while quickly catching stale parts. Adjust to 3 days for fast pipelines or 14 days for large data transfer jobs.
Getting S3 versioning costs under control
Versioning cleanup is the first move, not the full strategy. It can cut obvious waste fast, but it will not hold up by itself once buckets, teams, and retention rules start multiplying. Scroll down for what sustainable control looks like.
Versioning itself is worth keeping (the rollback protection is real). The problem is running it without rules for what happens to old versions.
Apply NoncurrentVersionTransition and NoncurrentVersionExpiration rules
A lifecycle rule targeting current objects won’t touch version history at all. You need a separate rule for noncurrent versions.
Here’s a working policy for a versioned backup bucket:
{
"Rules": [{
"ID": "manage-noncurrent-versions",
"Filter": {},
"Status": "Enabled",
"NoncurrentVersionTransitions": [{
"NoncurrentDays": 30,
"StorageClass": "GLACIER"
}],
"NoncurrentVersionExpiration": {
"NoncurrentDays": 90,
"NewerNoncurrentVersions": 3
}
}]
}
The NewerNoncurrentVersions parameter is what most teams miss. It keeps the three most recent previous versions intact before expiration kicks in, which gives you rollback protection without letting version history grow unchecked.
Clean up orphaned delete markers
S3 creates a delete marker instead of removing data when you delete a versioned object. The underlying versions expire, but orphaned markers linger and inflate object counts.
Add this rule to remove them:
{
"Rules": [{
"ID": "remove-expired-delete-markers",
"Filter": {},
"Status": "Enabled",
"Expiration": {
"ExpiredObjectDeleteMarker": true
}
}]
}
S3 lifecycle mistakes that wipe out your savings
Even well-designed lifecycle policies fail when they're not reviewed or tested. Here are the mistakes I see most often in S3 environments.
At a small scale, these can look like simple configuration mistakes. In larger AWS environments, they usually point to a bigger control problem: lifecycle policies drift, changes in bucket usage, and gaps that stay hidden until costs climb.
- Setting it and forgetting it costs real money. Lifecycle rules don't update when your infrastructure changes. New prefixes, new workloads, changed retention requirements: all create gaps that old rules won't catch.
- A quarterly review takes 30 minutes with S3 Storage Lens metrics. That catches drift before it compounds.
- Archiving data you'll need soon wipes out the savings fast. Glacier Deep Archive retrieval takes up to 12 hours and costs $0.02/GB at standard speed. An urgent 500 TB restore generates a $10,000 retrieval bill before egress and compute are factored in.
- I’ve watched teams celebrate their archiving savings, then burn through those savings and more on a single emergency retrieval. Run the retrieval math before archiving anything time-sensitive.
- One-size-fits-all policies delete compliance records too early or keep log data too long. Create separate lifecycle rules for each data function. A compliance archive and a log bucket should never share the same policy.
- Deploying rules without testing is the most dangerous mistake. Lifecycle expiration is irreversible. Scope the rule to a test prefix first. Use S3 Inventory reports to verify targeting before rolling out to the full bucket.
Lifecycle cleanup helps, but it does not hold up as the only control layer once S3 usage spreads across teams and accounts.
When to use S3 Intelligent-Tiering instead of lifecycle rules
Lifecycle rules work when you know your access patterns. S3 Intelligent-Tiering works when you don't.
Intelligent-Tiering watches how often each object is accessed and automatically moves colder objects into lower-cost tiers. If access patterns are unpredictable, it can be cheaper than guessing wrong with lifecycle rules.
The tradeoff: AWS charges a monitoring fee of $0.0025 per 1,000 objects per month. For buckets with millions of small objects, that fee adds up. For larger objects with unpredictable access patterns, it's almost always cheaper than guessing wrong on a lifecycle rule.
Here's how I evaluate when to use Intelligent-Tiering:
- Access patterns are predictable (logs, backups, compliance archives): use lifecycle rules. You already know when the data goes cold.
- Access patterns are unpredictable (analytics datasets, shared project files, ML training data): use Intelligent-Tiering. Let AWS figure it out.
- Objects under 128KB: Intelligent-Tiering won't auto-tier them. They remain in the frequent-access tier and still incur the monitoring fee. Lifecycle rules are the better option here.
One more thing worth knowing: Intelligent-Tiering has no retrieval fees and no minimum storage duration. That removes two of the biggest cost traps in Glacier and Standard-IA.
Data transfer costs most teams overlook
Storage waste gets most of the attention, but retrieval and transfer charges are where a “cheap” storage decision often gets expensive again.
S3 charges $0.09/GB for data transferred out to the internet after the first 100GB/month free tier. A team pulling 5TB of data per month pays roughly $450 in egress fees alone.
Three ways to cut transfer costs:
- Route through CloudFront. S3 to CloudFront transfers are free. CloudFront's distribution pricing is lower than S3 egress pricing. According to AWS, CloudFront charges as low as $0.085/GB for the first 10 TB, compared to S3's $0.09/GB, with steeper discounts at higher volumes.
- Use VPC Gateway Endpoints. If your EC2 instances or Lambda functions access S3 via a NAT Gateway, you're paying $0.045/GB in addition to S3 egress charges. A VPC Gateway Endpoint for S3 is free, eliminating that charge entirely.
- Keep compute and storage in the same region. Cross-region transfers cost $0.02/GB. Intra-region transfers between S3 and EC2 are free.
Most S3 cost calculators focus on storage costs and ignore transfer costs. Check your AWS Cost Explorer filtered by "Data Transfer" to see what you’re paying today.
Why Lifecycle Policies and Versioning Are Only the Starting Point
Those fixes work at the bucket level. The problem is that most enterprises do not have a one-bucket problem. They have dozens or hundreds of buckets across teams, accounts, and environments. That is where S3 cost optimization stops being a lifecycle-rule exercise and becomes a posture and enforcement problem.
At org scale, that breaks down. Ownership is distributed across teams, so no single person owns the full picture. Rules drift as infrastructure changes. Tagging breaks when teams skip conventions or onboard new workloads without labels. Coverage gaps hide waste because nobody has a single view of which buckets have policies and which don't.
Native AWS tools let you write rules for individual buckets. What they do not give you is an org-wide control layer that shows where protection is missing, where retention is drifting, and where low-value data is quietly driving unnecessary cost across accounts. That's where the real cost-control gap lies.
How Eon takes S3 cost optimization further
AWS gives you the tools to write rules. It doesn't tell you when those rules go stale. It won't flag new resources without a policy or show you where version bloat is compounding across accounts. That's the gap I see in almost every environment I review.
Eon's Cloud Backup Posture Management (CBPM) fixes this at the platform level. It classifies your cloud resources (including S3 data), maps them to the right backup and retention policies, and helps keep those policies aligned as the environment changes, without manual tagging or rule authoring.
Here’s what that looks like in practice:
- Policy-driven automation: Eon classifies S3 data and maps it to the right backup and retention policies based on data type and compliance needs, without relying on manual tagging.
- Incremental deduplication: Eon reduces the storage footprint of versioned backup data through deduplication, compression, and incremental snapshots, helping lower storage costs without compromising recovery.
- Granular restoration: Restore only the files or objects you need, rather than recovering an entire dataset, which helps reduce recovery time and unnecessary egress.
- Agentless deployment: Eon connects without agents or backup infrastructure in your production environment.
- Central visibility: One dashboard shows where policies are missing, where versioning bloat is growing, and where S3 backup costs are drifting across accounts.
Lifecycle policies help reduce S3 costs at the bucket level. Eon helps enterprises enforce the right protection, retention, and recovery posture across all of their S3 environments.
Eon’s cost model is also simpler: no backup agents, no added compute to protect data, and pricing based on storage used rather than a stack of extra infrastructure charges.
NETGEAR saw this in practice. After moving from a legacy backup model to Eon, the team cut backup storage costs by 35% and gained real-time visibility into backup spend across their AWS workloads.
Want to find out where S3 waste is hiding across your accounts? Schedule a free posture review, and we'll map your lifecycle gaps, versioning drift, and coverage blind spots in your own environment.
S3 cost optimization FAQs
What is cost optimization in S3?
S3 cost optimization is the practice of matching your storage setup to your actual usage. That means choosing the right storage class for each data type, using lifecycle policies to automatically move or expire objects, cleaning up versioning bloat, and avoiding unnecessary data transfer fees.
What is S3 Intelligent-Tiering cost optimization?
S3 Intelligent-Tiering cost optimization is an automated storage class that moves objects between frequent, infrequent, and archive tiers based on access patterns. It charges a monitoring fee of $0.0025 per 1,000 objects per month but has no retrieval fees and no minimum storage duration.
How much can S3 cost optimization save?
Savings depend on how much cold data is sitting in Standard, how much versioning bloat has accumulated, and how consistent your lifecycle coverage is. For cold data, moving from S3 Standard to lower-cost archive tiers can materially reduce storage costs. Eon can reduce backup storage overhead further through deduplication, compression, and incremental snapshots.
Will lifecycle rules affect objects already in my buckets?
Yes. Lifecycle rules apply to both new and existing objects. A rule that expires objects after 30 days will immediately queue any older objects for removal.
Test with a scoped prefix before applying rules to the full bucket.
What’s the difference between NoncurrentVersionExpiration and standard expiration?
The main difference between NoncurrentVersionExpiration and standard expiration is which version they target. Standard expiration removes the current version. NoncurrentVersionExpiration removes previous versions (the ones S3 creates whenever the current version is overwritten).
Versioned buckets need both rules. The second one prevents version history from causing your bill to increase indefinitely.
Is Glacier Deep Archive the cheapest option?
Not always. Glacier Deep Archive is the cheapest for storage at $0.00099/GB per month, but retrieval costs $0.02/GB at standard speed and takes up to 12 hours. It also enforces a 180-day minimum storage duration, so deleting data early triggers a pro-rated charge. It saves money only for data you won't need for at least six months.
How often should I review my S3 lifecycle rules?
S3 lifecycle rules need a quarterly review. S3 Storage Lens shows whether rules are hitting their intended targets, whether noncurrent version bytes are growing, and whether new workloads were added without lifecycle coverage since the last review.





