Every S3 storage class below Standard is cheaper per gigabyte and carries at least one of three catches: a minimum storage duration, a minimum billable object size, and a retrieval fee. Move the wrong data and you pay more than you did before, on a bill that still shows you moved to a cheaper class.
What are the actual minimums?
From the comparison table in Understanding and managing Amazon S3 storage classes:
| Storage class | Min storage duration | Min billable object size | Retrieval fee |
|---|---|---|---|
| S3 Standard | None | None | No |
| S3 Intelligent-Tiering | None | None | No |
| S3 Standard-IA | 30 days | 128 KB | Per-GB |
| S3 One Zone-IA | 30 days | 128 KB | Per-GB |
| S3 Glacier Instant Retrieval | 90 days | 128 KB | Per-GB |
| S3 Glacier Flexible Retrieval | 90 days | see below | Per-GB |
| S3 Glacier Deep Archive | 180 days | see below | Per-GB |
Note the top two rows. Intelligent-Tiering is the only class below Standard with no minimum duration, no minimum billable size and no retrieval fee. That is the whole reason it exists, and it is why the analysis below mostly does not apply to it.
What does the minimum duration actually charge you?
Not a penalty at deletion. A full period's storage, whatever you do with the object. AWS states it for the IA classes: "if you delete an object before the end of the 30-day minimum storage duration period, you are charged for 30 days. Objects that are deleted, overwritten, or transitioned to a different storage class before 30 days will incur the normal storage usage charge plus a pro-rated charge for the remainder of the 30-day minimum."
Three words there matter more than the rest: deleted, overwritten, or transitioned. Overwriting counts. So does moving the object onward to another class. A lifecycle policy that hops Standard to Standard-IA at 30 days and then to Glacier at 60 days pays a full 30-day minimum in Standard-IA even though the object only lived there for 30 days, and if you tighten that second hop to 45 days you start paying for storage you are not using.
The failure mode to watch for is a workload that rewrites objects in place. Every overwrite restarts the clock and bills the remainder of the old one. On frequently rewritten data, an IA class can cost more than Standard while showing a lower per-GB rate.
Why does a 20 KB object cost the same as a 128 KB one?
Because AWS rounds it up. "The S3 Standard-IA and S3 One Zone-IA storage classes are suitable for objects larger than 128 KB that you plan to store for at least 30 days. If an object is less than 128 KB, Amazon S3 charges you for 128 KB."
Work that through. A 20 KB object billed at 128 KB is charged for 6.4 times the bytes it occupies. If the IA rate is roughly half the Standard rate, that object costs about three times more in Standard-IA than it did in Standard. The break-even is at 128 KB, and every object under it loses.
Before assuming this is happening to you, though, check the date on your lifecycle configuration, because AWS closed most of this hole and the fix is not retroactive.
Doesn't S3 block small transitions now?
Mostly, yes, and this is the part that makes older advice on the subject wrong. Per Transitioning objects using Amazon S3 Lifecycle: "Amazon S3 applies a default behavior to S3 Lifecycle configurations that prevents objects smaller than 128 KB from being transitioned to any storage class."
AWS gives the reason in the same paragraph, and it is a different cost from the rounding one: "we don't recommend transitioning objects less than 128 KB because you are charged a transition request for each object. This means, for smaller objects, the transition costs can outweigh the storage savings."
The catch is which configurations that default applies to:
| Configuration | Behaviour for objects under 128 KB |
|---|---|
| Created or edited since September 2024 | "Prevents objects smaller than 128 KB from being transitioned to any storage class" |
| Created before September 2024 and never modified | Retains the old behaviour: small objects "transitioned only to the S3 Glacier Flexible Retrieval and S3 Glacier Deep Archive storage classes" |
So the exposure is now specific rather than general. An untouched rule written before September 2024 is still feeding small objects into exactly the two classes where the 40 KB metadata overhead below hurts most. AWS notes the switchover is triggered by editing: "if you create, edit, or delete rules, the default transition behavior for your configuration changes to the updated behavior."
You can also opt back into transitioning small objects deliberately, with an
ObjectSizeGreaterThan or ObjectSizeLessThan filter on
the rule, or the x-amz-transition-default-minimum-object-size
header on PutBucketLifecycleConfiguration. If someone added one of
those to "make the policy cover everything", that is worth a second look.
What else does a transition itself cost?
A request, per object. "Each object that you transition to the S3 Glacier Flexible Retrieval or S3 Glacier Deep Archive storage class constitutes one transition request. There is a cost for each such request. If you plan to transition a large number of objects, consider the request costs."
That charge is independent of object size, which is why it dominates for small objects: the storage saving scales with bytes, the request charge does not. AWS's own recommendation is the size filter: "if you are archiving a mix of objects that includes small objects, especially those under 128KB, we recommend using the lifecycle object size filter to filter out small objects from your transition to reduce request costs."
There is also a constraint on chaining rules that catches people building waterfalls: "you can't create a single Lifecycle rule that transitions objects from one storage class to another before the minimum storage duration period has passed." AWS's example is worth memorising because the arithmetic is not obvious:
"S3 Glacier Instant Retrieval has a minimum storage duration of 90 days. You can't specify a lifecycle rule that transitions objects to S3 Glacier Instant Retrieval after 4 days, and then transitions objects to S3 Glacier Deep Archive after 20 days. In this case the S3 Glacier Deep Archive transition must occur after at least 94 days."
Ninety-four, not ninety: the minimum runs from the transition, not from object creation. You can express the same thing as two separate rules, but AWS is explicit that "you pay the minimum duration storage charges" if you do.
One more timing subtlety that affects when the meter starts. Transitions to Glacier happen asynchronously, but billing does not wait: "you are charged at the destination storage class rate… starting from the date the lifecycle rule is satisfied, even if the physical transition has not yet occurred. The minimum storage duration charges… and the per-object storage overhead …also begin at this point. The only exception is transitions to S3 Intelligent-Tiering, where billing changes occur after the physical transition completes."
What is the 40 KB Glacier overhead?
A per-object metadata charge that applies on top of the object itself, and it is easy to miss because it lives in a footnote. For S3 Glacier Flexible Retrieval, AWS states it "requires 40 KB of additional metadata for each archived object. This includes 32 KB of metadata charged at the S3 Glacier Flexible Retrieval rate (required to identify and retrieve your data), and an additional 8 KB data charged at the S3 Standard rate."
S3 Glacier Deep Archive carries the same 40 KB structure, with its 32 KB charged at the Deep Archive rate.
The 8 KB at S3 Standard rate is the part that breaks the arithmetic. Deep Archive is the cheapest storage AWS sells, but every object in it drags 8 KB billed at the most expensive per-GB rate. Archive ten million small objects and you have quietly created 80 GB of S3 Standard storage that no lifecycle rule will ever move, plus 320 GB at the archive rate, on top of the data itself. For large objects this is a rounding error. For small ones it can exceed the object.
The fix is not to avoid archiving; it is to archive fewer, larger objects. That is AWS's own advice, not an inference: "if you are archiving small objects, consider these storage charges. Also consider aggregating many small objects into a smaller number of large objects to reduce overhead costs." It is the same reason flow logs are cheaper in Parquet than as many small text files.
When does Intelligent-Tiering make sense?
When you genuinely do not know the access pattern. AWS positions it exactly that way: it "is the ideal storage class when you want to optimize storage costs for data that has unknown or changing access patterns", and confirms "there are no retrieval fees for S3 Intelligent-Tiering."
The automatic tiers move on a documented schedule, with no action from you:
| Tier | Moves after | Access latency |
|---|---|---|
| Frequent Access | Default on upload | Milliseconds |
| Infrequent Access | 30 consecutive days of no access | Milliseconds |
| Archive Instant Access | 90 consecutive days of no access | Milliseconds |
| Archive Access (optional) | Min 90 days, configurable to 730 | 3–5 hours |
| Deep Archive Access (optional) | Min 180 days, configurable to 730 | ~12 hours |
All three automatic tiers deliver millisecond access, so the first 90 days of
tiering cost you nothing in latency. The two optional tiers do not: AWS warns
to "activate the Archive Access and Deep Archive Access tiers only if your
objects can be accessed asynchronously by your application", because retrieval
requires an explicit RestoreObject call. Turning those on for a
bucket serving live traffic converts a millisecond read into a multi-hour one.
The cost is a per-object fee rather than a per-GB one: "monitoring and automation fees per object apply." That inverts the usual economics. The saving scales with how many bytes you hold; the fee scales with how many objects. A bucket of many small objects can therefore pay more in monitoring than it recovers in tiering, and the rate is on the S3 pricing page rather than here, because it is exactly the sort of figure that goes stale.
What happens to objects under 128 KB?
Nothing, and that is the good news: "if the size of an object is less than 128 KB, it is not monitored and is not eligible for automatic tiering. Smaller objects are always stored in the Frequent Access tier."
So small objects in Intelligent-Tiering do not accrue the monitoring fee and do not get rounded up to 128 KB the way they would in Standard-IA. They simply sit in Frequent Access at that tier's rate. Compare that with a Standard-IA lifecycle rule, which bills them at 128 KB regardless. For a bucket with a long tail of small objects, Intelligent-Tiering is the safer default precisely because it declines to act on them.
What counts as "access"?
Less than you would guess, and the distinction decides whether your objects
ever tier down. AWS lists the operations that move an object back to Frequent
Access: console download or copy, CopyObject,
UploadPartCopy, Batch Replication, GetObject,
PutObject, RestoreObject and
CompleteMultipartUpload.
And the ones that do not:
-
HeadObject,GetObjectTagging,PutObjectTagging,ListObjects,ListObjectsV2,ListObjectVersions. So an inventory crawler or a tagging sweep does not reset anything, which is what you want. -
SelectObjectContent, which has genuinely surprising behaviour. AWS says it "doesn't constitute access that tiers objects up to a Frequent Access tier" and "doesn't prevent tiering objects down" through the automatic tiers, yet the same page states that if objects "are accessed throughSelectObjectContentbefore your specified number of days of no access… that action resets the timer" for the two optional archive tiers.
Read that twice if you use S3 Select. The same operation is invisible to automatic tiering and visible to archive tiering. A workload querying objects with Select will keep them out of Archive Access indefinitely while still allowing them to sink into Archive Instant Access, which is probably what you want but is nobody's default mental model.
What does restoring an archived object cost?
Two storage charges at once, for the duration of the restore. "When you restore an archive, you are paying for both the archive (S3 Glacier Flexible Retrieval or S3 Glacier Deep Archive rate) and a copy that you restored temporarily (S3 Standard storage rate)."
The archived object does not move. The restored copy is temporary and
"available only for the duration that you specify in the restore request", and
a HeadObject or GetObject call still reports the
Glacier storage class. So a restore of a large archive for a one-off audit
quietly adds Standard-rate storage for as long as you keep the copy alive,
which is a reason to specify the shortest duration that works rather than a
comfortable default.
Early deletion is prorated rather than a flat penalty: "if you delete or overwrite an archived object within the minimal duration period, Amazon S3 charges a prorated early deletion fee."
A decision order that avoids the traps
- Measure object size distribution first, not total bytes. Every minimum in the table above is per object. A bucket's average size tells you more about which class fits than its total does.
- Check when your lifecycle configuration was last edited. If it predates September 2024 and has not been touched, it is still on the old default and can transition sub-128 KB objects into the Glacier classes.
- If access is unpredictable, use Intelligent-Tiering. No minimum duration, no minimum billable size, no retrieval fee, and objects under 128 KB are left alone.
- If access is predictable and objects are large and long-lived, a lifecycle rule to an IA or Glacier class is cheaper, because you avoid the per-object monitoring fee entirely.
- Check the minimum duration against how long the data actually lives, including overwrites, before transitioning anything.
- Aggregate small objects before archiving. The 40 KB Glacier metadata overhead, 8 KB of it at Standard rate, is charged per object no matter how small the object is.
Then verify against the bill rather than the plan. Storage class transitions show up as their own usage types in the Cost and Usage Report, so you can confirm a lifecycle rule actually reduced spend instead of relocating it. If the total moved the wrong way, the usual reasons are in this post; if the CUR and the console disagree about the total, that is a different problem, covered in why Cost Explorer and your CUR disagree.