RustFS: A MinIO Replacement That Actually Gets S3 Semantics Right

|7 min read|

A look at RustFS: a single-binary, open-source S3-compatible object store written in Rust. Covers the Docker Compose deployment, OIDC integration with Authentik, and why MinIO's community edition stopped being a safe long-term bet.

Yoga Novaindra

Author

MinIO used to be the default answer to "self-hosted S3." That stopped being true over the course of about a year, in stages gradual enough that it was easy to miss until the community edition had effectively no future left. I moved my homelab's object storage to RustFS, a MinIO-API-compatible server written from scratch in Rust, and this is the reasoning behind that move: what changed with MinIO, what RustFS actually offers instead, and how the deployment looks running behind Traefik with OIDC.


1. The MinIO Problem

MinIO earned its default status honestly: it defined the API surface that most of the self-hosted-S3 ecosystem built against, RustFS included. But the community edition's decline happened in three distinct, spaced-out stages, and each one looked survivable in isolation.

  • May 2025, the admin console went commercial. MinIO stripped the administrative console out of the community edition entirely, taking user management, bucket policies, and IAM configuration with it. Everything that made MinIO manageable from a browser moved behind the commercial AIStor product.
  • October 2025, the binaries stopped shipping. MinIO stopped publishing pre-built binaries and Docker images for the community edition. Anyone still on it was either self-compiling from source or frozen on a stale version with no update path forward.
  • April 2026, the repository went read-only. The minio/minio GitHub repository was archived. No further commits, no security patches, no maintenance of any kind, on a project a large share of the self-hosted world had standardized on.

Individually, each step reads as an ordinary product decision. Strung together, they describe a project being deliberately wound down as a free option in favor of pushing users onto a commercial tier. That's a legitimate business strategy for MinIO to run. It's also a good reason not to build new infrastructure on the free tier of something that has already shown it will get gated, then frozen, then archived out from under you.


2. Introducing RustFS

RustFS is a modern, open-source object storage server that implements the S3 API with the same fidelity MinIO does. It offers full compatibility with the MinIO API surface, so any tool built against MinIO works against RustFS unmodified, while giving away the parts MinIO now gates:

  • Complete MinIO API surface. Buckets, objects, multipart uploads, presigned URLs, lifecycle policies, access policies. A drop-in swap, not a migration project.
  • The full admin console, given away rather than gated. User management, bucket policies, access key rotation, OIDC session visibility, and storage metrics are all available from the browser with no license key required, the exact feature set MinIO moved behind AIStor in May 2025.
  • OIDC-native. OpenID Connect is a first-class feature, not a plugin. It integrates directly with Authentik, Keycloak, Dex, or any standards-compliant OIDC provider.
  • No license restrictions, and no track record yet of introducing them. Fully open source, no AGPL commercial carve-outs, no feature gating behind enterprise tiers. Worth flagging as a caveat rather than a guarantee: that's also what MinIO's community edition looked like before May 2025.

RustFS can run anywhere you can run a Docker container or a Linux binary: bare metal, a VM on any cloud provider, a Raspberry Pi, a homelab NUC. It just needs a storage volume and network access.


3. Architecture Overview

In my setup, RustFS runs as a Docker Compose service alongside Traefik on a dedicated host, with Authentik as the OIDC provider handling console login.

Anything that speaks S3 can plug into this regardless of where it runs: Kubernetes, Docker Compose, bare metal, another cloud. The RustFS host just needs to be network-reachable with a valid TLS certificate.


4. Deploying RustFS with Docker Compose

The full deployment is a single Compose service. Traefik handles TLS and routing; RustFS handles everything else.

services:
  rustfs:
    image: rustfs/rustfs:latest
    container_name: rustfs
    restart: unless-stopped
    command: ["--address", ":9000", "--console-enable", "--server-domains", "rustfs.ygnv.my.id", "/data"]
    environment:
      - RUSTFS_ACCESS_KEY=your-access-key
      - RUSTFS_SECRET_KEY=your-secret-key
      - RUSTFS_CONSOLE_ENABLE=true
      - RUSTFS_SERVER_DOMAINS=rustfs.ygnv.my.id
      - RUSTFS_REGION=asia-east-1
      # OIDC Configuration
      - RUSTFS_IDENTITY_OPENID_CONFIG_URL=https://auth.ygnv.my.id/application/o/rustfs/.well-known/openid-configuration
      - RUSTFS_IDENTITY_OPENID_CLIENT_ID=your-oidc-client-id
      - RUSTFS_IDENTITY_OPENID_SCOPES=openid,profile,email,rustfs_policy
      - RUSTFS_IDENTITY_OPENID_DISPLAY_NAME=Authentik
      - RUSTFS_IDENTITY_OPENID_CLAIM_NAME=policy
      - RUSTFS_IDENTITY_OPENID_CLAIM_USERINFO=on
      # Logging
      - RUSTFS_OBS_LOG_STDOUT_ENABLED=true
      - RUST_LOG=warn
    ports:
      - "9000:9000"
      - "9001:9001"
    volumes:
      - rustfs:/data
    networks:
      - traefik
    labels:
      - traefik.enable=true
      # API router: handles all S3 API calls
      - traefik.http.routers.rustfs-api.rule=Host(`rustfs.ygnv.my.id`)
      - traefik.http.routers.rustfs-api.entrypoints=http,https
      - traefik.http.routers.rustfs-api.service=rustfs-api
      - traefik.http.routers.rustfs-api.tls=true
      - traefik.http.services.rustfs-api.loadbalancer.server.port=9000
      # Console router: scoped to /rustfs/console path
      - traefik.http.routers.rustfs-console.rule=Host(`rustfs.ygnv.my.id`) && PathPrefix(`/rustfs/console`)
      - traefik.http.routers.rustfs-console.entrypoints=http,https
      - traefik.http.routers.rustfs-console.service=rustfs-console
      - traefik.http.routers.rustfs-console.tls=true
      - traefik.http.services.rustfs-console.loadbalancer.server.port=9001
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

