Versioned S3 buckets with no lifecycle rule to expire noncurrent object versions
What does ZopNight detect here?
ZopNight flags S3 buckets with versioning enabled but no lifecycle rule that expires noncurrent versions. Each old version is a full copy billed at its storage class rate, $0.023 per GB-month in S3 Standard in us-east-1, and ZopNight prices the saving from the noncurrent bytes it measures, up to 20,000 versions per bucket.
Signal and threshold
| Field | Value |
|---|---|
| Rule IDs | RC-073 |
| Category | rightsizing |
| Severity | low |
| Metric | none — pure configuration read |
| Threshold | versioning enabled and no noncurrent expiration rule |
| Source | ZopNight |
| Permissions used | s3:GetBucketVersioning · s3:ListBucketVersions · s3:GetLifecycleConfiguration |
Where it applies
Every overwrite keeps a full copy
Turning on versioning protects against accidental deletes and overwrites, but it changes the bill. AWS’s versioning workflows page states that normal rates apply for every stored version and that each version is the entire object, not a diff. Three versions of a 1 GB file cost the same as three files. A log or export job that rewrites the same keys every night can quietly double a bucket’s size within weeks.
Measuring noncurrent versions in a bucket
aws s3api get-bucket-versioning --bucket my-bucket
aws s3api list-object-versions --bucket my-bucket \ --query 'sum(Versions[?IsLatest==`false`].Size)'
aws s3api get-bucket-lifecycle-configuration --bucket my-bucketThe second command pages through every version, which is slow on large buckets. S3 Inventory or Storage Lens gives the same split at scale.
Checks the bucket goes through
- ZopNight’s inventory shows versioning enabled for the bucket.
- No lifecycle rule on the bucket expires noncurrent versions. A bucket that already has such a rule is skipped; lifecycle rules that only transition current objects do not count.
- The bucket has a known monthly cost and ZopNight has the current Standard storage rate.
- ZopNight has a measured size of the bucket’s noncurrent versions.
How the noncurrent size is measured
CloudWatch’s BucketSizeBytes metric reports storage by class, not by current versus noncurrent,
so it cannot answer the question. ZopNight instead lists the bucket’s object versions and sums the
size of the noncurrent ones, up to 20,000 versions per bucket per scan. Very large buckets are
under-counted rather than over-counted, and a bucket with no noncurrent versions raises nothing.
The old fixed monthly estimate is gone because it bore no relation to the actual version count.
Pricing the old versions
saving = noncurrent GB x Standard rate per GB-monthThis is the full storage rate, not a tier difference, because expiring a version removes it. For buckets where some noncurrent versions sit in cheaper classes, the figure is an upper bound.
Expiring noncurrent versions
- Agree how long old versions must be kept and how many recent ones to retain.
- Add a rule like the lifecycle examples show. Both conditions must be exceeded before a version is deleted:
{"Rules":[{"ID":"expire-old-versions","Status":"Enabled","Filter":{}, "NoncurrentVersionExpiration":{"NoncurrentDays":30,"NewerNoncurrentVersions":3}}]}- Apply it with
aws s3api put-bucket-lifecycle-configuration --bucket my-bucket --lifecycle-configuration file://rule.json.