Skip to main content
RunBook Academy

CephXLIV · Object Storage FoundationsObject Storage Foundations

S3 versioning and its capacity consequences

Advanced⏱ ~17 minawsradosgw-admin

What you'll learn

  • Enable and use bucket versioning
  • Explain delete markers and version retrieval
  • Predict the capacity growth versioning causes
  • Manage versions with lifecycle rules

Prerequisites

None — start here.

Verified against Ceph Tentacle 20.2.x · Ceph Squid 19.2.x (supported previous) · cephadm matches the verified Ceph release · podman 4.x · csi-rbd and csi-cephfs current · RBD / CephFS / RGW current (matches Ceph release) · Linux kernel 5.15+ (5.10 minimum) · Ubuntu 24.04 LTS (Ceph host baseline) · Debian 12 (Bookworm) (Ceph host baseline) · Rocky Linux / RHEL / AlmaLinux 9.x (Ceph host baseline) · Proxmox VE 9.x (cross-course integration) · Kubernetes 1.31+ (cross-course integration) · 2026-08-18

Not yet marked complete on this device.

Why this matters in production

Versioning is the answer to “a script overwrote all our objects” and it is also the most common cause of unexplained bucket growth. Enabling it without a lifecycle rule to expire old versions is enabling unbounded growth.

Enabling

aws s3api put-bucket-versioning --bucket data \
    --versioning-configuration Status=Enabled

aws s3api get-bucket-versioning --bucket data

Versioning can be suspended but never disabled. Suspending stops new versions being created; existing versions remain.

Behaviour

aws s3api put-object --bucket data --key report.pdf --body v1.pdf
aws s3api put-object --bucket data --key report.pdf --body v2.pdf

aws s3api list-object-versions --bucket data --prefix report.pdf
{"Versions": [
  {"Key": "report.pdf", "VersionId": "9x2...", "IsLatest": true},
  {"Key": "report.pdf", "VersionId": "3k7...", "IsLatest": false}
]}

A GET without a version id returns the latest. Any version can be retrieved explicitly:

aws s3api get-object --bucket data --key report.pdf --version-id 3k7... old.pdf

Delete markers

aws s3api delete-object --bucket data --key report.pdf

This does not delete anything. It creates a delete marker that becomes the latest version, so GET returns 404 while every previous version remains and still consumes capacity.

# restore by removing the delete marker
MARKER_ID=12
aws s3api delete-object --bucket data --key report.pdf --version-id ${MARKER_ID}

# actually delete a version
aws s3api delete-object --bucket data --key report.pdf --version-id 3k7...

Capacity growth

capacity = sum of all versions of all objects

An object rewritten daily on a versioned bucket accumulates 365 copies a year. Workloads that rewrite objects in place — a status file, a rendered index, a checkpoint — are the ones that surprise people.

radosgw-admin bucket stats --bucket=data
aws s3api list-object-versions --bucket data --max-keys 1000 | \
  jq '[.Versions[].Size] | add'

Bounding it with lifecycle

{"Rules": [{
  "ID": "expire-noncurrent",
  "Status": "Enabled",
  "Filter": {"Prefix": ""},
  "NoncurrentVersionExpiration": {"NoncurrentDays": 30},
  "Expiration": {"ExpiredObjectDeleteMarker": true}
}]}
aws s3api put-bucket-lifecycle-configuration --bucket data \
    --lifecycle-configuration file://lifecycle.json

NoncurrentVersionExpiration removes old versions after a period, and ExpiredObjectDeleteMarker cleans up delete markers left with no versions behind them.

Quiz

Knowledge check · 4 questions

  1. Q1. What does `delete-object` do on a versioned bucket?

  2. Q2. Bucket versioning can be disabled once enabled.

  3. Q3. Explain unexplained growth on a versioned bucket.

    A bucket shows 40 TB of usage in `radosgw-admin bucket stats` while an ordinary listing accounts for only 3 TB. Versioning was enabled a year ago after an accidental mass overwrite. No lifecycle rules exist.

  4. Q4. Why does S3 create a delete marker rather than deleting immediately on a versioned bucket?

Passing score: 75%. Answers are checked in this browser.

Production discipline

Enable versioning and its expiration lifecycle rule in the same change; the rule as a follow-up task is the one that does not happen, and the resulting growth is invisible in ordinary listings. Use list-object-versions when accounting for a versioned bucket’s capacity — list-objects shows a fraction of what is stored.

Cross-course references

  • Kubernetes: revision history limits on Deployments serve the same bounded-retention purpose
  • Linux: filesystem snapshots without retention policy grow the same way