R2 Lifecycle Policies
Configure lifecycle policies to automatically manage object retention, deletion, and storage class transitions in R2 buckets.
Overview
public class LifecycleService(ICloudflareApiClient cf)
{
public async Task SetRetentionPolicyAsync(string bucket, int days)
{
await cf.Accounts.Buckets.SetLifecycleAsync(bucket, new BucketLifecyclePolicy([
new LifecycleRule(
Id: "auto-delete",
DeleteObjectsTransition: new DeleteObjectsTransition(
Condition: LifecycleCondition.AfterDays(days)
)
)
]));
}
}
Note
Jurisdictional Buckets: If your bucket was created with a jurisdiction (e.g., R2Jurisdiction.EuropeanUnion), you must pass the jurisdiction parameter to all lifecycle operations. See Working with Jurisdictional Buckets.
await cf.Accounts.Buckets.SetLifecycleAsync("my-eu-bucket", lifecyclePolicy, R2Jurisdiction.EuropeanUnion);
Setting Lifecycle Policy
Auto-Delete After Age
Delete objects after a specified number of days:
await cf.Accounts.Buckets.SetLifecycleAsync("my-bucket", new BucketLifecyclePolicy([
new LifecycleRule(
Id: "delete-after-30-days",
Enabled: true,
DeleteObjectsTransition: new DeleteObjectsTransition(
Condition: LifecycleCondition.AfterDays(30)
)
)
]));
Delete on Specific Date
Delete objects on a specific date:
await cf.Accounts.Buckets.SetLifecycleAsync("my-bucket", new BucketLifecyclePolicy([
new LifecycleRule(
Id: "delete-on-date",
DeleteObjectsTransition: new DeleteObjectsTransition(
Condition: LifecycleCondition.OnDate(new DateTime(2025, 12, 31))
)
)
]));
Transition to Infrequent Access
Move objects to cheaper storage after a period:
await cf.Accounts.Buckets.SetLifecycleAsync("my-bucket", new BucketLifecyclePolicy([
new LifecycleRule(
Id: "move-to-ia",
StorageClassTransitions: [
new StorageClassTransition(
Condition: LifecycleCondition.AfterDays(30),
StorageClass: R2StorageClass.InfrequentAccess
)
]
)
]));
Abort Incomplete Multipart Uploads
Clean up failed multipart uploads:
await cf.Accounts.Buckets.SetLifecycleAsync("my-bucket", new BucketLifecyclePolicy([
new LifecycleRule(
Id: "cleanup-multipart",
AbortMultipartUploadsTransition: new AbortMultipartUploadsTransition(
Condition: LifecycleCondition.AfterDays(7)
)
)
]));
Apply to Prefix Only
Apply rules to objects with a specific prefix:
await cf.Accounts.Buckets.SetLifecycleAsync("my-bucket", new BucketLifecyclePolicy([
new LifecycleRule(
Id: "temp-files-cleanup",
Conditions: new LifecycleRuleConditions(Prefix: "temp/"),
DeleteObjectsTransition: new DeleteObjectsTransition(
Condition: LifecycleCondition.AfterDays(1)
)
)
]));
Complete Lifecycle Configuration
await cf.Accounts.Buckets.SetLifecycleAsync("my-bucket", new BucketLifecyclePolicy([
// Move to IA after 30 days
new LifecycleRule(
Id: "archive-old-objects",
StorageClassTransitions: [
new StorageClassTransition(
Condition: LifecycleCondition.AfterDays(30),
StorageClass: R2StorageClass.InfrequentAccess
)
]
),
// Delete after 365 days
new LifecycleRule(
Id: "delete-old-objects",
DeleteObjectsTransition: new DeleteObjectsTransition(
Condition: LifecycleCondition.AfterDays(365)
)
),
// Cleanup incomplete uploads
new LifecycleRule(
Id: "cleanup-multipart",
AbortMultipartUploadsTransition: new AbortMultipartUploadsTransition(
Condition: LifecycleCondition.AfterDays(7)
)
),
// Delete temp files quickly
new LifecycleRule(
Id: "temp-cleanup",
Conditions: new LifecycleRuleConditions(Prefix: "temp/"),
DeleteObjectsTransition: new DeleteObjectsTransition(
Condition: LifecycleCondition.AfterDays(1)
)
)
]));
Getting Lifecycle Policy
var policy = await cf.Accounts.Buckets.GetLifecycleAsync("my-bucket");
foreach (var rule in policy.Rules)
{
Console.WriteLine($"Rule: {rule.Id}");
Console.WriteLine($" Enabled: {rule.Enabled}");
if (rule.DeleteObjectsTransition is { } del)
{
Console.WriteLine($" Delete after: {del.Condition.MaxAge / 86400} days");
}
}
Deleting Lifecycle Policy
await cf.Accounts.Buckets.DeleteLifecycleAsync("my-bucket");
Models Reference
LifecycleRule
| Property | Type | Description |
|---|---|---|
Id |
string? |
Rule identifier |
Enabled |
bool |
Whether rule is active (default: true) |
Conditions |
LifecycleRuleConditions? |
Object filtering conditions |
DeleteObjectsTransition |
DeleteObjectsTransition? |
Deletion trigger |
AbortMultipartUploadsTransition |
AbortMultipartUploadsTransition? |
Multipart cleanup trigger |
StorageClassTransitions |
IReadOnlyList<StorageClassTransition>? |
Storage class changes |
LifecycleCondition
| Method | Description |
|---|---|
AfterDays(int days) |
Trigger after N days |
AfterSeconds(int seconds) |
Trigger after N seconds |
OnDate(DateTime date) |
Trigger on specific date |
LifecycleConditionType
| Value | Description |
|---|---|
Age |
Based on object age |
Date |
Based on specific date |
R2StorageClass (Extensible Enum)
Storage class determines cost and access characteristics. This is an extensible enum that accepts both known constants and custom string values.
| Constant | Value | Description |
|---|---|---|
Standard |
Standard |
Frequently accessed data (default) |
InfrequentAccess |
InfrequentAccess |
Lower cost, retrieval fees apply |
// Using known constants
var standard = R2StorageClass.Standard;
var ia = R2StorageClass.InfrequentAccess;
// Using custom values for future storage classes
R2StorageClass future = "ArchiveStorage";
See SDK Conventions for more on extensible enums.
Common Patterns
Log Retention
public async Task SetLogRetentionAsync(string bucket, int retentionDays)
{
await cf.Accounts.Buckets.SetLifecycleAsync(bucket, new BucketLifecyclePolicy([
new LifecycleRule(
Id: "log-retention",
Conditions: new LifecycleRuleConditions(Prefix: "logs/"),
StorageClassTransitions: [
new StorageClassTransition(
Condition: LifecycleCondition.AfterDays(7),
StorageClass: R2StorageClass.InfrequentAccess
)
],
DeleteObjectsTransition: new DeleteObjectsTransition(
Condition: LifecycleCondition.AfterDays(retentionDays)
)
)
]));
}
Backup Lifecycle
public async Task SetBackupPolicyAsync(string bucket)
{
await cf.Accounts.Buckets.SetLifecycleAsync(bucket, new BucketLifecyclePolicy([
// Daily backups: keep 30 days
new LifecycleRule(
Id: "daily-backups",
Conditions: new LifecycleRuleConditions(Prefix: "daily/"),
DeleteObjectsTransition: new DeleteObjectsTransition(
Condition: LifecycleCondition.AfterDays(30)
)
),
// Weekly backups: move to IA after 30 days, keep 180 days
new LifecycleRule(
Id: "weekly-backups-archive",
Conditions: new LifecycleRuleConditions(Prefix: "weekly/"),
StorageClassTransitions: [
new StorageClassTransition(
Condition: LifecycleCondition.AfterDays(30),
StorageClass: R2StorageClass.InfrequentAccess
)
]
),
new LifecycleRule(
Id: "weekly-backups-delete",
Conditions: new LifecycleRuleConditions(Prefix: "weekly/"),
DeleteObjectsTransition: new DeleteObjectsTransition(
Condition: LifecycleCondition.AfterDays(180)
)
)
]));
}
Required Permissions
| Permission | Scope | Level |
|---|---|---|
| Workers R2 Storage | Account | Write |
Important Notes
- Processing delay: Rules are processed within 24 hours of being set
- Maximum rules: Up to 1000 lifecycle rules per bucket
- Age calculation: Based on object creation time
- Prefix matching: Case-sensitive, matches key prefixes
Related
- Bucket Management - Create and manage buckets
- R2 Deleting Objects - Manual object deletion