S3 Storage Classes Without the Alphabet Soup
Standard, IA, Glacier tiers, lifecycle rules, and the retrieval fees that ruin your day — how I stopped overpaying for object storage.
Last year we were paying for four years of application logs in S3 Standard — the most expensive default — because "s3 is cheap." It wasn't. The bill wasn't catastrophic, but it was pure waste: data nobody had queried in months, sitting at the premium rate.
S3 has too many storage classes, I'll give you that. But you really only need to understand three tiers plus one concept, and you can make sensible decisions forever.
The tiers that matter
S3 Standard — instant access, highest cost, for anything frequently read. Current data, active assets, things your app touches daily.
S3 Standard-Infrequent Access (IA) — cheaper storage, but you pay each time you read. Data you expect to touch maybe once a month. Minimum storage duration of 30 days, so don't put objects you'll delete tomorrow here.
S3 Glacier Instant Retrieval — archive pricing with millisecond access, for archival data you still need to grab occasionally (compliance copies, old project archives).
S3 Glacier Flexible Retrieval / Deep Archive — the cheap seats. Retrieval takes minutes to hours (or up to 12 hours for Deep Archive). This is for "we might need this someday" data.
There's also Intelligent-Tiering, which auto-moves objects between frequent and infrequent tiers based on access patterns for a small monitoring fee. For workloads with unpredictable access, I've found it worth it; for predictable workloads, explicit lifecycle rules are cheaper and easier to reason about.
Lifecycle rules do the work for you
The whole game is writing a lifecycle configuration once and forgetting about it. Example for logs:
{
"Rules": [
{
"ID": "logs-rollup",
"Status": "Enabled",
"Filter": { "Prefix": "logs/" },
"Transitions": [
{ "Days": 30, "StorageClass": "STANDARD_IA" },
{ "Days": 90, "StorageClass": "GLACIER" },
{ "Days": 365, "StorageClass": "DEEP_ARCHIVE" }
],
"Expiration": { "Days": 1095 }
}
]
}
That one rule moved our logs from always-premium to a sensible ladder and eventually deletes them after three years — matching our retention policy. Zero ongoing effort.
The gotchas nobody mentions in the marketing page
Retrieval fees are the trap. Moving data to Glacier saves you monthly pennies; if your job then bulk-retrieves it during an incident, you pay for the privilege. Know the retrieval cost before you transition anything critical.
Minimum duration charges. IA has a 30-day minimum, Glacier tiers have 90 or 180. Objects uploaded and deleted inside that window still bill for the minimum.
Request costs add up on tiny objects. Millions of 2KB objects with frequent LIST/GET requests can cost more in request fees than in storage. Sometimes the right answer is packing small objects together, not changing storage class.
Delete costs are real too. Early delete penalties exist on IA and Glacier; read your bill line items the first month after a big lifecycle rollout.
Versioning changes the math. Non-current versions accumulate quietly. Pair lifecycle rules with noncurrent version expiration or you'll pay for every historical copy forever.
What I check quarterly
- Bucket metrics via S3 Storage Lens (it's built into the console and honestly underrated).
- Buckets where >90% of objects haven't been touched in 90 days — those want lifecycle rules.
- Any bucket named something like
tmp,test, orbackup-old— they always contain 2019.
The simple policy I follow
Write-once, rarely-read data gets a lifecycle rule in the same PR that creates the bucket. I don't let buckets exist without one, even for side projects. Future-me, staring at a cost explorer graph at 11 PM, will thank present-me — and so will whoever inherits this account after I move teams.