Why Your S3 Bucket Keeps Growing: Lifecycle Policies for Non-Current Versions in Versioned Buckets

Ned
Ned Cloud Engineer
· Updated · 2 min read
Why Your S3 Bucket Keeps Growing: Lifecycle Policies for Non-Current Versions in Versioned Buckets

The Problem

You store CloudTrail logs in an S3 bucket with versioning enabled. Your lifecycle policy deletes current objects after 3 years. After the fourth year, the bucket metrics show the object count continues to rise even though new log delivery is consistent. Something is wrong.

The Solution

The lifecycle policy only targets current versions. In a versioned bucket, deleting a current object creates a delete marker and the previous version becomes a noncurrent version that is never cleaned up. You need to add a lifecycle rule that also expires noncurrent versions.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
aws s3api put-bucket-lifecycle-configuration \
  --bucket cloudtrail-logs-bucket \
  --lifecycle-configuration '{
    "Rules": [
      {
        "ID": "ExpireCurrentVersions",
        "Status": "Enabled",
        "Filter": {},
        "Expiration": {
          "Days": 1095
        }
      },
      {
        "ID": "ExpireNoncurrentVersions",
        "Status": "Enabled",
        "Filter": {},
        "NoncurrentVersionExpiration": {
          "NoncurrentDays": 1
        }
      },
      {
        "ID": "CleanupDeleteMarkers",
        "Status": "Enabled",
        "Filter": {},
        "Expiration": {
          "ExpiredObjectDeleteMarker": true
        }
      }
    ]
  }'

How It Works

The Versioning Lifecycle

When S3 versioning is enabled:

  1. Upload object → Creates version v1 (current)
  2. Overwrite object → v2 becomes current, v1 becomes noncurrent
  3. Delete object → Creates a delete marker (becomes “current”), v2 becomes noncurrent
  4. Lifecycle expires current after 3 years → Delete marker created, but noncurrent versions persist

This is why the bucket keeps growing. Every “deletion” by the lifecycle just creates a delete marker, and the actual data (noncurrent versions) accumulates indefinitely.

The Fix: Three Lifecycle Rules

Rule What It Does
Expiration.Days: 1095 Creates delete markers for current objects after 3 years
NoncurrentVersionExpiration.NoncurrentDays: 1 Permanently deletes noncurrent versions after 1 day
Expiration.ExpiredObjectDeleteMarker: true Cleans up orphaned delete markers

Why Not the Alternatives?

CloudTrail expiration setting — CloudTrail delivers logs to a destination. It has no feature to manage lifecycle or expiration of delivered files. Lifecycle management happens at the S3 bucket level.

Lambda function to enumerate and delete old objects — This is custom code that must be written, deployed, maintained, and monitored. S3 lifecycle policies are a native feature that handles this automatically at no additional cost.

Change S3 Object Ownership — Object Ownership controls cross-account permissions. It has no effect on lifecycle or deletion behavior.

Key Takeaways

  • In versioned buckets, deleting a current object only creates a delete marker — the actual data persists as a noncurrent version
  • Lifecycle policies must explicitly target noncurrent versions to permanently remove old data
  • Use NoncurrentVersionExpiration to clean up old versions
  • Use ExpiredObjectDeleteMarker to remove orphaned delete markers
  • This is one of the most common S3 cost traps — always check if versioning is enabled when diagnosing unexpected storage growth
Rating:
Share
Previous S3 Standard-IA: Lower Cost Storage with Immediate Retrieval for Infrequent Access Next S3 Object Lock Compliance Mode: Immutable Data Retention That Even Root Cannot Override