Library / SDK
durch/rust-s3 avatar
durch/rust-s3

rust-s3 talks to S3 and the services that imitate it, on any runtime

Rust library for interfacing with S3 API compatible services

677 stars260 forksRustMIT

At a glance

What is it?
rust-s3 is a Rust library for Amazon S3 and for the many services that speak its API, with the async runtime and the TLS stack chosen by Cargo features, a blocking variant of every method, and presigned URLs as the headline capability. The README also tells you when a service will need an escape hatch, and the repository carries a CI matrix that tests ten configurations.
Who is it for?
Adopt rust-s3 when your Rust service needs to talk to S3 or to a compatible service such as Backblaze B2, Wasabi, Minio, Yandex or Google Cloud Storage, and when you want to choose between Tokio, async-std and a plain blocking client without changing your call sites. Do not adopt it if you need a minimal dependency footprint, since the crate brings in a TLS stack and an HTTP client whatever you choose.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 15 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A modest interface over a wide set of services

The README calls the interface modest, and that is the right word. It supports `put`, `get`, `list`, `delete`, operations on `tags` and `location`, as well as `head`, and it works against Amazon S3 and arbitrary S3 compatible object storage APIs such as Backblaze B2, Wasabi, Yandex, Minio or Google Cloud Storage.

The compatibility list in the repository topics matches the prose, with entries for backblaze-b2, gcp-storage, minio, r2, wasabi and yandex alongside aws-s3.

Two behaviours are worth knowing before you write code against it. First, the library is fully compatible with `async/await` and `futures ^0.3`, but all the async features can be turned off and sync only implementations used, so the same crate serves a Tokio service and a command line tool. Second, all requests are automatically retried once, and that behaviour can be further modified with a `set_retries` call.

That retry default is the sort of detail that matters in production. A single implicit retry changes the failure semantics of every call, so an operation that appears to have failed may in fact have succeeded on the second attempt, and a caller that does not know about it may act on a stale assumption. The `set_retries` call is the escape hatch, but you have to know it exists.

Cargo features decide the runtime and the TLS stack

The feature list is where the design shows. The default set is the `tokio` runtime and a `native-tls` implementation. The `blocking` feature generates a `*_blocking` variant of all `Bucket` methods, so without it only async versions are available. And `fail-on-err` returns `Result::Err` for HTTP errors.

With `default-features = false` you get more choices. `with-async-std` selects the `async-std` runtime with the `surf` client. `async-std-native-tls` and `async-std-rustls-tls` open a fresh HTTP/1 connection for each request to honour server `Connection: close` responses, and the README is unusually precise about the cost: this adds TCP and TLS setup overhead and disables Surf's HTTP/1 pool, so its implicit per-host connection cap no longer applies. The `sync` feature means no async runtime at all, with `attohttpc` used for HTTP requests. And `tags` is required for `Bucket::get_object_tagging`.

All runtimes support either `native-tls` or `rustls-tls`, and the README points to `s3/Cargo.toml` for the complete list of combinations. To build with Tokio and rustls backed by ring, you disable the default native-TLS feature:

bash
cargo build -p rust-s3 --no-default-features --features tokio-rustls-tls-ring

The default feature set remains Tokio with native TLS.

One practical note about the `blocking` feature. The `*_blocking` variants are generated rather than hand written, which means the blocking API is a second surface rather than a thin wrapper over the async one, and the two can behave differently around cancellation and partial reads. If you plan to mix them, read both.

The examples cover each provider and each runtime

The quick start is a Cargo dependency and a folder of examples. The dependency:

toml
[dependencies]
rust-s3 = "0.37.2"

And then the README tells you to read and run examples from the `examples` folder, making sure you have valid credentials for the variant you are running. The commands are one per target:

bash
# tokio, default
cargo run --example tokio

# async-std
cargo run --example async-std --no-default-features --features async-std-native-tls

# sync
cargo run --example sync --no-default-features --features sync-native-tls

# minio
cargo run --example minio

# r2
cargo run --example r2

# google cloud
cargo run --example google-cloud

The repository listing shows the same set with the runtime in the file name, including `async-std-backend.rs`, `gcs-tokio.rs`, `minio-tokio.rs`, `r2-tokio.rs`, `sync-backend.rs` and `tokio-backend.rs`.

Note the version skew in the dependency snippet. The quick start pins `0.37.2`, while the newest tag listed for the repository is v0.38.0 from 2026-10-04, ahead of v0.37.1 from 2025-12-09 and v0.37.0 from 2025-09-16. Copying the quick start verbatim gives you a version that is not the current one, which is a small thing but the kind of small thing that a reader will trust for a long time.

Presigned URLs are the capability worth reading the docs for

The README singles out one method: a dedicated `presign_get` `Bucket` method is available. The reason it gets its own sentence is the use case. This means you can upload to S3 and give the link to select people without having to worry about publicly accessible files on S3.

And the same mechanism works in the other direction. A `PUT` presigned URL means you can give people a way to upload to a specific key in S3 for the duration of the presigned URL. That is a genuinely useful pattern for anything where a client has to send a file to your bucket without holding your credentials.

The API table covers four verbs, `POST` through `presign_post`, `PUT` through `presign_put`, `GET` through `presign_get` and `DELETE` through `presign_delete`. Four verbs rather than one is the point: the same mechanism that lets a user download a private object also lets an application hand out a time limited upload target.

