GreenOps

© 2026 GreenOps. All rights reserved.

npx greenops-scanBlogPrivacy PolicyTermsRefund PolicyMethodology
GreenOps
ProblemSolutionPricingBlognpx greenops-scan
Sign in / Sign upFree audit
← Back to blog
GreenOps dashboard preview

S3 Lifecycle Policies: The Cheapest Storage Cost Win You’re Ignoring

The storage bill nobody reads

S3 is where the cloud’s cheapest storage story starts. But “cheap” can become surprisingly expensive when the data just sits there forever.

Most teams set up an S3 bucket for a clear purpose: logs, backups, uploads, archives. Over time the bucket fills up. Object counts grow. Storage classes stay in the default Standard tier. Old versions pile up. Incomplete multipart uploads linger. And the monthly cost slowly becomes one of those background expenses nobody questions.

A few lifecycle rules can fix most of this automatically. The problem is that many buckets were created by someone who has since left the company, and the rules were either never added or were added for one use case and never generalized.

What an S3 lifecycle policy actually does

A lifecycle rule is an S3-level instruction that moves or deletes objects based on age and prefix or tag conditions.

Common actions:

  • Transition objects to a cheaper storage class after a number of days
    • Standard → Standard-IA after 30 days
    • Standard-IA → Glacier Instant Retrieval after 90 days
    • Glacier Instant Retrieval → Glacier Flexible Retrieval or Deep Archive after 1–2 years
  • Expire old versions of objects
  • Delete incomplete multipart uploads after a few days
  • Delete entire objects after a defined retention period

The cost differences are meaningful. For infrequently accessed data, Standard-IA is roughly half the storage price of Standard. Glacier Flexible Retrieval is a small fraction. Deep Archive is the cheapest long-term option. The key is matching the transition to how the data is actually used.

The three biggest S3 money leaks

1. Data in Standard storage that is rarely accessed

A log bucket, an old dataset, or a media archive sitting in S3 Standard is usually over-provisioned. If an object has not been accessed in 30 or 90 days, it is a candidate for Standard-IA or Glacier.

2. Versioning without an expiration rule

S3 versioning is a good safety feature. Without an expiration rule, every update creates a permanent old copy. Buckets that hold changing data can accumulate hundreds of versions per object. The current version might be small; the version stack can be many times larger.

3. Incomplete multipart uploads

When a multipart upload fails or is abandoned, the uploaded parts stay in S3. They are not visible in the default console view, but they are billed. A rule that deletes incomplete multipart uploads after 7 days almost always saves money and causes no harm.

How to audit your own buckets

List buckets and check which have lifecycle rules:

aws s3api list-buckets --query 'Buckets[*].Name' --output table
aws s3api get-bucket-lifecycle-configuration --bucket <your-bucket>

If the second command returns an error, the bucket has no lifecycle rules. That is not always bad, but it is a signal to investigate.

Check storage class distribution:

aws s3api list-objects-v2 \
  --bucket <your-bucket> \
  --query 'Contents[*].{Key:Key,Size:Size,StorageClass:StorageClass}' \
  --output table

For a larger bucket, use S3 Storage Lens or run a report via CloudWatch. You are looking for:

  • High object counts with no lifecycle rules
  • A large share of data in Standard storage
  • Buckets older than a few months with heavy version counts
  • Buckets with no incomplete-multipart cleanup

Example lifecycle rules that cover most cases

A sensible default for a log or backup bucket:

aws s3api put-bucket-lifecycle-configuration \
  --bucket <your-bucket> \
  --lifecycle-configuration file://lifecycle.json

lifecycle.json:

{
  "Rules": [
    {
      "ID": "cleanup-incomplete-multipart",
      "Status": "Enabled",
      "Filter": {},
      "AbortIncompleteMultipartUpload": {
        "DaysAfterInitiation": 7
      }
    },
    {
      "ID": "transition-old-objects",
      "Status": "Enabled",
      "Filter": {},
      "Transitions": [
        { "Days": 30, "StorageClass": "STANDARD_IA" },
        { "Days": 90, "StorageClass": "GLACIER_IR" }
      ]
    },
    {
      "ID": "expire-noncurrent-versions",
      "Status": "Enabled",
      "Filter": {},
      "NoncurrentVersionExpiration": {
        "NoncurrentDays": 30
      }
    }
  ]
}

This is a starting point, not a universal rule. A bucket that serves user uploads daily should not be treated like an annual compliance archive. Adjust transitions to match actual access patterns.

How GreenOps Scan can help

The s3 module in the free GreenOps Scan CLI flags:

  • buckets with no lifecycle configuration
  • buckets with versioning enabled but no noncurrent-version expiration
  • large buckets that are still primarily in Standard storage
  • buckets with suspicious storage growth over time

Run it on a dev or sandbox account:

npx greenops-scan --profile dev --region us-east-1 --modules s3

Or use the Docker image:

docker run --rm -it \
  -v ~/.aws:/home/greenops/.aws:ro \
  -v "$(pwd)/reports:/reports" \
  -e AWS_PROFILE=dev \
  spidgorny/greenops-scan:latest \
  --region us-east-1 --modules s3

Each finding includes the bucket name, the issue, and a confidence level. Review the recommendations before applying rules, especially in production accounts.

What to expect from cleanup

A single bucket with a few terabytes of logs or backups can move from a few hundred dollars per month in Standard storage to a fraction of that in Glacier. The exact saving depends on:

  • how much data is infrequently accessed
  • how aggressively you can move data to colder tiers
  • how long versions and incomplete uploads have been accumulating
  • your data-retention and compliance requirements

Savings are recurring. Once the lifecycle rules are in place, they continue to move and delete data automatically. That is the real win: set the rule once, and the bill improves every month.

Start with the obvious buckets

  1. List your buckets and identify the oldest, largest ones.
  2. Check whether they have lifecycle rules and versioning rules.
  3. Look at actual access patterns in S3 access logs or Storage Lens.
  4. Add a lifecycle rule for incomplete multipart uploads first — it is always safe.
  5. Add transition and version-expiration rules for infrequently accessed data.
  6. Review after 30 days to confirm the storage class mix is shifting.

S3 is one of the few AWS services where a small configuration change can cut costs for years. It is worth a 30-minute audit.

Tags: #AWS #CloudCostOptimization #FinOps #S3 #StorageCost #LifecyclePolicy #CloudWaste #GreenOps

More posts

View all posts →

Every Finding Now Tells You Which Team, Project, and Stack Owns It

GreenOps Scan now attaches each flagged resource's AWS tags — Project, Environment, Team, even the exact CloudFormation stack and logical ID — straight into every finding, no new IAM permissions required.

Stop Pasting AWS CLI Commands Into Prod — Let Your Agent Fix the Code Instead

GreenOps Scan's new --llm-prompts flag turns every finding into a copy/paste prompt for Copilot, Claude, or Cursor to fix in your actual Terraform/CDK/CloudFormation source — no more IaC drift from hand-run remediation commands.

The Bill You Don't See Coming: AWS Extended Support Surcharges

GreenOps Scan now proactively detects deprecated Lambda runtimes, RDS/Aurora/DocumentDB engines, EKS versions, ElastiCache Redis, and OpenSearch domains before AWS starts billing you extra for staying on them.