Skip to main content

Supported APIs

LocalS3 routes every request to one of three kinds of handler: an operation it implements, an operation it knows and answers 501 NotImplemented, or the fallback handler for everything else. The lists below are checked against the routes of the router by SupportedApiDocumentationTest, so they can't drift from what the service answers.

For how the supported operations behave where Amazon S3 leaves room for interpretation, e.g. conditional requests and versioning, see semantics.md.

Many configurations below are stored and read back but never applied, e.g. notifications, replication and ACLs. Stored, not applied lists them in one table, with how each differs from Amazon S3.

Supported Amazon S3 APIs​

  • AbortMultipartUpload
  • CopyObject
  • CreateBucket
  • CreateMultipartUpload
  • CreateSession
  • CompleteMultipartUpload
  • DeleteBucket
  • DeleteBucketAnalyticsConfiguration
  • DeleteBucketCors
  • DeleteBucketEncryption
  • DeleteBucketIntelligentTieringConfiguration
  • DeleteBucketInventoryConfiguration
  • DeleteBucketLifecycle
  • DeleteBucketMetricsConfiguration
  • DeleteBucketOwnershipControls
  • DeleteBucketPolicy
  • DeleteBucketReplication
  • DeleteBucketTagging
  • DeleteBucketWebsite
  • DeleteObject
  • DeleteObjects
  • DeleteObjectTagging
  • GetObject
  • GetObjectTagging
  • GetObjectAcl
  • PutObjectAcl
  • GetObjectAttributes
  • GetObjectLegalHold
  • GetObjectLockConfiguration
  • GetObjectRetention
  • GetBucketAbac
  • GetBucketAccelerateConfiguration
  • GetBucketAcl
  • GetBucketAnalyticsConfiguration
  • GetBucketCors
  • GetBucketEncryption
  • GetBucketIntelligentTieringConfiguration
  • GetBucketInventoryConfiguration
  • GetBucketLifecycleConfiguration
  • GetBucketLogging
  • GetBucketMetricsConfiguration
  • GetBucketNotificationConfiguration
  • GetBucketOwnershipControls
  • GetBucketPolicy
  • GetBucketPolicyStatus
  • GetBucketReplication
  • GetBucketRequestPayment
  • GetBucketVersioning
  • GetBucketWebsite
  • GetBucketTagging
  • GetBucketLocation
  • HeadBucket
  • HeadObject
  • ListBucketAnalyticsConfigurations
  • ListBucketIntelligentTieringConfigurations
  • ListBucketInventoryConfigurations
  • ListBucketMetricsConfigurations
  • ListBuckets
  • ListDirectoryBuckets
  • ListObjects
  • ListObjectsV2
  • ListObjectVersions
  • ListMultipartUploads
  • ListParts
  • PutBucketAbac
  • PutBucketAccelerateConfiguration
  • PutBucketAcl
  • PutBucketAnalyticsConfiguration
  • PutBucketCors
  • PutBucketEncryption
  • PutBucketIntelligentTieringConfiguration
  • PutBucketInventoryConfiguration
  • PutBucketLifecycleConfiguration
  • PutBucketLogging
  • PutBucketMetricsConfiguration
  • PutBucketNotificationConfiguration
  • PutBucketOwnershipControls
  • PutBucketPolicy
  • PutBucketReplication
  • PutBucketRequestPayment
  • PutBucketVersioning
  • PutBucketWebsite
  • PutBucketTagging
  • PostObject
  • PutObject
  • PutObjectTagging
  • PutObjectLegalHold
  • PutObjectLockConfiguration
  • PutObjectRetention
  • RenameObject
  • RestoreObject
  • UploadPart
  • UploadPartCopy
  • PutPublicAccessBlock
  • GetPublicAccessBlock
  • DeletePublicAccessBlock
  • OPTIONS object (CORS preflight requests, answered without authentication)

The lifecycle configuration of a bucket is stored, and applied only when a test asks for it, with POST /_admin/lifecycle or LocalS3#applyLifecycle(Instant), at a time of its choosing, e.g. 30 days from now, or on a schedule that a long-running service turns on, e.g. LOCAL_S3_LIFECYCLE_INTERVAL=1h. See semantics.md. The deprecated PutBucketLifecycle and GetBucketLifecycle send the same requests, and are answered the same way.

The notification configuration of a bucket is stored and returned as it was put, but no event is sent to the SNS topics, SQS queues, Lambda functions or EventBridge it names, and their ARNs aren't checked, so that code that configures notifications when it starts, e.g. Terraform, CDK or an application, works against LocalS3. A bucket that was never configured answers an empty NotificationConfiguration. To hear of changes, register a listener; see change events. The deprecated PutBucketNotification and GetBucketNotification send the same requests, and are answered the same way.

