CephXLIV · Object Storage FoundationsObject Storage Foundations
S3 versioning and its capacity consequences
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
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
Q1. What does `delete-object` do on a versioned bucket?
Q2. Bucket versioning can be disabled once enabled.
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.
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