S3 Bucket Lifecycle Policy Missing
What does ZopNight detect here?
Missing lifecycle policies on S3 buckets leave cold STANDARD-tier data paying full price. ZopNight would price savings as cold GB times the (STANDARD − STANDARD-IA) rate delta times 730 hours, but abstains on live data today: the per-bucket standard_cold_bytes_gb signal from Storage Lens has no producer yet.
Signal and threshold
| Field | Value |
|---|---|
| Rule IDs | RC-072 |
| Category | rightsizing |
| Severity | low |
| Metric | none — pure configuration read |
| Source | s3_lifecycle.go |
Where it applies
Buckets with no lifecycle rule at all
- Metadata[“lifecycle_policy_present”] (JSON bool) must be false. The prior lifecycle_policy=true Tag gate had the wrong key (missing _present) and the wrong field (Tags, not Metadata), so it was inert and the rule over-fired on every bucket; abstains when the key is absent (unknown).
- PricingAware: per-resource monthly cost (applicability gate)
- SKURatesAware: STANDARD and STANDARD-IA per-GB-hour rates
- Producer gap on standard_cold_bytes_gb (per-bucket GB of cold STANDARD-tier data from S3 Storage Lens / Storage Class Analysis): getMetadataFloat returns -1 when absent, so the rule abstains until this key is emitted.
Pricing cold data at the Standard to IA gap
concrete-or-abstain (cost-driven rightsizing). savings = coldGB × (LookupSKURate(STANDARD) − LookupSKURate(STANDARD-IA)) × hoursPerMonth. The rates are per-GB-hour (the published per-GB-month figures for each storage class ÷ 730 at ingest; see pricing/aws/stream.go isStorageRate), so the ×730 is mandatory to get a monthly figure (omitting it under-reports ~730× and the rec is swallowed by the $5 floor, the same trap as GCP RC-147). The rule abstains (returns nil) when standard_cold_bytes_gb is absent (the current live state), either rate is missing, or the delta is non-positive. It never uses the prior cost0.30 fabricated fraction and never ships a $0 cost rec. (Siblings RC-070/RC-073/RC-1512 already removed the same cost0.30; RC-072 was missed in that sweep.)
Building the transition and expiry rules
- Go to S3 Console → Select bucket → Management
- Create lifecycle rule for object transitions
- Transition to Standard-IA after 30 days
- Transition to a Glacier tier after 90 days (Instant / Flexible Retrieval / Deep Archive by access pattern)
- Expire objects after 365 days if applicable
No metrics, just Standard and Standard-IA rates
None (SKURatesAware: STANDARD / STANDARD-IA per-GB-hour rates)