> ## Documentation Index
> Fetch the complete documentation index at: https://docs.basaltic.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Lifecycle rules

> Expiring objects and versions on a schedule the bucket applies for you.

## Lifecycle rules

`PUT /v1/buckets/{bucket}/lifecycle` replaces the whole configuration. Each rule
needs a `status` and at least one action; a rule with `status: disabled` stays in
the configuration but is skipped during evaluation.

<Tabs>
  <Tab title="Console">
    The bucket's **Management** tab has a **Lifecycle rules** card. **Add
    rule** gives you a **Prefix filter** and the day-based actions: **Expire
    current objects after (days)**, **Expire noncurrent versions after
    (days)** with an optional **Always keep newest (versions)**, and **Abort
    incomplete multipart uploads after (days)**. Each rule has its own enable
    switch, and **Save** writes the whole set.
  </Tab>

  <Tab title="API">
    ```json theme={null}
    {
      "lifecycle": {
        "rules": [
          {
            "id": "archive-then-expire",
            "status": "enabled",
            "filter": { "prefix": "logs/" },
            "transition": { "days": 30, "storage_class": "COLD" },
            "expiration": { "days": 365 },
            "abort_incomplete_multipart_upload": { "days_after_initiation": 7 }
          }
        ]
      }
    }
    ```
  </Tab>

  <Tab title="CLI">
    ```bash theme={null}
    basaltic storage bucket set-lifecycle <bucket> --from-file lifecycle.json
    ```

    The whole rule set is replaced, exactly as the API replaces it.
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    err := storage.New(cfg).PutBucketLifecycle(ctx, bucket,
        &storage.PutBucketLifecycleRequest{Lifecycle: lc})
    ```
  </Tab>
</Tabs>

<Warning>
  **A `transition` can only be set through the API, and the console will drop
  one.** The **Lifecycle rules** card has no field for a storage-class
  transition, and a save from that card replaces the whole configuration with
  what the card models — so saving it on a bucket whose rules transition to
  `COLD` silently removes the transition. Manage lifecycle through the API on
  any bucket that uses one.

  An absolute `expiration.date` is API-only too, but that one the card does
  preserve: it round-trips a date it cannot edit, and only replaces it if you
  type a number of days.
</Warning>

| Action                              | What it does                                                                                                         |
| ----------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| `transition`                        | Moves matching objects to another storage class once they are old enough.                                            |
| `expiration`                        | Deletes objects past `days` (from last-modified) or after `date`.                                                    |
| `noncurrent_version_expiration`     | Removes non-current versions past `noncurrent_days`, optionally keeping the `newer_noncurrent_versions` most recent. |
| `abort_incomplete_multipart_upload` | Discards uploads still unfinished `days_after_initiation` later.                                                     |

Rules are evaluated independently, so an object matching several is subject to
all their actions. `filter.prefix` narrows a rule; an absent or empty filter
applies it to the whole bucket.

<Note>
  A transition keeps the object's identity — same key, same version id, same
  last-modified — and moves only its bytes. That is what makes pairing a
  transition with an expiration work: the move does not restart the expiry
  clock.
</Note>

Two constraints on transitions, both rejected at write time rather than failing
silently later:

* **A transition must come strictly before the rule's expiration.** Otherwise
  the object would be deleted before it ever moved.
* **One transition per rule.** With two storage classes a second has nowhere to
  go. The S3 endpoint accepts a single-element `<Transition>` list and rejects
  longer ones rather than applying the first and ignoring the rest.
