S3 Cost Optimisation

S3 looks cheap at $0.023/GB until you have a few hundred terabytes, versioning turned on, and multipart uploads accumulating in the background. S3 cost optimisation is about matching storage class to access frequency and eliminating the invisible accumulation charges.

S3 storage classes and their costs

S3 offers multiple storage classes optimised for different access patterns. Choosing the right class for each category of data is the primary lever for S3 cost reduction.

Storage classStorage cost (US)Retrieval costMinimum storage durationBest for
Standard$0.023/GBFreeNoneFrequently accessed data
Intelligent-Tiering$0.023–$0.0036/GBFree (frequent tier)NoneUnknown or variable access patterns
Standard-IA$0.0125/GB$0.01/GB retrieved30 daysInfrequently accessed, rapid retrieval
One Zone-IA$0.01/GB$0.01/GB retrieved30 daysInfrequently accessed, non-critical, single AZ
Glacier Instant Retrieval$0.004/GB$0.03/GB retrieved90 daysArchives accessed once/quarter
Glacier Flexible Retrieval$0.0036/GB$0.01/GB (bulk)90 daysArchives with hours-acceptable retrieval
Glacier Deep Archive$0.00099/GB$0.02/GB180 daysLong-term compliance archives, rarely accessed

For data accessed daily: Standard. For data accessed monthly: Standard-IA. For data accessed once a year or for compliance: Glacier. The savings between Standard and Glacier Deep Archive are dramatic: $0.023 vs $0.00099/GB — a 95% reduction.

For 100TB of data:

  • Standard: $2,300/month
  • Standard-IA: $1,250/month
  • Glacier Deep Archive: $99/month

Lifecycle policies: automatic cost management

S3 lifecycle policies automatically transition objects to cheaper storage classes or delete them after a specified period. Without lifecycle policies, objects accumulate in Standard storage indefinitely.

A common lifecycle policy for a web application log bucket:

  • After 30 days: transition to Standard-IA
  • After 90 days: transition to Glacier Instant Retrieval
  • After 365 days: transition to Glacier Deep Archive
  • After 2557 days (7 years): delete

Here’s the lifecycle policy JSON:

{
  "Rules": [
    {
      "ID": "log-retention-policy",
      "Status": "Enabled",
      "Filter": {
        "Prefix": "logs/"
      },
      "Transitions": [
        {
          "Days": 30,
          "StorageClass": "STANDARD_IA"
        },
        {
          "Days": 90,
          "StorageClass": "GLACIER_IR"
        },
        {
          "Days": 365,
          "StorageClass": "DEEP_ARCHIVE"
        }
      ],
      "Expiration": {
        "Days": 2557
      }
    }
  ]
}

Apply it with the CLI:

aws s3api put-bucket-lifecycle-configuration \
  --bucket my-log-bucket \
  --lifecycle-configuration file://lifecycle.json

Also see S3 lifecycle policies for full configuration options.

S3 Intelligent-Tiering

If you can’t predict the access pattern for your objects, S3 Intelligent-Tiering removes the guesswork. It monitors access patterns and automatically moves objects between:

  • Frequent Access tier — same cost as Standard
  • Infrequent Access tier — 40% cheaper, objects not accessed for 30 days
  • Archive Instant Access tier — 68% cheaper, objects not accessed for 90 days (optional)
  • Archive Access tier — 95% cheaper, objects not accessed for 180 days (optional, with async retrieval)

There is a monitoring charge of $0.0025 per 1,000 objects/month. For small objects (under 128KB), this monitoring charge can exceed the storage savings — Intelligent-Tiering is most cost-effective for larger files.

For large datasets where access patterns shift over time (analytics data, ML training sets, media libraries), Intelligent-Tiering often saves 30–50% without any manual intervention.

Versioning costs and how to control them

S3 versioning is valuable for protecting against accidental deletion and enabling rollback. But it doubles (or more) your storage costs if you overwrite objects frequently, because every version is retained.

Scenario: a database backup that runs nightly and uploads a 10GB dump to S3 with versioning enabled and no lifecycle rule. After one month: 30 versions × 10GB = 300GB stored. At $0.023/GB = $6.90/month — just in accumulated backup versions of the same data.

After one year: 365 versions × 10GB = 3,650GB = $83.95/month.

The fix is a lifecycle rule to expire non-current versions:

{
  "Rules": [
    {
      "ID": "expire-old-versions",
      "Status": "Enabled",
      "NoncurrentVersionExpiration": {
        "NoncurrentDays": 30
      }
    }
  ]
}

This deletes versions older than 30 days, capping storage at approximately 30 versions rather than unlimited accumulation.

Delete markers also accumulate in versioned buckets. When you “delete” an object in a versioned bucket, S3 creates a delete marker instead of actually removing the object. Old delete markers themselves are objects — add an expiration rule for delete markers too:

{
  "ExpiredObjectDeleteMarker": true
}

Incomplete multipart uploads

S3 multipart upload is used for large files — it splits a large object into chunks that are uploaded in parallel. If a multipart upload is interrupted (connection drop, application crash), the parts that were already uploaded remain in S3 and are charged as storage, even though no complete object exists.

These incomplete uploads are not visible in the S3 console object list — you have to specifically query for them:

aws s3api list-multipart-uploads --bucket my-bucket

To abort all incomplete multipart uploads older than 7 days, add this to your bucket lifecycle policy:

{
  "AbortIncompleteMultipartUpload": {
    "DaysAfterInitiation": 7
  }
}