Transfer acceleration, access logging, requester pays, ownership controls and attribute-based access control (ABAC) are stored and returned as they were put, but never applied: nothing is accelerated, logged or billed to the requester, ACLs keep working whatever the object ownership, and no access is decided by the tags of a bucket. They exist so that clients that read or write them, e.g. Terraform refreshing an aws_s3_bucket, or CDK and SDK tool chains setting the ownership controls, work against LocalS3. A bucket that was never configured answers like a new bucket of Amazon S3 does:

OperationNever configuredAfter the delete
GetBucketAccelerateConfigurationan empty AccelerateConfiguration–
GetBucketLoggingan empty BucketLoggingStatus–
GetBucketRequestPayment<Payer>BucketOwner</Payer>–
GetBucketOwnershipControls<ObjectOwnership>BucketOwnerEnforced</ObjectOwnership>404 OwnershipControlsNotFoundError
GetBucketAbac<Status>Disabled</Status>–

The analytics, intelligent-tiering, inventory and metrics configurations of a bucket, of which it has several, each named by the id parameter, are stored and returned as they were put, but never applied either: no storage class analysis runs, no object moves between access tiers, no inventory report is written and no CloudWatch metric is published. They exist so that Terraform's aws_s3_bucket_metric, aws_s3_bucket_inventory, aws_s3_bucket_analytics_configuration and aws_s3_bucket_intelligent_tiering_configuration, or CDK stacks that declare them, work against LocalS3. A bucket has none until one is put; getting or deleting an id it doesn't have answers 404 NoSuchConfiguration, and a PUT whose id isn't the <Id> of its document answers 400 InvalidArgument. The List… operations answer every configuration on one page, in the order of their IDs.

The website configuration of a bucket (PutBucketWebsite, GetBucketWebsite, DeleteBucketWebsite) is applied: its index and error documents, redirects and routing rules decide how the bucket is served as a website on the port of the S3 API, see semantics.md. A bucket that was never configured, or whose configuration was deleted, answers GetBucketWebsite with 404 NoSuchWebsiteConfiguration.

Object Lock (x-amz-bucket-object-lock-enabled, the x-amz-object-lock-* headers of PutObject, CopyObject and CreateMultipartUpload, retention and legal holds) protects object versions from being deleted, see semantics.md.

RestoreObject of a GLACIER or DEEP_ARCHIVE object completes at once, and HeadObject answers the restored copy with x-amz-restore, so that code that restores cold data before reading it works; see semantics.md.

ListDirectoryBuckets lists the buckets named like S3 Express One Zone directory buckets, e.g. my-bucket--usw2-az1--x-s3, paginated with max-directory-buckets (at most 1000) and continuation-token. It is a GET / like ListBuckets, told apart by its max-directory-buckets parameter or by the s3express service that the AWS SDKs sign it for. ListBuckets keeps listing every bucket, the directory buckets included.

RenameObject and appends (PutObject with x-amz-write-offset-bytes), which Amazon S3 offers for S3 Express One Zone directory buckets, work on the buckets whose versioning was never enabled, see semantics.md. A bucket named like a directory bucket, e.g. my-bucket--usw2-az1--x-s3, is addressed by the AWS SDKs the S3 Express way, with the stateless session credentials of CreateSession, see semantics.md.

Server-side encryption with customer-provided keys (SSE-C) is accepted and echoed, but nothing is encrypted, see semantics.md. The x-amz-server-side-encryption* headers of SSE-S3, SSE-KMS and DSSE-KMS are stored with the object and answered like Amazon S3 answers them, but nothing is encrypted either, see semantics.md.

PostObject is the upload of a file by an HTML form that a browser posts to a bucket, with its policy document and signature; see semantics.md.

ListBuckets is paginated with max-buckets, continuation-token, prefix and bucket-region, so listBucketsPaginator works. A request without them gets every bucket on one page, like Amazon S3 answers it; a paginated one gets at most max-buckets (1 to 10000) per page. Buckets are listed in the order they were created, and the token marks the last bucket of a page, so a bucket created or deleted between two pages neither repeats a bucket nor skips one.

Besides the S3 API, a service answers a health check and a few admin endpoints; see deployment.md.

Supported Amazon S3 Vectors APIs​

Vector Bucket Operations:

  • CreateVectorBucket
  • GetVectorBucket
  • ListVectorBuckets
  • DeleteVectorBucket

Vector Index Operations:

  • CreateIndex
  • GetIndex
  • ListIndexes
  • DeleteIndex

Vector Data Operations:

  • PutVectors
  • QueryVectors
  • GetVectors
  • ListVectors
  • DeleteVectors

An index of the cosine distance metric rejects a zero vector — one whose values are all 0 — as AWS does, since it has no direction: PutVectors and QueryVectors answer it with ValidationException.

