# PutBucketLifecycle

`PutBucketLifecycle` is the older, deprecated way to replace a bucket's lifecycle rules - the rules
that tidy a bucket up by expiring old versions and abandoned uploads
([Lifecycle policies](https://docs.strg.com/lifecycle-policies) explains them). STRG answers a request in its
format with `501 NotImplemented`, so a client knows straight away, and the bucket's existing rules
stay exactly as they were. [PutBucketLifecycleConfiguration](https://docs.strg.com/s3-api/PutBucketLifecycleConfiguration)
is the way to set lifecycle rules: it sends the same request, with each rule's prefix inside
`Filter`.

**Note: Deprecated**

Amazon S3 deprecated `PutBucketLifecycle` in favour of
[PutBucketLifecycleConfiguration](https://docs.strg.com/s3-api/PutBucketLifecycleConfiguration). The AWS SDK for
JavaScript v3 does not offer this operation at all; the AWS CLI (`put-bucket-lifecycle`) and
boto3 (`put_bucket_lifecycle`) still do.

**Info: Good to know on STRG**

- This operation's format puts a top-level `Prefix` on every rule, and STRG answers such a
  rule with `501 NotImplemented`. Nothing about the bucket changes.
- A document whose rules carry `Filter` rather than `Prefix` is the
  PutBucketLifecycleConfiguration format, and STRG handles it exactly as that operation does.
- `x-amz-expected-bucket-owner` is welcome and has nothing to check: a key only ever reaches
  buckets of its own account.

## When you'd use it

- **An older script or backup tool that still calls `put-bucket-lifecycle`** gets a clear
  `501 NotImplemented` and leaves the bucket's rules untouched, so nothing changes without anyone
  noticing.
- **Moving such a script over** takes two small changes - the command, and the prefix moving into
  `Filter` - and [the examples below](https://docs.strg.com/s3-api/PutBucketLifecycle#examples) show both side by side.
- **A tool that sends the newer `Filter` format under this name** keeps working: STRG reads
  the document as [PutBucketLifecycleConfiguration](https://docs.strg.com/s3-api/PutBucketLifecycleConfiguration) and
  stores the rules.

## Request

```http
PUT /?lifecycle HTTP/1.1
Host: my-bucket.s3.eu-central-1.strg.com
Authorization: AWS4-HMAC-SHA256 Credential=...
x-amz-date: 20260926T120000Z
x-amz-content-sha256: <SHA-256 of the body>
Content-MD5: <base64 MD5 of the body>
Content-Type: application/xml

<LifecycleConfiguration>...</LifecycleConfiguration>
```

Path-style, the same request is `PUT /my-bucket?lifecycle` on `s3.eu-central-1.strg.com`;
[Addressing](https://docs.strg.com/s3-api/overview#addressing) has the details.

### Permissions

The access key needs **Manage lifecycle rules** (`bucket-lifecycle:write`). A key limited to
certain buckets reaches only those. A key prefix does not narrow this operation, since the
configuration belongs to the whole bucket. A key without the permission gets `403 AccessDenied`
before the body is read. [Access keys](https://docs.strg.com/access-keys) has the full picture.

### URI parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `Bucket` | String | Yes | The bucket whose lifecycle rules would be replaced. |
| `lifecycle` | Flag | Yes | Selects the lifecycle subresource. Takes no value. |

Any other query parameter gets `501 NotImplemented`, so a client knows straight away, except the
`X-Amz-*` parameters of a presigned URL and the `x-id` parameter the JavaScript SDK adds.

### Request headers

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `Content-MD5` | String | Conditional | Base64 MD5 of the body. One integrity header is required: this or an `x-amz-checksum-*` header, and a request with neither gets `400 InvalidRequest`. A value that does not match the body is `400 BadDigest`. The AWS CLI and SDKs send a checksum automatically. |
| `x-amz-sdk-checksum-algorithm` | String | No | The algorithm of the `x-amz-checksum-*` header the SDK sends. Accepted. |
| `x-amz-expected-bucket-owner` | String | No | Welcome, with nothing to check: an access key only ever reaches its own account's buckets. |

### Request body

A `LifecycleConfiguration` document of at most 2 MiB. This operation's rule format always
carries a top-level `Prefix`, which STRG answers first, so the rest of the rule is never read:

| Element | Type | Required | Description |
| --- | --- | --- | --- |
| `Rule` | Container | Yes | One lifecycle rule. Repeated. |
| `Rule/Prefix` | String | Yes | Answered with `501 NotImplemented` on any rule that carries it. The same prefix in `Filter/Prefix`, sent as [PutBucketLifecycleConfiguration](https://docs.strg.com/s3-api/PutBucketLifecycleConfiguration), sets the rule. |
| `Rule/ID` | String | No | Never read: the rule's `Prefix` has already been answered. |
| `Rule/Status` | String | Yes | Never read: the rule's `Prefix` has already been answered. |
| `Rule/Expiration` | Container | No | Never read: the rule's `Prefix` has already been answered. |
| `Rule/NoncurrentVersionExpiration` | Container | No | Never read: the rule's `Prefix` has already been answered. |
| `Rule/AbortIncompleteMultipartUpload` | Container | No | Never read: the rule's `Prefix` has already been answered. |
| `Rule/Transition` | Container | No | Never read: the rule's `Prefix` has already been answered. Every object stays in one storage class, so neither operation has transitions. |
| `Rule/NoncurrentVersionTransition` | Container | No | Never read: the rule's `Prefix` has already been answered. Neither operation has transitions, for the same reason. |

The actions STRG stores and their limits are on
[PutBucketLifecycleConfiguration](https://docs.strg.com/s3-api/PutBucketLifecycleConfiguration) and
[Lifecycle policies](https://docs.strg.com/lifecycle-policies).

## Response

A request in this operation's format always gets an error document, so there is no success
response to describe. The `x-amz-request-id` header carries the request ID, handy for a support
request.

## Errors

The answers this operation can give, and the situation behind each. Once the key is accepted in
this endpoint's region, the request is answered before the bucket is looked up, so a bucket that
does not exist gets the same answer as one that does.

| Code | HTTP status | When |
| --- | --- | --- |
| `NotImplemented` | 501 | A rule carries a top-level `Prefix` - every request in this operation's format - or the request carries a query parameter this operation does not take. |
| `MalformedXML` | 400 | The body is not well-formed XML, not UTF-8, its root element is not `LifecycleConfiguration`, or it uses a comment, CDATA section, `DOCTYPE`, processing instruction or namespace prefix. |
| `InvalidRequest` | 400 | The request carries neither `Content-MD5` nor an `x-amz-checksum-*` header. |
| `BadDigest` | 400 | `Content-MD5` does not match the body. |
| `XAmzContentSHA256Mismatch` | 400 | `x-amz-content-sha256` does not match the body. |
| `MaxMessageLengthExceeded` | 400 | The body is larger than 2 MiB. |
| `AccessDenied` | 403 | The access key does not hold **Manage lifecycle rules**, or the bucket is outside its scope. |
| `InvalidAccessKeyId` | 403 | The access key does not exist, has expired or been revoked - or it was created moments ago and is not yet usable in this endpoint's region, where the account already stores data, in which case the same request works shortly after. |
| `NoSuchBucket` | 404 | The account has no storage in this endpoint's region yet. The bucket is not looked for, whatever the body holds. |
| `SignatureDoesNotMatch` | 403 | The request signature does not match. |
| `RequestTimeTooSkewed` | 403 | The request time is more than 15 minutes from the server's. |
| `AuthorizationHeaderMalformed` | 400 | The signature's credential scope names a region other than the endpoint's. |
| `ServiceUnavailable` | 503 | A temporary failure. The AWS SDKs retry it on their own. |

## Differences from Amazon S3

| Aspect | Amazon S3 | STRG |
| --- | --- | --- |
| The operation | Deprecated, still accepted | `501 NotImplemented`, with the bucket's rules unchanged. [PutBucketLifecycleConfiguration](https://docs.strg.com/s3-api/PutBucketLifecycleConfiguration) sets the same rules. |
| `x-amz-expected-bucket-owner` | Checked against the bucket owner | Welcome, with nothing to check: a key only reaches its own account's buckets |

[Differences from Amazon S3](https://docs.strg.com/s3-api/overview#differences-from-amazon-s3) covers the ones that
apply to every operation.

## Examples

Moving from this operation to PutBucketLifecycleConfiguration takes two changes: the command,
and the prefix moving into `Filter`. Here is a rule that removes objects under `logs/` 30 days
after upload, previous versions 7 days after they are replaced, and incomplete multipart uploads
after 2 days:

**AWS CLI**

Instead of `put-bucket-lifecycle` with `"Prefix": "logs/"` on the rule:

```bash
aws s3api put-bucket-lifecycle-configuration \
  --bucket my-bucket \
  --endpoint-url https://s3.eu-central-1.strg.com \
  --region eu-central-1 \
  --lifecycle-configuration '{
    "Rules": [
      {
        "ID": "expire-logs",
        "Filter": { "Prefix": "logs/" },
        "Status": "Enabled",
        "Expiration": { "Days": 30 },
        "NoncurrentVersionExpiration": { "NoncurrentDays": 7 },
        "AbortIncompleteMultipartUpload": { "DaysAfterInitiation": 2 }
      }
    ]
  }'
```

**Python (boto3)**

Instead of `put_bucket_lifecycle` with `"Prefix": "logs/"` on the rule:

```python
import boto3

s3 = boto3.client(
    "s3",
    endpoint_url="https://s3.eu-central-1.strg.com",
    region_name="eu-central-1",
)

s3.put_bucket_lifecycle_configuration(
    Bucket="my-bucket",
    LifecycleConfiguration={
        "Rules": [
            {
                "ID": "expire-logs",
                "Filter": {"Prefix": "logs/"},
                "Status": "Enabled",
                "Expiration": {"Days": 30},
                "NoncurrentVersionExpiration": {"NoncurrentDays": 7},
                "AbortIncompleteMultipartUpload": {"DaysAfterInitiation": 2},
            }
        ]
    },
)
```

**JavaScript (AWS SDK v3)**

The AWS SDK for JavaScript v3 has no command for this deprecated operation.
`PutBucketLifecycleConfigurationCommand` sends the same request and sets the rule:

```javascript
import { PutBucketLifecycleConfigurationCommand, S3Client } from "@aws-sdk/client-s3";

const client = new S3Client({
  endpoint: "https://s3.eu-central-1.strg.com",
  region: "eu-central-1",
});

await client.send(
  new PutBucketLifecycleConfigurationCommand({
    Bucket: "my-bucket",
    LifecycleConfiguration: {
      Rules: [
        {
          ID: "expire-logs",
          Filter: { Prefix: "logs/" },
          Status: "Enabled",
          Expiration: { Days: 30 },
          NoncurrentVersionExpiration: { NoncurrentDays: 7 },
          AbortIncompleteMultipartUpload: { DaysAfterInitiation: 2 },
        },
      ],
    },
  }),
);
```

## Related

- [PutBucketLifecycleConfiguration](https://docs.strg.com/s3-api/PutBucketLifecycleConfiguration)
- [GetBucketLifecycleConfiguration](https://docs.strg.com/s3-api/GetBucketLifecycleConfiguration)
- [GetBucketLifecycle](https://docs.strg.com/s3-api/GetBucketLifecycle)
- [DeleteBucketLifecycle](https://docs.strg.com/s3-api/DeleteBucketLifecycle)
- [Lifecycle policies](https://docs.strg.com/lifecycle-policies)
- [Access keys](https://docs.strg.com/access-keys)
- [S3 API overview](https://docs.strg.com/s3-api/overview)
