S3 Lifecycle
SeaweedFS implements the S3 PutBucketLifecycleConfiguration API. Configured rules are evaluated and enforced by a worker that runs as a scheduled job and exits when each pass completes.
This page is the operator-facing entry point. Developers and architecture readers should see weed/s3api/s3lifecycle/DESIGN.md.
Supported features
| Feature | Status | Notes |
|---|---|---|
Expiration.Days |
Yes | Latest-version PUT clock |
Expiration.Date |
Yes | Walker path; fires once date is reached |
Expiration.ExpiredObjectDeleteMarker |
Yes | Walker path; sibling-aware |
NoncurrentVersionExpiration.NoncurrentDays |
Yes | Clock starts at the demoting PUT, not the entry's own mtime |
NoncurrentVersionExpiration.NewerNoncurrentVersions |
Yes | Walker path; version-list aware |
AbortIncompleteMultipartUpload.DaysAfterInitiation |
Yes | |
Filter.Prefix |
Yes | |
Filter.Tag |
Yes | |
Filter.ObjectSizeGreaterThan / ObjectSizeLessThan |
Yes | |
Filter.And (composite) |
Yes | |
Transition / NoncurrentVersionTransition |
Rejected | SeaweedFS doesn't model storage class tiers. A PUT whose enabled rules contain either is rejected with NotImplemented — the config is not stored. See Limits and validation. |
API endpoints
PUT /{bucket}?lifecycle # PutBucketLifecycleConfiguration
GET /{bucket}?lifecycle # GetBucketLifecycleConfiguration
DELETE /{bucket}?lifecycle # DeleteBucketLifecycle
The CLI examples below are the convenient way to drive these. The PUT body on the wire is XML — the same LifecycleConfiguration document AWS uses:
<LifecycleConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<Rule>
<ID>expire-logs</ID>
<Status>Enabled</Status>
<Filter>
<And>
<Prefix>logs/</Prefix>
<Tag><Key>class</Key><Value>temp</Value></Tag>
<ObjectSizeGreaterThan>4096</ObjectSizeGreaterThan>
</And>
</Filter>
<Expiration><Days>30</Days></Expiration>
<NoncurrentVersionExpiration>
<NoncurrentDays>7</NoncurrentDays>
<NewerNoncurrentVersions>3</NewerNoncurrentVersions>
</NoncurrentVersionExpiration>
<AbortIncompleteMultipartUpload>
<DaysAfterInitiation>7</DaysAfterInitiation>
</AbortIncompleteMultipartUpload>
</Rule>
</LifecycleConfiguration>
Unknown elements are skipped on decode. Filter with a single Prefix (or a single Tag) needs no And wrapper; And is only required when combining more than one predicate. The stored XML is returned verbatim by GET — SeaweedFS does not rewrite or canonicalize it.
Limits and validation
The PUT handler validates the whole document before touching any state, so a rejected config never half-applies:
| Condition | Result |
|---|---|
| Body larger than 1 MiB | EntityTooLarge |
| Body not valid lifecycle XML | MalformedXML |
An enabled rule contains Transition or NoncurrentVersionTransition |
NotImplemented (whole config rejected) |
| Filer / backing-store error | InternalError |
GET when no lifecycle is configured |
NoSuchLifecycleConfiguration |
Notes:
- Only enabled rules are checked for transitions — a
Disabledrule carrying aTransitionis ignored rather than rejected, matching how a disabled rule is otherwise inert. - There is no enforced cap on the number of rules, rule-ID length, or tag count beyond the 1 MiB document size.
GEThas a legacy fallback: if a bucket has no lifecycle XML but does have directory TTLs configured viafs.configure -ttl(see S3 Lifecycle vs Volume TTL),GETsynthesizesExpiration.Daysrules from those TTLs instead of returningNoSuchLifecycleConfiguration.
Example: AWS CLI
# Set lifecycle configuration
aws s3api put-bucket-lifecycle-configuration \
--endpoint-url http://localhost:8333 \
--bucket my-bucket \
--lifecycle-configuration '{
"Rules": [
{
"ID": "expire-old",
"Status": "Enabled",
"Filter": { "Prefix": "" },
"Expiration": { "Days": 90 },
"NoncurrentVersionExpiration": { "NoncurrentDays": 30 },
"AbortIncompleteMultipartUpload": { "DaysAfterInitiation": 7 }
}
]
}'
# Get lifecycle configuration
aws s3api get-bucket-lifecycle-configuration \
--endpoint-url http://localhost:8333 \
--bucket my-bucket
# Delete lifecycle configuration
aws s3api delete-bucket-lifecycle \
--endpoint-url http://localhost:8333 \
--bucket my-bucket
Example: Terraform
resource "aws_s3_bucket_lifecycle_configuration" "example" {
bucket = aws_s3_bucket.example.id
rule {
id = "expire-logs"
status = "Enabled"
filter {
prefix = "logs/"
}
expiration {
days = 30
}
noncurrent_version_expiration {
noncurrent_days = 7
newer_noncurrent_versions = 2
}
abort_incomplete_multipart_upload {
days_after_initiation = 7
}
}
}
How it works
The lifecycle worker is a scheduled job (default daily). Each invocation:
- Reads bucket lifecycle XML from each bucket's metadata.
- Compiles rules into a per-shard partition (replay-eligible vs. walker-bound).
- Subscribes to the filer meta-log — one stream covering all 16 shards in this worker process.
- For replay-eligible actions (
ExpirationDays,NoncurrentDays,AbortMPU), checks each event's DueTime and dispatchesLifecycleDeleteif elapsed. - For walker-bound rules (
ExpirationDate,ExpiredObjectDeleteMarker,NewerNoncurrent, or anything promoted to scan-only), iterates the bucket and evaluates each entry against current state. - Persists per-shard cursors so the next pass resumes where this one left off.
The worker exits when the pass is done. The admin scheduler invokes it on a daily cadence by default; operators can change that via the standard plugin scheduler config.
Timing
Expiration is not event-time accurate. The lag from the triggering PUT to the actual delete is bounded by the worker invocation cadence (default 24h) plus, for walker-only rules, up to one walker_interval_minutes window. Budget for "up to a day," not seconds.
Days count in 24-hour units from the relevant clock (the latest-version PUT for Expiration.Days, the demoting PUT for NoncurrentDays, the MPU initiation for AbortIncompleteMultipartUpload). The AWS CLI and SDKs reject Days < 1 client-side, so one day is the smallest window you'll set through them. SeaweedFS does not validate this server-side — a raw PUT with Days set to 0 (or omitted) is stored and returned as-is, but the rule compiles to a no-op and deletes nothing. The in-repo integration tests compile with a build tag that shortens one "day" to 10 seconds so a full lifecycle can be exercised in CI; released binaries always use 24 hours.
Versioning integration
Lifecycle rules interact with S3 Object Versioning:
NoncurrentVersionExpirationonly applies to versioned buckets. Non-current versions are deleted afterNoncurrentDaysdays since they were superseded (the demoting PUT's TsNs, not the version's own mtime).NewerNoncurrentVersionsretains the N newest non-current versions.ExpiredObjectDeleteMarkerremoves delete markers that are the sole remaining version of an object (no non-current versions behind them).Expiration.Dayson a versioned bucket creates a delete marker when the current version expires; it does not permanently delete the object.
Disk reclamation and the TTL fast path
By default the worker deletes objects individually and disk is freed later by volume vacuum. For high-churn, non-versioned buckets with a fixed Expiration.Days retention, an opt-in TTL fast path stamps the expiry as a volume TTL at write time, so disk is reclaimed by dropping the whole volume and the worker only removes the metadata entry. It is off by default and applies only to non-versioned, non-Object-Lock buckets with prefix/size Expiration.Days rules; everything else falls back to the worker automatically.
weed shell -master <host:port>
> s3.bucket.lifecycle.fastpath -name my-bucket # show current state
> s3.bucket.lifecycle.fastpath -name my-bucket -enable
> s3.bucket.lifecycle.fastpath -name my-bucket -disable
Trade-offs and when to choose it are covered in S3 Lifecycle vs Volume TTL.
Quick references
- Recipes — copy-paste configs for the common scenarios, with how to verify each
- Operator Guide — config knobs, defaults, when to change each
- Monitoring — Prometheus metrics, heartbeat log line, what a healthy run looks like
- Troubleshooting — stuck cursor, missing deletes, head-of-line blocking
- Architecture — high-level overview of the worker, engine, and dispatch path
Introduction
- Quick Start with weed mini
- Simplest S3 Bucket and User Setup
- Components
- Blob Store Architecture
- Getting Started
- Production Setup
- A typical step‐by‐step example
- Benchmarks
- FAQ
- Applications
API
Configuration
- Replication
- Store file with a Time To Live
- Failover Master Server
- Erasure coding for warm storage
- EC Bitrot Detection
- Server Startup via Systemd
- Environment Variables
Filer
- Filer Setup
- Directories and Files
- File Operations Quick Reference
- Data Structure for Large Files
- Filer Data Encryption
- Filer Commands and Operations
- Filer JWT Use
- TUS Resumable Uploads
Filer Stores
- Filer Cassandra Setup
- Filer Redis Setup
- Super Large Directories
- Path-Specific Filer Store
- Choosing a Filer Store
- Customize Filer Store
Management
Advanced Filer Configurations
- Migrate to Filer Store
- Add New Filer Store
- Filer Store Replication
- Filer Active Active cross cluster continuous synchronization
- Filer as a Key-Large-Value Store
- Path Specific Configuration
- Filer Change Data Capture
- Filer Operation Serialization
FUSE Mount
- Mount on Windows
- FIO benchmark
- fstab and systemd mount
- POSIX Compliance
- Distributed POSIX Locks
- P2P reading in weed mount
- Mount over the Internet
WebDAV
SFTP Server
Cloud Drive
- Cloud Drive Benefits
- Cloud Drive Architecture
- Configure Remote Storage
- Azure Blob Storage Authentication
- Mount Remote Storage
- Cache Remote Storage
- Cloud Drive Quick Setup
- Gateway to Remote Object Storage
AWS S3 API
- Amazon S3 API
- Supported APIs vs Minio
- S3 Lifecycle
- S3 Lifecycle vs Volume TTL
- S3 Conditional Operations
- S3 CORS
- S3 Object Lock and Retention
- S3 Object Versioning
- S3 RenameObject
- S3 API Benchmark
- S3 API FAQ
- S3 Bucket Quota
- S3 Rate Limiting
- S3 API Audit log
- S3 Nginx Proxy
- Docker Compose for S3
S3 Table Bucket
- S3 Table Bucket
- S3 Table Bucket Commands
- S3 Tables Security
- SeaweedFS Iceberg Catalog
- Iceberg REST Catalog API
- Iceberg Table Maintenance
- SeaweedFS Lance Catalog
- Lance Maintenance Worker
Iceberg Integrations
- Spark Iceberg Integration
- Trino Iceberg Integration
- Dremio Iceberg Integration
- DuckDB Iceberg Integration
- Doris Iceberg Integration
- RisingWave Iceberg Integration
- Lakekeeper Iceberg Integration
Lance Integrations
S3 Authentication & IAM
- S3 Configuration - Start Here
- S3 Credentials (
-s3.config) - OIDC Integration (
-s3.iam.config) - Kubernetes ServiceAccount Authentication (IRSA-style)
- S3 Policy Variables
- S3 Policy Conditions
- S3 Bucket Policies
- Amazon IAM API
- AWS IAM CLI
- weed shell - Shell IAM Commands
Server-Side Encryption
S3 Client Tools
- AWS CLI with SeaweedFS
- s3cmd with SeaweedFS
- rclone with SeaweedFS
- restic with SeaweedFS
- nodejs with Seaweed S3
Machine Learning
HDFS
- Hadoop Compatible File System
- run Spark on SeaweedFS
- run HBase on SeaweedFS
- Run Trino on SeaweedFS
- Hadoop Benchmark
- HDFS via S3 connector
Replication and Backup
- Async Replication to another Filer [Deprecated]
- Async Backup
- Async Filer Metadata Backup
- Async Replication to Cloud [Deprecated]
- Kubernetes Backups and Recovery with K8up
Metadata Change Events
Messaging
- Structured Data Lake with SMQ and SQL
- Seaweed Message Queue
- SQL Queries on Message Queue
- SQL Quick Reference
- PostgreSQL-compatible Server weed db
- Pub-Sub to SMQ to SQL
- Kafka to Kafka Gateway to SMQ to SQL
Use Cases
Operations
- System Metrics
- weed shell
- Data Backup
- Deployment to Kubernetes and Minikube
- Helm Chart Recipes
- Deployment with seaweed-up
Rust Volume Server
Advanced
- Large File Handling
- Optimization
- Optimization for Many Small Buckets
- Volume Management
- Tiered Storage
- Cloud Tier
- Cloud Monitoring
- Load Command Line Options from a file
- SRV Service Discovery
- Volume Files Structure
Security
- Security Overview
- Security Configuration
- Cryptography and FIPS Compliance
- Run Blob Storage on Public Internet