Compatibility
Which S3 features Shelby supports, which work differently, and which are not available.
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
Objects
| Capability | Status | Notes |
|---|---|---|
| PutObject, GetObject, HeadObject | Supported | Single PUTs up to 5 GiB. Range reads are supported. |
| Read-after-write consistency | 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. |
| Overwrite an existing key | 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: *) | Supported | Returns 412 PreconditionFailed if the key already exists. |
Conditional overwrite (If-Match on PUT) | 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. |
Conditional reads (If-Match, If-None-Match, If-Modified-Since, If-Unmodified-Since) | Not supported | Accepted and ignored. GET always returns the full object. |
| DeleteObject | 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) | 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. |
CopyObject, UploadPartCopy, aws s3 mv | Not supported | Returns 501 NotImplemented. Download and re-upload instead. |
Multipart uploads
| Capability | Status | Notes |
|---|---|---|
| Create, UploadPart, Complete, Abort, ListParts, ListMultipartUploads | 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. |
| Parts per upload | 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 | 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 | 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 | Supported | A mismatch is rejected with 400 BadDigest. |
Flexible checksums (x-amz-checksum-*, aws-chunked uploads) | 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 shows the one setting that turns them off. |
Object metadata
| Capability | Status | Notes |
|---|---|---|
| Content-Type, Cache-Control, Content-Disposition, Content-Encoding, Content-Language, Expires | Supported | Stored and returned. Objects stored before Shelby kept metadata come back as binary/octet-stream. |
User metadata (x-amz-meta-*) | 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 | 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 | 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 | 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) | Supported | One object per URL, for up to 7 days. Sign with region default. See Presigned URLs. |
| Presigned uploads, deletes and listings | 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. |
| CORS and browser uploads | 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 | Supported with differences | Bad or missing credentials return 401 with a plain-text body instead of an S3 XML error. See Troubleshooting. |
Buckets
| Capability | Status | Notes |
|---|---|---|
| Bucket name | 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 | 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. |
| Bucket configuration: lifecycle, versioning, policy, CORS, website, replication, logging, notifications, tagging | 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. |
| DeleteBucket | 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.
| Client | Status | What to set | Notes |
|---|---|---|---|
| AWS CLI v2 | 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) | Works with settings | request_checksum_calculation="when_required" | |
| JavaScript (AWS SDK v3) | 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 | Works as-is | None required. --checksum is recommended on copy and sync | |
| DuckDB (httpfs) | 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, 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). So, over the S3 API:
- You can't create, delete or rename buckets.
CreateBucketon the access key's own bucket succeeds without doing anything, so clients that create the bucket before writing keep working.DeleteBucketis not available. ListBucketsreturns 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.
Last verified against the hosted endpoint on 2026-11-06.