Guides · S3
In US East (N. Virginia), S3 Standard costs $0.023 per GB-month for the first 50 TB. Three things quietly add to it: data in Standard that nobody reads, parts of multipart uploads that never finished, and old versions of objects in versioned buckets. All three are billed as storage, and lifecycle rules can deal with all three. The cheaper storage classes have catches, though: minimum storage durations, per-object fees and retrieval charges.
| Storage class | Storage, per GB-month | Minimum storage duration | Minimum billable object size | Retrieval, per GB |
|---|---|---|---|---|
| S3 Standard | $0.023 (first 50 TB) $0.022 (next 450 TB) $0.021 (over 500 TB) | None | None | None |
| S3 Intelligent-Tiering | $0.023 / $0.0125 / $0.004 by access tier, plus $0.0025 per 1,000 monitored objects | None | None | None |
| S3 Standard-IA | $0.0125 | 30 days | 128 KB | $0.01 |
| S3 One Zone-IA | $0.01 | 30 days | 128 KB | $0.01 |
| S3 Glacier Instant Retrieval | $0.004 | 90 days | 128 KB | $0.03 |
| S3 Glacier Flexible Retrieval | $0.0036 | 90 days | 40 KB added per object | $0.01 standard, free bulk |
| S3 Glacier Deep Archive | $0.00099 | 180 days | 40 KB added per object | $0.02 standard, $0.0025 bulk |
A lifecycle configuration is a set of rules on a bucket. Each rule picks objects (by prefix, tag or size) and either moves them to a cheaper class after a number of days (a transition) or deletes them (an expiration). Rules apply to objects already in the bucket as well as new ones. Expirations are free: you aren't charged for the expiration or for storage after an object expires.
Transitions are not free. Each object moved is one request:
| Transition into | Per 1,000 objects |
|---|---|
| Standard-IA, One Zone-IA or Intelligent-Tiering | $0.01 |
| Glacier Instant Retrieval | $0.02 |
| Glacier Flexible Retrieval | $0.03 |
| Glacier Deep Archive | $0.05 |
That's why the object count matters as much as the size. Take 1,000 GB stored as one million 1 MB objects:
| Where it's stored | Storage per month | One-time transition | Reading it all back once |
|---|---|---|---|
| S3 Standard | $23.00 | $0.40 in GET requests | |
| S3 Standard-IA | $12.50 | $10 | $10 retrieval + $1 in GET requests |
| S3 Glacier Instant Retrieval | $4.00 | $20 | $30 retrieval + $10 in GET requests |
| S3 Glacier Deep Archive | $0.99 + about $0.22 overhead | $50 | $20 retrieval + $100 in restore requests, plus Standard storage for the restored copies |
If the same 1,000 GB were 100 million 10 KB objects, moving them to Standard-IA would cost $1,000 in transition requests, and each object would then be billed as 128 KB. That's why, by default, lifecycle rules don't transition objects smaller than 128 KB. Objects that are read often belong in Standard; colder classes pay off for data that's rarely read and kept past the minimum duration.
Large files are often uploaded in parts. Until the upload is completed or stopped, S3 keeps every part and bills it as storage in the class the upload was headed for. A failed or abandoned upload can leave parts in the bucket indefinitely. They aren't objects, so look for them with list-multipart-uploads or Storage Lens rather than an object listing. They do count in the bucket's size.
AWS recommends a lifecycle rule with the AbortIncompleteMultipartUpload action as a best practice. It stops uploads that haven't completed within a set number of days after they started, and deletes their parts. It doesn't touch completed objects, and deleting the parts has no early-deletion charge in any storage class.
With versioning on, overwriting an object keeps the old one as a noncurrent version, and deleting an object only adds a delete marker. Every version is a full copy, billed at normal rates: three versions of a 1 GB file is 3 GB of storage. An expiration rule on a versioned bucket doesn't delete anything either; it makes the current version noncurrent.
To remove old versions, use the NoncurrentVersionExpiration action. It permanently deletes versions a set number of days after they became noncurrent, and can keep a number of the newest noncurrent versions (1 to 100) regardless of age. Deleted versions can't be recovered, so decide how far back you need to be able to restore first. The ExpiredObjectDeleteMarker option cleans up delete markers that have no versions left behind them.
Intelligent-Tiering moves each object between access tiers based on when it was last read: to Infrequent Access ($0.0125) after 30 days without access, and to Archive Instant Access ($0.004) after 90 days. Reading an object moves it back to Frequent Access ($0.023). There are no retrieval fees. Two archive tiers are optional: Archive Access ($0.0036, after at least 90 days) and Deep Archive Access ($0.00099, after at least 180 days). Objects in those must be restored before reading: a standard restore takes 3 to 5 hours from Archive Access and up to 12 hours from Deep Archive Access.
The catch is the monitoring and automation fee: $0.0025 per 1,000 objects a month. Objects smaller than 128 KB aren't monitored, aren't charged the fee, and always stay in Frequent Access. For the rest, the fee is per object, while the saving is per GB:
Intelligent-Tiering suits buckets of larger objects with unknown or changing access patterns. For millions of small objects, or data that's read constantly, it can cost more than Standard.
1. What S3 costs you: Billing and Cost Management → Cost Explorer, filtered to S3 and grouped by Usage type. Usage types ending in TimedStorage-ByteHrs are Standard storage, TimedStorage-SIA-ByteHrs Standard-IA and Monitoring-Automation-INT the Intelligent-Tiering monitoring fee. Usage types containing EarlyDelete are minimum-duration charges.
2. Old versions and stale uploads across the account: S3 console → Storage Lens → the default dashboard (default-account-dashboard). Its free metrics include Noncurrent version bytes and Incomplete multipart upload storage bytes greater than 7 days old.
3. One bucket (read-only commands):
# lifecycle rules, if any, and whether versioning is on
aws s3api get-bucket-lifecycle-configuration --bucket my-bucket
aws s3api get-bucket-versioning --bucket my-bucket
# multipart uploads that were started and never completed or stopped
aws s3api list-multipart-uploads --bucket my-bucket \
--query 'Uploads[].[Key,Initiated]' --output table
# bytes in Standard, from CloudWatch's daily storage metric (use the bucket's region)
aws cloudwatch get-metric-statistics --region us-east-1 --namespace AWS/S3 \
--metric-name BucketSizeBytes \
--dimensions Name=BucketName,Value=my-bucket Name=StorageType,Value=StandardStorage \
--start-time 2026-09-30T00:00:00Z --end-time 2026-10-03T00:00:00Z \
--period 86400 --statistics Average --output table
BucketSizeBytes includes noncurrent versions and the parts of incomplete multipart uploads, so it can be larger than the total of the objects you can see. Other StorageType values include StandardIAStorage, GlacierInstantRetrievalStorage and IntelligentTieringFAStorage.
Console: open the bucket → Management → Create lifecycle rule. Or with the CLI. This example aborts multipart uploads still incomplete after 7 days, deletes noncurrent versions 30 days after they become noncurrent while keeping the newest 3, and moves objects under logs/ to Standard-IA after 30 days. The numbers are examples; choose your own.
{
"Rules": [
{ "ID": "abort-incomplete-uploads", "Status": "Enabled", "Filter": { "Prefix": "" },
"AbortIncompleteMultipartUpload": { "DaysAfterInitiation": 7 } },
{ "ID": "expire-noncurrent-versions", "Status": "Enabled", "Filter": { "Prefix": "" },
"NoncurrentVersionExpiration": { "NoncurrentDays": 30, "NewerNoncurrentVersions": 3 } },
{ "ID": "logs-to-standard-ia", "Status": "Enabled", "Filter": { "Prefix": "logs/" },
"Transitions": [ { "Days": 30, "StorageClass": "STANDARD_IA" } ] }
]
}
Then (this one changes your bucket):
aws s3api put-bucket-lifecycle-configuration --bucket my-bucket \
--lifecycle-configuration file://lifecycle.json
This replaces the bucket's whole lifecycle configuration. If the bucket already has rules, copy them into the file first, or they'll be lost. A version is only deleted once it's both older than NoncurrentDays and beyond the NewerNoncurrentVersions count.
The free checkup finds this and 20 other kinds of issues automatically. For S3 it checks one thing. For each bucket (up to 300), it reads whether the bucket has a lifecycle configuration and how many bytes it holds in S3 Standard, from CloudWatch's daily storage metric. It lists Standard buckets of 100 GB or more that have no lifecycle configuration at all, and estimates the saving at 20% of their Standard storage cost ($0.023 per GB-month, roughly adjusted for the bucket's region), marked low confidence. It doesn't look inside a lifecycle configuration, so a bucket with any rule passes. It doesn't separately measure incomplete multipart uploads, noncurrent versions, other storage classes, the Intelligent-Tiering monitoring fee, or request and retrieval charges. It never reads your objects.
TimedStorage-ByteHrs $0.023/$0.022/$0.021 per GB-month; TimedStorage-INT-FA-ByteHrs $0.023, -INT-IA- $0.0125, -INT-AIA- $0.004, -INT-AA- $0.0036, -INT-DAA- $0.00099; Monitoring-Automation-INT $0.0025 per 1,000 objects; TimedStorage-SIA-ByteHrs $0.0125, -ZIA- $0.01, -GIR- $0.004, TimedStorage-GlacierByteHrs $0.0036; retrieval Retrieval-SIA and Retrieval-ZIA $0.01, Retrieval-GIR $0.03, Glacier Flexible Retrieval standard $0.01 and bulk $0.00 per GB; requests Requests-Tier1 $0.005 and Requests-Tier2 $0.0004 per 1,000, Requests-SIA-Tier2 $0.001 and Requests-GIR-Tier2 $0.01 per 1,000; lifecycle transitions $0.01 per 1,000 into Standard-IA, One Zone-IA and Intelligent-Tiering, $0.02 into Glacier Instant Retrieval, $0.03 into Glacier Flexible Retrieval, $0.05 into Deep Archive. AWS Price List API, S3 Glacier Deep Archive offer for us-east-1, version published September 11, 2026: TimedStorage-GDA-ByteHrs $0.00099 per GB-month, standard retrieval $0.02 and bulk $0.0025 per GB, standard restore requests $0.10 per 1,000.NoncurrentVersionExpiration 1 to 100 versions), Examples of S3 Lifecycle configurations (both NoncurrentDays and NewerNoncurrentVersions must be exceeded; the logs/ example), PutBucketLifecycleConfiguration (replaces an existing configuration)NonCurrentVersionStorageBytes and IncompleteMPUStorageBytesOlderThan7Days), Amazon S3 Storage Lens metrics and features (default dashboard), Metrics and dimensions (BucketSizeBytes includes noncurrent versions and incomplete multipart upload parts; StorageType values)Prices are AWS list prices for US East (N. Virginia), checked October 3, 2026. Other regions differ. Examples use 1 GB = 1,000 MB for simplicity.