A vector that contains NaN or an infinite value is rejected the same way by PutVectors and QueryVectors, for every distance metric, since its distance to any vector is NaN.

PutVectors also applies the limits of AWS to each vector, with ValidationException: a key of at most 1024 characters, metadata of at most 40 KB, and filterable metadata — the metadata without the nonFilterableMetadataKeys of the index — of at most 2 KB. A size is the one of the metadata written as JSON in UTF-8.

In the metadata filter of QueryVectors, every key of an object is a condition that must hold, $and and $or included: {"genre": "drama", "$or": [{"year": 2020}, {"year": 2021}]} matches a drama of 2020 or 2021. $gt, $gte, $lt and $lte take a number, and a filter that compares with anything else is rejected with InvalidRequestException. A vector whose value of the field isn't a number — a string "2019", say — doesn't match the comparison, as AWS does, instead of failing the query. The whole filter is validated before any vector is compared, so a malformed one is rejected with InvalidRequestException whatever the index holds, an empty index included, and however $and or $or would short-circuit: the filter must be an object ({} filters nothing), an operator must be one of $eq, $ne, $gt, $gte, $lt, $lte, $in, $nin, $exists, $and and $or, $and, $or, $in and $nin take a non-empty array, $eq, $ne and the elements of $in and $nin take a single value, and $exists takes a boolean. A key listed in the nonFilterableMetadataKeys of the index is not indexed for filtering, as on AWS: a filter treats it as absent from every vector, so {"text": "hello"} matches none of them while {"text": {"$exists": false}} matches all. Such a key is still stored and returned with returnMetadata.

ListVectors lists the vectors of an index in the order of their keys. Its nextToken marks the last key of the page it answers, so a listing continues after that key however the vectors change between the pages: no vector that exists for the whole listing is skipped or listed twice. With segmentCount and segmentIndex, a vector belongs to a segment by a hash of its key, so it is listed by one segment only, whatever other vectors are put or deleted. A nextToken that ListVectors didn't answer with is rejected with ValidationException, and so is a vector whose data can't be read when returnData is set, with InternalServerException. GetVectors answers such a vector among the error keys instead of with an empty data. Both read under the read lock of the vector bucket, as QueryVectors does, so a concurrent PutVectors or DeleteVectors doesn't drop the data of a vector while it's being read.

ListVectorBuckets and ListIndexes list in the order of the names, and their nextToken marks the last name of the page in the same way: a listing continues after that name however buckets or indexes are created or deleted between the pages. The token isn't bound to prefix, so a prefix changed between pages continues after that name too. A nextToken that they didn't answer with is rejected with ValidationException.

Vector Bucket Policy Operations:

  • PutVectorBucketPolicy
  • GetVectorBucketPolicy
  • DeleteVectorBucketPolicy

Tagging Operations (of a vector bucket or an index, addressed by its ARN; CreateVectorBucket and CreateIndex accept tags too):

  • TagResource
  • UntagResource
  • ListTagsForResource

Supported Amazon S3 Tables APIs​

The Amazon S3 Tables API, which an S3TablesClient of the AWS SDK and the s3-tables-catalog library of Iceberg speak. Every table bucket is also served as an Iceberg REST catalog of its own, so an engine reaches the same tables by giving a RESTCatalog the ARN of the table bucket as its warehouse.

A request of this API shares its paths with Amazon S3 — PUT /buckets is CreateTableBucket here and CreateBucket of a bucket named buckets there — so it is told apart by the s3tables service in the credential scope of its signature. A client that signs nothing reaches the API under /s3tables instead; see the S3 Tables API.

Table Bucket Operations:

  • CreateTableBucket
  • GetTableBucket
  • ListTableBuckets
  • DeleteTableBucket

Namespace Operations:

  • CreateNamespace
  • GetNamespace
  • ListNamespaces
  • DeleteNamespace

Table Operations:

  • CreateTable
  • GetTable
  • ListTables
  • DeleteTable
  • RenameTable
  • GetTableMetadataLocation
  • UpdateTableMetadataLocation

Tagging Operations (of a table bucket or a table, addressed by its ARN; CreateTableBucket and CreateTable accept tags too):

  • TagResource
  • UntagResource
  • ListTagsForResource

Encryption and Storage Class Operations (stored and read back; nothing is encrypted or tiered, see semantics.md):

  • PutTableBucketEncryption
  • GetTableBucketEncryption
  • DeleteTableBucketEncryption
  • GetTableEncryption
  • PutTableBucketStorageClass
  • GetTableBucketStorageClass
  • GetTableStorageClass

Resource Policy Operations (stored and read back; nothing is enforced):

  • PutTableBucketPolicy
  • GetTableBucketPolicy
  • DeleteTableBucketPolicy
  • PutTablePolicy
  • GetTablePolicy
  • DeleteTablePolicy

