# Compatibility (/tools/s3-gateway/compatibility)

Shelby implements the S3 calls that object storage workloads use: put, get, head, range
reads, multipart uploads, listing and delete. This page lists how Shelby handles every
part of the S3 API, so you can check your workload before you move it.

Each row is marked:

* **Supported**: works as it does on Amazon S3.
* **Supported with differences**: works, with a difference noted alongside it.
* **Not supported**: not available. The notes give the alternative.

## S3 features

{/*
  GENERATED FRAGMENT. Do not edit by hand.
  Source: data/s3-compat.json, checked against the beta-harness corpus.
  Regenerate: pnpm generate:s3
  */}

### Objects

| Capability                                                                                  | Status                                                       | Notes                                                                                                                                                                                                                                                                                                         |
| ------------------------------------------------------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| PutObject, GetObject, HeadObject                                                            | <span aria-hidden="true">✓</span> Supported                  | Single PUTs up to 5 GiB. Range reads are supported.                                                                                                                                                                                                                                                           |
| Read-after-write consistency                                                                | <span aria-hidden="true">◐</span> Supported with differences | GET and HEAD see a new or overwritten object as soon as the write returns. A new object can take a few seconds to appear in listings. See [Known compatibility issues](/tools/s3-gateway/known-issues).                                                                                                       |
| Overwrite an existing key                                                                   | <span aria-hidden="true">✓</span> Supported                  | A plain PUT replaces the object, as a full new write. Reads see the new version as soon as the PUT returns.                                                                                                                                                                                                   |
| Create-only writes (`If-None-Match: *`)                                                     | <span aria-hidden="true">✓</span> Supported                  | Returns `412 PreconditionFailed` if the key already exists.                                                                                                                                                                                                                                                   |
| Conditional overwrite (`If-Match` on PUT)                                                   | <span aria-hidden="true">✕</span> Not supported              | Not enforced on the hosted endpoint yet: the write succeeds whether or not the ETag matches. Do not rely on it for concurrency control. See [Known compatibility issues](/tools/s3-gateway/known-issues).                                                                                                     |
| Conditional reads (`If-Match`, `If-None-Match`, `If-Modified-Since`, `If-Unmodified-Since`) | <span aria-hidden="true">✕</span> Not supported              | Accepted and ignored. GET always returns the full object.                                                                                                                                                                                                                                                     |
| DeleteObject                                                                                | <span aria-hidden="true">✓</span> Supported                  | Returns `204`, including for a key that does not exist. `If-Match` supports exact ETags and `*` for an existence check.                                                                                                                                                                                       |
| DeleteObjects (batch, up to 1,000 keys)                                                     | <span aria-hidden="true">◐</span> Supported with differences | Up to 1,000 keys. Keys that do not exist are reported as deleted, as on S3. Per-key ETags are supported and failed conditions return per-key errors. The AWS SDK for JavaScript sends this request in a form Shelby does not recognise yet. See [Known compatibility issues](/tools/s3-gateway/known-issues). |
| CopyObject, UploadPartCopy, `aws s3 mv`                                                     | <span aria-hidden="true">✕</span> Not supported              | Returns `501 NotImplemented`. Download and re-upload instead.                                                                                                                                                                                                                                                 |

### Multipart uploads

| Capability                                                           | Status                                                       | Notes                                                                                                                                                                     |
| -------------------------------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Create, UploadPart, Complete, Abort, ListParts, ListMultipartUploads | <span aria-hidden="true">◐</span> Supported with differences | Parts are 5 MiB to 5 GiB. `ListMultipartUploads` from the AWS SDK for JavaScript gets the wrong answer. See [Known compatibility issues](/tools/s3-gateway/known-issues). |
| Parts per upload                                                     | <span aria-hidden="true">◐</span> Supported with differences | Up to 3,000 parts (S3 allows 10,000). Use parts of at least 10 MiB, which fit files up to about 29 GiB.                                                                   |

### Listing

| Capability                 | Status                                                       | Notes                                                                                                                                                                  |
| -------------------------- | ------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| ListObjects, ListObjectsV2 | <span aria-hidden="true">◐</span> Supported with differences | Prefix, delimiter and pagination work. `max-keys` above 1,000 is rejected rather than clamped. `fetch-owner` is ignored. New objects can take a few seconds to appear. |

### Integrity