networks:
  traefik:
    external: true

volumes:
  rustfs:
    name: rustfs
RustFS admin console

Key Configuration Decisions

/data as the storage root. The positional argument passed to the RustFS binary. A Docker volume backs this path for stable storage across restarts; swap in local disk, NFS, CephFS, or a cloud block device as needed.

--server-domains rustfs.ygnv.my.id. Tells RustFS which domain to serve. Combined with s3forcepathstyle: true on clients, this routes path-style requests correctly through Traefik without needing wildcard DNS for bucket subdomains.

Dual Traefik router setup. The S3 API and the management console run on different internal ports, 9000 and 9001. Two routers share the same hostname: the console router uses PathPrefix to capture /rustfs/console and forward it to port 9001, everything else goes to the S3 API on 9000. Without this split, the console returns a blank page because Traefik can't distinguish the two services.

OIDC via Authentik. Authentication is delegated entirely to the OIDC provider. The rustfs_policy scope claim (RUSTFS_IDENTITY_OPENID_CLAIM_NAME=policy) lets Authentik push a policy name (readwrite, readonly) directly in the token, so users who log in via Authentik get the right RustFS access policy automatically, with no manual IAM user management inside RustFS itself.

RUST_LOG=warn. At info level, RustFS logs every S3 request. A busy bucket generates thousands of calls per hour; warn keeps the container log driver from flooding while still surfacing real errors.


5. A Quick Example: Backing Grafana Loki and Tempo

As one example of what a demanding S3 client actually requires: Grafana Loki's compactor writes retention deletion markers back to the object store and immediately reads them back to confirm, which needs strong read-after-write consistency to not loop forever on the same job. Grafana Tempo writes large trace blocks through the full multipart upload lifecycle, including clean aborts on failure, or writes silently degrade. RustFS handles both correctly: the same instance, same credentials, same bucket semantics, backing two very different write patterns with no per-service configuration differences.

That's the general shape of the claim this post is making: RustFS implements the S3 spec closely enough that clients with real consistency requirements, not just clients that treat S3 as a flat file dump, work against it without surprises. Loki and Tempo are just the two I happen to run; the same logic applies to a backup target, a CI artifact cache, or anything else that talks S3.

RustFS Buckets
RustFS Dashboard


6. Troubleshooting Cheat Sheet

NoSuchBucket on application startup

RustFS does not auto-create buckets. Create them before starting anything that depends on them:

mc alias set rustfs https://rustfs.ygnv.my.id ACCESS_KEY SECRET_KEY --api s3v4
mc mb rustfs/your-bucket

SignatureDoesNotMatch

Usually a region mismatch. The signing region in the client's Authorization header must exactly match RUSTFS_REGION on the server. Most AWS SDKs default to us-east-1 if unset, which won't match a server configured for another region.

ListObjectsV2 returns empty results unexpectedly

Almost always a path-style vs virtual-hosted-style mismatch. If s3forcepathstyle isn't set to true on the client, the SDK constructs bucket URLs as bucketname.yourdomain.tld. If that subdomain isn't in DNS and your reverse proxy isn't configured for it, requests silently fail or return wrong results. Set s3forcepathstyle: true (or forcepathstyle: true, depending on the client library).

Console shows a blank page after login

Console traffic is hitting the S3 API port (9000) instead of the console port (9001). Add explicit priorities to the Traefik routers so the PathPrefix rule wins:

- traefik.http.routers.rustfs-console.priority=10
- traefik.http.routers.rustfs-api.priority=1

Conclusion

MinIO's community edition didn't fail on features; it failed on trajectory. A console pulled behind a paywall, then binaries that stopped shipping, then a repository archived outright, is a project telling you plainly that the free tier isn't where its future is. That's a reasonable business decision for MinIO to make and a reasonable reason for anyone building new infrastructure to look elsewhere.

RustFS is where I landed: a from-scratch Rust implementation of the same API surface, with the admin console, OIDC integration, and license terms MinIO's community edition used to have. It runs on a single node with 512 MiB of RAM, deploys as one binary or one Compose service, and, as the Loki and Tempo example shows, holds up against S3 clients that actually exercise the parts of the spec that are easy to get wrong.

© 2026 Yoga Novaindra Powered by Ghost