Maintenance and Metrics Operations (stored and read back; no job ever runs):

  • PutTableBucketMaintenanceConfiguration
  • GetTableBucketMaintenanceConfiguration
  • PutTableMaintenanceConfiguration
  • GetTableMaintenanceConfiguration
  • GetTableMaintenanceJobStatus
  • PutTableBucketMetricsConfiguration
  • GetTableBucketMetricsConfiguration
  • DeleteTableBucketMetricsConfiguration

Record Expiration Operations (stored and read back; no record ever expires):

  • PutTableRecordExpirationConfiguration
  • GetTableRecordExpirationConfiguration
  • GetTableRecordExpirationJobStatus

Replication Operations (stored and read back; nothing is replicated):

  • PutTableBucketReplication
  • GetTableBucketReplication
  • DeleteTableBucketReplication
  • PutTableReplication
  • GetTableReplication
  • DeleteTableReplication
  • GetTableReplicationStatus

Supported AWS STS APIs​

A stateless STS endpoint on the same port issues temporary credentials of LocalS3, like the one of MinIO; see embedding.md. STS requests are POST / with a form-urlencoded body.

  • AssumeRole
  • GetSessionToken
  • GetCallerIdentity

Supported AWS KMS APIs​

A stateless KMS endpoint on the same port wraps and unwraps data keys, so that a client which calls KMS before it talks to S3, e.g. the Amazon S3 Encryption Client, runs against LocalS3; see embedding.md. KMS requests are POST / with an X-Amz-Target: TrentService.<action> header and a JSON body. Nothing is really encrypted, see semantics.md.

  • GenerateDataKey
  • GenerateDataKeyWithoutPlaintext
  • Encrypt
  • Decrypt
  • DescribeKey
  • GenerateRandom

Known unimplemented Amazon S3 APIs​

LocalS3 is a mock for testing, so it implements the operations that application code exercises and leaves the ones that configure a real bucket's billing, hosting and reporting alone. The operations below are routed and answer 501 NotImplemented with an <Error> document naming the operation, so a client fails with a clear error instead of appearing to succeed. If your tests need one of them, please open an issue.

Object retrieval and transformation

  • GetObjectTorrent
  • SelectObjectContent
  • WriteGetObjectResponse

Object encryption

  • UpdateObjectEncryption

Object annotations

  • DeleteObjectAnnotation
  • GetObjectAnnotation
  • ListObjectAnnotations
  • PutObjectAnnotation

S3 Metadata (journal, inventory and annotation tables)

  • CreateBucketMetadataConfiguration
  • CreateBucketMetadataTableConfiguration
  • DeleteBucketMetadataConfiguration
  • DeleteBucketMetadataTableConfiguration
  • GetBucketMetadataConfiguration
  • GetBucketMetadataTableConfiguration
  • UpdateBucketMetadataAnnotationTableConfiguration
  • UpdateBucketMetadataInventoryTableConfiguration
  • UpdateBucketMetadataJournalTableConfiguration

Not routed at all​

Every other operation of the S3 API that isn't listed on this page has no route, and answers 501 NotImplemented from the fallback handler, e.g. LocalS3 does not implement PATCH /<bucket>.

That includes a request that carries a subresource of Amazon S3, i.e. a query parameter that names an operation, such as ?acl, ?cors, ?uploadId or ?metadataConfiguration, which no route of its method and path answers. Without this rule, such a request would reach the operation that its method and path name without parameters, and appear to succeed: GET /<bucket>?cors would be listed by ListObjects, PUT /<bucket>?location would create the bucket, POST /<bucket>?tagging would be taken for a form upload, and DELETE /<bucket>/<key>?acl would delete the object. A query parameter that names no operation, e.g. ?v=3 of a cache-busting link, is ignored, like Amazon S3 does. A CORS preflight (OPTIONS) is answered whatever its parameters.

The S3 Control API (access points, batch operations, Storage Lens, …) isn't implemented either. Its requests, whose paths start with /v20180820/ and which carry an x-amz-account-id or x-amz-outpost-id header or are signed for s3-control, answer 501 NotImplemented with an error that names S3 Control, rather than NoSuchBucket of a bucket named v20180820.

LocalS3SdkOperationsTest reads the HTTP binding of every operation of the S3Client of the AWS SDK for Java, and checks that each one reaches its own route: an operation that the SDK adds is either implemented or listed above, never answered by another operation.

GET /_admin/stats counts the requests answered 501 under notImplemented, by operation, e.g. SelectObjectContent, or, for a request without a route, by its method, the shape of its path and its query parameters, e.g. GET /{bucket}?analytics. It tells which operations your client needs that LocalS3 lacks.