RustFS: S3-Compatible Object Storage

The Architecture Rationale

While a hardened SFTP backend is highly efficient for standard web environments, specific cloud-native applications and backup pipelines demand strict S3 API compliance (such as AWS Signature V4 and bucket policies). Rather than deploying a heavy enterprise storage array, I chose to evaluate RustFS—a cutting-edge, open-source S3-compatible object store built entirely in Rust. Known for its memory safety, high performance, and minimal idle CPU footprint, RustFS serves as a modern, lightweight alternative to MinIO.

Phase 1: Rapid Deployment on Proxmox

To ensure clean package execution and service isolation, RustFS was deployed within a Debian Linux LXC utilizing the Proxmox Helper Scripts as a foundation.

Rather than compiling from source or configuring binaries manually, I used the official one-click installation script to set up RustFS in Single Node Single Disk (SNSD) mode. This provides a clean production-ready directory tree and default service parameters instantly.

Bash

# Execute the official one-click installer inside the Debian LXC console
curl -O https://rustfs.com/install_rustfs.sh && bash install_rustfs.sh

⚙️ Deployment Notes:

  • The installer binds the API to the default port 9000 (and console to 9001).
  • The default storage directory is initialized at /data/rustfs0.
  • Ensure unzip is pre-installed on the Debian system so the script can seamlessly unpack the binary payload.

Phase 2: S3 Bucket Policy Hardening

By default, newly initialized buckets in RustFS are highly permissive. To secure the persistent data layer, strict S3 Bucket Policies (complying with AWS IAM syntax) must be applied to lock down bucket privileges.

Implementing the Wildcard Policy

When creating access policies for your applications (such as backup agents or development frameworks), a common mistake is only granting permissions to the bucket itself. S3 API conventions require permissions for both the Bucket container (for metadata and listing) and the Objects inside the bucket (for writing and retrieving).

To grant an application full read/write access to a specific bucket, the following S3 policy layout is applied:

JSON

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowAppBucketAccess",
      "Effect": "Allow",
      "Principal": "*",
      "Action": [
        "s3:ListBucket",
        "s3:GetBucketLocation",
        "s3:PutObject",
        "s3:GetObject",
        "s3:DeleteObject"
      ],
      "Resource": [
        "arn:aws:s3:::my-app-bucket",
        "arn:aws:s3:::my-app-bucket/*"
      ]
    }
  ]
}

Note how both arn:aws:s3:::my-app-bucket and the wildcard resource suffix arn:aws:s3:::my-app-bucket/* are declared. Without the trailing /* wildcard, actions like GetObject and PutObject will fail with an Access Denied (403) error, because the policy engine cannot match permissions to individual file keys. You can also clone the system’s default policies and append these paths to save manual policy mapping.

Phase 3: Architectural Evaluation

During testing, RustFS lived up to its reputation as a remarkably fast, stable, and memory-safe storage layer. However, the decision to migrate away was a matter of operational fit:

  • Why RustFS? It is exceptionally fast, boasts a clean, modern design, and supports complex S3 bucket policies natively.
  • Why it was replaced: For my specific workloads, configuring and maintaining S3 bucket policies and dealing with S3 signature variations introduced more complexity than necessary. Switching to a customized, hardened Alpine SFTP service solved my storage needs while drastically reducing the time spent managing security policies.