| Capability                                                     | Status                                          | Notes                                                                                                                                                                                                                                           |
| -------------------------------------------------------------- | ----------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| ETags                                                          | <span aria-hidden="true">✓</span> Supported     | MD5 of the content for single-part objects, S3's `<md5>-<parts>` form for multipart objects. Objects written before mid-October 2026 have older ETags that are not MD5s.                                                                        |
| `Content-MD5` upload check                                     | <span aria-hidden="true">✓</span> Supported     | A mismatch is rejected with `400 BadDigest`.                                                                                                                                                                                                    |
| Flexible checksums (`x-amz-checksum-*`, `aws-chunked` uploads) | <span aria-hidden="true">✕</span> Not supported | Checksum headers are accepted but not verified. `aws-chunked` uploads, which the AWS CLI and boto3 send by default, are refused with `501`: each [quickstart](/tools/s3-gateway/quickstarts/aws-cli) shows the one setting that turns them off. |

### Object metadata

| Capability                                                                                    | Status                                          | Notes                                                                                                                 |
| --------------------------------------------------------------------------------------------- | ----------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| Content-Type, Cache-Control, Content-Disposition, Content-Encoding, Content-Language, Expires | <span aria-hidden="true">✓</span> Supported     | Stored and returned. Objects stored before Shelby kept metadata come back as `binary/octet-stream`.                   |
| User metadata (`x-amz-meta-*`)                                                                | <span aria-hidden="true">✓</span> Supported     | Stored and returned, up to 2 KB per object as on S3. This includes the modification time rclone writes.               |
| Object tagging, storage classes, object lock                                                  | <span aria-hidden="true">✕</span> Not supported | Headers are accepted and ignored. Every object is `STANDARD`. The tagging, retention and legal-hold APIs are refused. |

### Security and access