This is one of the most commonly missed S3 cost items. Buckets with high upload activity can accumulate gigabytes of incomplete upload parts over months, all silently charged.

Request and data retrieval costs

S3 charges per request:

  • PUT, COPY, POST, LIST: $0.005 per 1,000 requests
  • GET, SELECT: $0.0004 per 1,000 requests
  • DELETE: free

For high-volume upload pipelines, batching small files before uploading reduces PUT request costs. 1,000 small uploads at $0.005/thousand = $0.005 vs one upload of a compressed archive = $0.000005. If you’re uploading millions of small objects, consider aggregating them first.

Data retrieval charges apply for Standard-IA, Glacier IR, and Deep Archive. If you move data to Standard-IA expecting to access it rarely but actually access it frequently, the retrieval charges can exceed the storage savings. Use S3 Storage Lens or S3 Inventory to validate your access pattern assumptions before transitioning data.

Egress costs and using CloudFront

S3 data transfer out to the internet: approximately $0.09/GB. S3 data transfer to CloudFront: free. CloudFront to internet: $0.0085–$0.085/GB depending on volume and region.

For any public-facing content served from S3, routing through CloudFront is almost always cheaper at significant volume — plus you get caching, lower latency, and HTTPS. The break-even is around 1GB of transfer per month.

To check your current S3 data transfer costs, go to Cost Explorer, filter by service = S3, and group by Usage Type. Look for items labelled “DataTransfer-Out-Bytes” — that’s your egress bill.

See S3 storage classes and S3 overview for additional context.

S3 Inventory for understanding what you have

For large S3 buckets, you need to know what’s stored before you can optimise it. S3 Inventory generates a daily or weekly CSV or ORC report of all objects in a bucket, including size, storage class, last modified date, and encryption status.

aws s3api put-bucket-inventory-configuration \
  --bucket my-bucket \
  --id my-inventory \
  --inventory-configuration '{
    "Id": "my-inventory",
    "IsEnabled": true,
    "Destination": {
      "S3BucketDestination": {
        "Bucket": "arn:aws:s3:::my-inventory-bucket",
        "Format": "CSV"
      }
    },
    "Schedule": {"Frequency": "Daily"},
    "IncludedObjectVersions": "All",
    "OptionalFields": ["Size", "StorageClass", "LastModifiedDate"]
  }'

With the inventory, you can query it using Athena to find, for example, all objects larger than 1GB that haven’t been accessed in 90 days and are still in Standard storage — prime candidates for a lifecycle transition.

Note: The 30-day and 90-day minimum storage durations for Standard-IA and Glacier classes mean that transitioning an object to Standard-IA and then deleting it after 10 days incurs a 30-day minimum storage charge. Only transition objects you intend to keep for at least the minimum period. Short-lived objects should stay in Standard.

Common mistakes

  1. Enabling versioning without a lifecycle rule — Versioning without expiry means every overwrite accumulates a new version indefinitely. Always pair versioning with a non-current-version expiration lifecycle rule.
  2. Not cleaning up incomplete multipart uploads — Incomplete uploads accumulate silently and are charged. Add an AbortIncompleteMultipartUpload rule to every bucket that receives large file uploads.
  3. Transitioning small objects to Standard-IA — Standard-IA has a 128KB minimum billable object size. A 1KB file stored in Standard-IA is charged as 128KB. For buckets of small objects, Standard or Intelligent-Tiering is usually cheaper.
  4. Not using CloudFront for public S3 content — Serving S3 content directly to the internet at $0.09/GB when CloudFront charges less and also improves performance is a common and easy-to-fix oversight.
  5. Assuming retrieved data is free because you already paid for storage — Standard-IA, Glacier IR, and Glacier all charge per GB retrieved. Model retrieval costs before choosing a storage class, especially if you might need to bulk-restore data.

Summary

  • Glacier Deep Archive at $0.00099/GB is 95% cheaper than Standard — use it for compliance archives
  • S3 lifecycle policies automatically transition objects to cheaper classes as they age
  • Intelligent-Tiering removes guesswork for variable access patterns — best for objects over 128KB
  • Versioning without expiration rules causes unbounded storage growth — always set non-current-version expiry
  • Incomplete multipart uploads accumulate as silent storage charges — abort them after 7 days via lifecycle
  • Route S3 public content through CloudFront — S3 to CloudFront is free, and CloudFront egress is cheaper than direct S3 egress
  • Use S3 Inventory to understand what’s stored before designing lifecycle policies

Frequently asked questions

What is S3 Intelligent-Tiering?

S3 Intelligent-Tiering automatically moves objects between frequent-access and infrequent-access tiers based on access patterns. Objects not accessed for 30 days move to the lower-cost tier automatically. There is a small monitoring charge per object ($0.0025 per 1,000 objects), so it is most cost-effective for objects larger than 128KB.

Does S3 versioning cost extra?

Yes. When versioning is enabled, every overwrite creates a new version that is stored in addition to the current version. If a 1GB file is updated daily, after 30 days you have 30GB of stored versions. Set lifecycle rules to expire old versions on a schedule to prevent unbounded storage growth.

What does it cost to transfer data from S3 to the internet?

S3 data transfer out to the internet costs approximately $0.09/GB for the first 10TB/month from US regions. Transfer from S3 to CloudFront is free, and CloudFront to internet is cheaper than S3 to internet direct — routing through CloudFront saves money at high transfer volumes.

Last verified: 19 March 2026 Cloud services change frequently. Verify details against official documentation before making infrastructure decisions.