s3 0.1.22

A lean, modern, unofficial S3-compatible client for Rust.
docs.rs failed to build s3-0.1.22
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
Visit the last successful build: s3-0.1.21

s3-rs

crates.io docs.rs CI

Lean, modern, unofficial S3-compatible client for Rust.

s3-rs logo

Highlights

  • Async + blocking clients with closely aligned APIs
  • Presigned URLs and multipart upload
  • Optional checksums, tracing, and metrics
  • Integration-tested against MinIO and RustFS
  • Small dependency surface (feature-gated)
  • Structured errors (status/code/request id/body snippet)

Install

# async + rustls (default)
cargo add s3

# blocking (disable defaults, pick one TLS backend)
cargo add s3 --no-default-features --features blocking,rustls

# async + native-tls
cargo add s3 --no-default-features --features async,native-tls

Mental model

Most applications only touch a small set of layers:

  • Auth decides how requests are signed: anonymous, env credentials, a custom provider, or optional IMDS/STS/profile flows
  • Client::builder(...) / BlockingClient::builder(...) capture endpoint, region, retry, TLS, and addressing policy once
  • client.objects() and client.buckets() return typed request builders for object and bucket operations
  • s3::types contains the public request/response models you work with; protocol XML DTOs stay internal
  • s3::providers offers endpoint presets for common S3-compatible services when enabled

Usage

Most code follows the same flow: choose Auth, build a client, then use objects() or buckets() to send requests and consume public output types from s3::types.

Async

use s3::{Auth, Client};

# async fn demo() -> Result<(), s3::Error> {
let client = Client::builder("https://s3.example.com")?
    .region("us-east-1")
    .auth(Auth::from_env()?)
    .build()?;

let obj = client.objects().get("my-bucket", "path/to/object.txt").send().await?;
let bytes = obj.bytes().await?;
println!("{} bytes", bytes.len());
# Ok(())
# }

Auth::from_env() reads AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and optionally AWS_SESSION_TOKEN.

Blocking

use s3::{Auth, BlockingClient};

fn demo() -> Result<(), s3::Error> {
    let client = BlockingClient::builder("https://s3.example.com")?
        .region("us-east-1")
        .auth(Auth::from_env()?)
        .build()?;

    let obj = client.objects().get("my-bucket", "path/to/object.txt").send()?;
    let bytes = obj.bytes()?;
    println!("{} bytes", bytes.len());
    Ok(())
}

Configuration

use std::time::Duration;

use s3::{AddressingStyle, AsyncTlsRootStore, Auth, Client};

# async fn demo() -> Result<(), s3::Error> {
let client = Client::builder("https://s3.example.com")?
    .region("us-east-1")
    .auth(Auth::from_env()?)
    .addressing_style(AddressingStyle::Auto)
    .tls_root_store(AsyncTlsRootStore::BackendDefault)
    .timeout(Duration::from_secs(30))
    .max_attempts(3)
    .build()?;
# Ok(())
# }

Operation services

use s3::{Auth, Client};

# async fn demo() -> Result<(), s3::Error> {
let client = Client::builder("https://s3.example.com")?
    .region("us-east-1")
    .auth(Auth::from_env()?)
    .build()?;

let buckets = client.buckets().list().send().await?;
let objects = client
    .objects()
    .list_v2("my-bucket")
    .prefix("logs/")
    .send()
    .await?;

println!("{} buckets, {} objects", buckets.buckets.len(), objects.contents.len());
# Ok(())
# }

Presign

use s3::{Auth, Client};

# async fn demo() -> Result<(), s3::Error> {
let client = Client::builder("https://s3.example.com")?
    .region("us-east-1")
    .auth(Auth::from_env()?)
    .build()?;

let presigned = client
    .objects()
    .presign_get("my-bucket", "path/to/object.txt")
    .build()?;

println!("GET {}", presigned.url);
# Ok(())
# }

Endpoint presets (feature = providers)

use s3::{Auth, providers};

# async fn demo() -> Result<(), s3::Error> {
let preset = providers::minio_local();
let client = preset
    .async_client_builder()?
    .auth(Auth::from_env()?)
    .build()?;
# Ok(())
# }

Authentication and credentials

Use Auth as the single entry point for signing strategy. Static credentials, refreshable providers, and optional cloud credential flows all feed the same client builder API.

  • Auth::Anonymous: unsigned requests (for public buckets / anonymous endpoints)
  • Auth::from_env(): static credentials from env vars
  • Auth::provider(...): plug in your own refreshable provider (cached/singleflight refresh)
  • Auth::from_imds_with_tls_root_store(...) / Auth::from_web_identity_env_with_tls_root_store(...): explicit TLS root policy for credentials fetch
  • Optional features:
    • credentials-profile: shared config/profile loader
    • credentials-imds: IMDS credentials (async/blocking APIs)
    • credentials-sts: web identity / STS flows

Compatibility

  • Addressing styles: AddressingStyle::Auto (default), Path, VirtualHosted
  • Targets: any S3-compatible service; CI runs against MinIO and RustFS

Feature flags

  • Modes: async (default), blocking
  • TLS: rustls (default), native-tls
  • Optional: multipart, checksums, providers, credentials-profile, credentials-imds, credentials-sts, tracing, metrics

Examples

Examples follow the same layering as the crate docs: choose Auth, build a client, then work through objects() or buckets().

Core object and bucket flows

  • Basic object lifecycle: examples/async_put_get_delete.rs
  • List buckets: examples/async_list_buckets.rs
  • List objects v2 + pagination: examples/async_list_objects.rs
  • Batch delete: examples/async_delete_objects_batch.rs
  • Copy object + replace metadata: examples/async_copy_object.rs
  • Streaming upload (requires Content-Length): examples/async_put_stream.rs
  • Multipart upload (feature = multipart): examples/async_multipart_upload.rs

Auth, presign, and endpoint presets

  • Presign with static credentials: examples/presign_get.rs
  • Presign with a refreshable provider: examples/async_presign_build_async.rs
  • IMDS credentials (feature = credentials-imds): examples/async_auth_imds.rs
  • Web identity credentials (feature = credentials-sts): examples/async_auth_web_identity.rs
  • MinIO local preset (feature = providers): examples/minio_local_put_get_delete.rs
  • Cloudflare R2 preset (feature = providers): examples/r2_put_get_delete.rs

TLS and blocking flows

  • Async request TLS root policy: examples/async_tls_root_store.rs
  • Blocking request TLS root policy: examples/blocking_tls_root_store.rs
  • Blocking object lifecycle: examples/blocking_put_get_delete.rs
  • Blocking list buckets: examples/blocking_list_buckets.rs
  • Blocking presign: examples/blocking_presign_get.rs

See examples/README.md for environment variables and feature requirements.

Development

Run just ci after local changes to validate formatting, feature combinations, tests, and clippy.