| Capability                               | Status                                                       | Notes                                                                                                                                                                               |
| ---------------------------------------- | ------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| AWS Signature Version 4                  | <span aria-hidden="true">✓</span> Supported                  | Header-signed requests from any S3 client, with any signing region. Presigned URLs must be signed with `default`. Use `default` everywhere: it's the region Shelby reports.         |
| Server-side encryption                   | <span aria-hidden="true">◐</span> Supported with differences | Always on: every object is encrypted at rest with keys Shelby manages (SSE-S3). SSE-C and SSE-KMS are refused with `501`.                                                           |
| Presigned URLs for downloads (GET, HEAD) | <span aria-hidden="true">✓</span> Supported                  | One object per URL, for up to 7 days. Sign with region `default`. See [Presigned URLs](/tools/s3-gateway/presigned-urls).                                                           |
| Presigned uploads, deletes and listings  | <span aria-hidden="true">✕</span> Not supported              | Refused with `405` or `400`. Upload, delete and list from a server with your credentials. To share a prefix, use a [folder share](/tools/s3-gateway/presigned-urls#share-a-folder). |
| CORS and browser uploads                 | <span aria-hidden="true">✕</span> Not supported              | You can't configure CORS. Browsers can open presigned links and load them in `<img>` and `<video>`, but don't build on cross-origin `fetch`. Upload from a server.                  |
| Authentication error format              | <span aria-hidden="true">◐</span> Supported with differences | Bad or missing credentials return `401` with a plain-text body instead of an S3 XML error. See [Troubleshooting](/tools/s3-gateway/troubleshooting).                                |

### Buckets

| Capability                                                                                                       | Status                                                       | Notes                                                                                                                                                                                                                         |
| ---------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Bucket name                                                                                                      | <span aria-hidden="true">◐</span> Supported with differences | Create buckets in the console. Each access key reaches only its own bucket, and `ListBuckets` returns only that bucket. You can't create, delete or rename buckets over the S3 API. Use path-style addressing.                |
| GetBucketLocation, GetBucketVersioning, GetBucketAcl                                                             | <span aria-hidden="true">◐</span> Supported with differences | Answered with fixed values: versioning off, owner-only ACL. GetBucketLocation reports `default`. The AWS SDK for JavaScript gets the wrong answer to these. See [Known compatibility issues](/tools/s3-gateway/known-issues). |
| Bucket configuration: lifecycle, versioning, policy, CORS, website, replication, logging, notifications, tagging | <span aria-hidden="true">✕</span> Not supported              | Objects do not expire and are not versioned. Some of these calls are answered with `404` or a plain object listing instead of `501`. See [Known compatibility issues](/tools/s3-gateway/known-issues).                        |
| DeleteBucket                                                                                                     | <span aria-hidden="true">✕</span> Not supported              | Buckets are managed in the console, not over the S3 API.                                                                                                                                                                      |

## Clients

These clients are tested against the hosted endpoint. Each quickstart shows the full
configuration.

{/*
  GENERATED FRAGMENT. Do not edit by hand.
  Source: data/s3-compat.json, checked against the beta-harness corpus.
  Regenerate: pnpm generate:s3
  */}

| Client                                                              | Status              | What to set                                                                                                                  | Notes                                                                                                |
| ------------------------------------------------------------------- | ------------------- | ---------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| [AWS CLI v2](/tools/s3-gateway/quickstarts/aws-cli)                 | Works with settings | `request_checksum_calculation = when_required`                                                                               | `aws s3 mv` and Shelby-to-Shelby `aws s3 cp` need server-side copy, which isn't supported.           |
| [Python (boto3)](/tools/s3-gateway/quickstarts/python)              | Works with settings | `request_checksum_calculation="when_required"`                                                                               |                                                                                                      |
| [JavaScript (AWS SDK v3)](/tools/s3-gateway/quickstarts/javascript) | Works with settings | `responseChecksumValidation: "WHEN_REQUIRED"`, for presigned URLs. `requestChecksumCalculation: "WHEN_REQUIRED"` is optional | `DeleteObjectsCommand` doesn't work yet. Delete keys one at a time.                                  |
| [rclone](/tools/s3-gateway/quickstarts/rclone)                      | Works as-is         | None required. `--checksum` is recommended on copy and sync                                                                  |                                                                                                      |
| [DuckDB (httpfs)](/tools/s3-gateway/quickstarts/duckdb)             | Works with settings | `URL_STYLE 'path'`, and `s3_uploader_max_parts_per_file = 3000` for large writes                                             | Tested writing and reading Parquet and CSV, including multipart writes. DuckDB can't delete objects. |
| MinIO Client (`mc`)                                                 | Works with settings | `MC_REGION=default`, and `--path on` in `mc alias set`                                                                       | Tested listing, uploading and downloading objects.                                                   |

Other S3 clients usually work too, as long as they can use path-style addressing and
send requests without `aws-chunked` checksums.

## Why some things work differently

### You manage buckets in the console

On Shelby, you create buckets in the [console](https://console.shelby.xyz), not with S3
calls. A project can have many buckets, and each access key belongs to one of them (see
[Projects, buckets and access keys](/protocol/account-and-access#projects-buckets-and-access-keys)).
So, over the S3 API:

* You can't create, delete or rename buckets. `CreateBucket` on the access key's own
  bucket succeeds without doing anything, so clients that create the bucket before writing
  keep working. `DeleteBucket` is not available.
* `ListBuckets` returns only the access key's bucket. To work with several buckets, use
  each bucket's own access key.
* Clients must use path-style addressing. The endpoint doesn't serve bucket names as
  hostnames (`<bucket>.<endpoint>`).
* Bucket-level configuration such as lifecycle rules, policies and CORS doesn't exist.

### Every write is a durable commit

A PUT returns once the object is erasure-coded across many storage nodes and the write is
committed. That's why:

* Writes take longer than on S3.
* An overwrite stores a complete new object. Shelby has no metadata-only update.
* Re-uploading an unchanged file repeats the whole write, so sync tools should skip
  unchanged files to save time. This is why the rclone quickstart uses `--checksum`.
* Reads see a new or overwritten object as soon as the PUT returns, but it can take a few
  seconds to show up in listings.

### Encryption is always on

Every object is encrypted at rest with keys Shelby manages. You don't need to send any
encryption headers. Shelby doesn't accept customer-provided keys (SSE-C) or AWS KMS keys
(SSE-KMS), and refuses requests that ask for them rather than storing the data unencrypted.

### ETags

Single-part objects have the MD5 of their content as their ETag, as on S3, so identical
content has identical ETags. Multipart objects use S3's `<md5-of-part-md5s>-<part-count>`
form, which isn't the MD5 of the whole object (as on S3).

Objects written before mid-October 2026 have older ETags that aren't MD5s. Single-part ones
end in `-s`, which tells clients such as rclone not to treat them as MD5s, so those clients
compare older objects by size only.

## Known compatibility issues

A few responses differ from S3 for now, each with a workaround. None of them affect the
durability of stored data. See
[Known compatibility issues](/tools/s3-gateway/known-issues).

{/*
  GENERATED FRAGMENT. Do not edit by hand.
  Source: data/s3-compat.json, checked against the beta-harness corpus.
  Regenerate: pnpm generate:s3
  */}

*Last verified against the hosted endpoint on 2026-11-06.*
