14.3 Lifecycle Rules: Transitioning and Expiring Objects by Age or Prefix
Right, so you’ve got your data in S3. Great. But unless you’re made of money and enjoy watching your CFO have an aneurysm, you can’t just leave every single file on the expensive, high-performance storage tier forever. This is where lifecycle rules come in. Think of them as your automated, hyper-efficient storage janitor. They quietly go about their business, moving things to cheaper storage or taking out the trash, all so you don’t have to.
Lifecycle rules are fundamentally about two actions: transitioning (moving an object to a different storage class) and expiring (permanently deleting an object). You configure these actions to fire based on either the object’s age or its key prefix (i.e., its “folder” path). The beauty is in the automation; set it and forget it (but, you know, maybe check in on it once in a while).
The Two Levers: Transitions and Expirations
A transition action moves an object from one storage class to a cheaper one. The classic, no-brainer path is Standard -> Standard-IA (Infrequent Access) -> Glacier Flexible Retrieval (or Deep Archive). You’re trading retrieval speed and cost for drastically cheaper storage costs. The important thing to remember is that these transitions have minimum ages. You can’t move an object to Glacier 5 seconds after you upload it; AWS isn’t running a charity. The minimums exist because they’ve crunched the numbers and need to ensure they make their cut on the initial storage period.
An expiration action is exactly what it sounds like: it permanently deletes the object and its metadata. It’s the digital shredder. This is perfect for things like temporary build artifacts, raw logs that you only need for 30 days, or that embarrassing company-wide meme that HR definitely does not have a copy of.
Configuring the Janitor’s Schedule
You define these rules in a JSON policy, either attached directly to a bucket or, more commonly these days, managed through the S3 Lifecycle configuration in the console, CloudFormation, or Terraform. The structure is logical but has a few quirks.
Here’s a realistic example. Let’s say you have an uploads/ prefix where users drop files. You want to move them to colder storage after 30 days and delete them after a year. You also have logs/ that you only want to keep for 90 days.
{
"Rules": [
{
"ID": "MoveUserUploadsToGlacier",
"Status": "Enabled",
"Filter": {
"Prefix": "uploads/"
},
"Transitions": [
{
"Days": 30,
"StorageClass": "STANDARD_IA"
},
{
"Days": 60,
"StorageClass": "GLACIER"
}
],
"Expiration": {
"Days": 365
}
},
{
"ID": "ExpireLogsAfter90Days",
"Status": "Enabled",
"Filter": {
"Prefix": "server-logs/"
},
"Expiration": {
"Days": 90
}
}
]
}
The Devilish Details and “Wait, What?” Moments
This is where the “knowledgeable friend” part kicks in. The AWS docs will tell you the how, but I’m here to tell you the why and the “watch out for that!”
It’s All About Object Age: The clock starts ticking from the object’s creation date. Not when you add the rule, not when it was last modified. This is a crucial distinction. If you upload a 100-day-old object, a rule set to expire at 30 days will delete it immediately. The system isn’t smart enough to know the file’s actual content age; it only cares about when it showed up in the bucket.
The Great Inconsistency Conundrum: Lifecycle rules run once a day. Just once. So if you set an expiration for 30 days, it might happen sometime on day 30. Or maybe in the early hours of day 31. You cannot rely on millisecond precision here. Never, ever use S3 lifecycle for mission-critical, time-sensitive deletion. It’s a janitor, not a bomb disposal robot.
Versioning Will Complicate Your Life: If you have versioning enabled (and you should), you need to think about what happens to noncurrent versions. The previous example only acts on the current version of the object. To clean up old versions, you need a separate set of rules specifically for them. This is a common pitfall.
{
"Rules": [
{
"ID": "CleanUpOldVersions",
"Status": "Enabled",
"Filter": {},
"NoncurrentVersionTransitions": [
{
"NoncurrentDays": 30,
"StorageClass": "GLACIER"
}
],
"NoncurrentVersionExpiration": {
"NoncurrentDays": 90
}
}
]
}
The Glacier Point of No Return (Mostly): Once you transition an object to Glacier Flexible Retrieval or Deep Archive, you cannot transition it to another storage class using a lifecycle rule. You’d have to restore it first (which creates a temporary copy in Standard), and then let a new lifecycle rule act on that copy. The designers made this choice to simplify their state management, but it’s a bit of a gotcha. Plan your storage journey carefully.
Small Object Penalty: This one will cost you real money. Storage classes like Standard-IA and Glacier have a minimum billable object size. If you have a 1KB file in Standard-IA, you get billed for 128KB. If you transition thousands of tiny files, you might actually end up paying more than if you’d just left them in Standard. Always analyze the size distribution of your objects before blindly applying transitions. Your brilliant friend says: aggregate those tiny logs into larger files before uploading.