The rest of the object API is more conventional. The `Bucket` operations are `create`, `delete`, `list` and `exists`. For getting an object there are a few different options, and the README explains that `sync` and `async` methods are generic over `std::io::Write` while `tokio` methods are generic over `tokio::io::AsyncWriteExt`, with `get_object` and `get_object_stream` in the table. The generic over the writer is the detail that matters if you plan to pipe an object straight into a file or a socket.

Path style, subdomain style and one environment escape hatch

Two configuration details sit outside the Cargo features, and both are about talking to services that are not exactly Amazon.

The first is URL shape. The `Bucket` struct provides constructors for `path-style` paths, and `subdomain` style is the default. The `Bucket` exposes methods for configuring and accessing the path-style configuration, which matters because a self-hosted Minio or a LocalStack instance is usually addressed by path while Amazon is addressed by subdomain. The `blocking` feature generates a `*_blocking` variant of all the methods, so the same choice applies to the blocking API.

The second is a specific compatibility problem with LocalStack. When using LocalStack, you may need to skip sending the location constraint in bucket creation requests, because LocalStack does not support location constraints in the request body and will return `InvalidLocationConstraint` errors. The fix is an environment variable:

bash
export RUST_S3_SKIP_LOCATION_CONSTRAINT=true
# or
export RUST_S3_SKIP_LOCATION_CONSTRAINT=1

The README notes that this may also be needed for other S3-compatible services that do not support AWS-style location constraints.

Both of these are the shape of this library in practice. The interface is clean and the compatibility surface is broad, and where a service deviates from Amazon's behaviour the library tends to answer with a flag or an environment variable rather than with an abstraction. That is a reasonable trade, and it does mean you will spend time in the README when a new provider misbehaves.

A workspace with a CI matrix and a lot of governance files

The repository is a Cargo workspace with three members:

toml
[workspace]
members = [
    "s3",
    "aws-region",
    "aws-creds"
]

resolver = "2"

So the S3 client, the region handling and the credential handling are separate crates, and the workspace is what you depend on as `rust-s3`.

The Makefile is where the testing strategy is visible. It starts with a `.NOTPARALLEL` declaration for the CI and test phases against the shared workspace target directory, with the comment explaining why. The default target is `all: ci`, and the README in the file calls plain `make` the full credential-free developer and CI path. The `ci` target is `fmt-check clippy test`, and `ci-support` adds the support crates. There are per-runtime targets, `ci-tokio`, `ci-async-std` and `ci-sync`, each delegating into the `s3` directory.

The distinction between the credential-free path and the rest is the important one. `ci-all` and `test-all` add the `integration-test` target, and the file says explicitly that the explicit all-tests path may contact configured object storage providers. The integration test runs the ignored tests, which is how a library like this tests against real services.

Around that sit the process files. The top level has `AUDIT.md`, `PR_TRIAGE.md`, `RELEASING.md`, `RELEASE_NOTES.md`, `TESTING.md`, `CONTRIBUTING.md`, `CODE_OF_CONDUCT.md`, `CLAUDE.md` and a `Makefile`, which is a lot of governance for a client library and tells you something about how the project is run. The licence is MIT, with the terms in `LICENSE.md`.

Editorial conclusion

Adopt rust-s3 when your Rust service needs to talk to S3 or to a compatible service such as Backblaze B2, Wasabi, Minio, Yandex or Google Cloud Storage, and when you want to choose between Tokio, async-std and a plain blocking client without changing your call sites. Do not adopt it if you need a minimal dependency footprint, since the crate brings in a TLS stack and an HTTP client whatever you choose. Verify first that the version you depend on is current, since the README's quick start still shows `rust-s3 = "0.37.2"` while the newest tag is v0.38.0, that the feature combination you enable matches the runtime you actually use, and that the `fail-on-err` behaviour you want, since without it HTTP errors are not surfaced as `Result::Err`.

Frequently asked questions

Which S3 compatible services does rust-s3 work with?

The README names Backblaze B2, Wasabi, Yandex, Minio and Google Cloud Storage alongside Amazon S3, and the repository topics list the same set. The examples folder includes runs against Minio, Cloudflare R2 and Google Cloud Storage.

How do I use rust-s3 without an async runtime?

Turn off the default features and enable the `sync` feature, which uses attohttpc for HTTP requests and no async runtime. The README's quick start runs it with `cargo run --example sync --no-default-features --features sync-native-tls`, and the `blocking` feature generates a `*_blocking` variant of every Bucket method.

What does the fail-on-err feature do in rust-s3?

It returns `Result::Err` for HTTP errors, so an HTTP level failure becomes an error in the return value rather than something the caller has to look for. Without it, errors are not surfaced that way, which is worth checking against your own error handling before you rely on a `?`.

How do I create a bucket against LocalStack with rust-s3?

Set the environment variable `RUST_S3_SKIP_LOCATION_CONSTRAINT` to `true` or `1` so the location constraint is not sent in the bucket creation request, because LocalStack returns `InvalidLocationConstraint` errors when it is. The README notes the same may be needed for other services that do not support AWS-style location constraints.

Official sources

  1. durch/rust-s3 on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/durch-rust-s3.svg)](https://hysenlabs.com/projects/durch-rust-s3)

Was this useful?