S3 object storage after MinIO

In late 2025 the company behind MinIO wound down its community edition — web console cut to a stub, prebuilt community binaries stopped, repository archived. That stranded two distinct populations: demos/compose files that used MinIO as a local S3 stand-in, and self-hosted production deployments. The replacements are not the same for each — the candidates divide sharply on concurrency correctness (conditional PUT, atomic updates, ETag semantics) and crash consistency, which is exactly what table formats like Apache Iceberg require.

Grounded in: rmoff’s single-node comparison (2026-01-14, versions tested then), the HN discussion it triggered (2026-09-16), and source-code verification of each repo at the tagged release (2026-09-16; details per project below).

Why conditional writes decide this field

Iceberg (and Delta without an external lock provider) commit by atomically swapping a pointer — PutObject with If-None-Match: * (create-only) or If-Match: <etag> (compare-and-swap on the current pointer). If the store returns success for a losing concurrent writer, two writers both believe they committed and the table silently diverges. AWS shipped PutObject conditional headers in August 2024; MinIO had them since February 2023.

A store that is only eventually consistent cannot offer this guarantee at all — and no per-project feature matrix will tell you; you have to read the write path.

The field

ProjectMulti-nodeConditional PUT (If-Match/If-None-Match)Crash consistencyHomelab fitProduction fit
silo (OSS) (MinIO fork)✅ erasure-coded pools✅ inherited from MinIO (checkPreconditionsPUT)✅ MinIO’s battle-tested write path✅ drop-in✅ known quantity
rustfs (OSS)✅ distributed EC✅ server-side precondition checks + race-hardening commits⚠️ young; HIGH CVE-2026-55189 (fixed)✅ single binary⚠️ GA since 2026-09-16
seaweedfs (OSS)✅ filer+volume servers✅ full impl in 4.47 (WriteCondition under filer lock) + conditional DELETE⚠️ fsync per-bucket opt-in; index fully in RAM⚠️ memory ∝ object count
hs5 (OSS)❌ single-node by design✅ checked before upload, on by default✅ fsync before success response (strong)✅ simplest❌ no replication
garage (OSS)✅ geo-distributed CRDT❌ maintainer-confirmed impossible without rearchitecture⚠️ fsync off by default; LMDB default can corrupt on hard shutdown⚠️❌ for Iceberg

Concurrency and consistency, per project

silo — the MinIO continuation

A fork maintained by Vonng (PGSTY) since 2026-03-02, renamed from pgsty/minio on 2026-08-06, 3.1k stars, releases every 1–2 months (latest: 2026-09-16). It inherits MinIO’s full conditional-write implementation (checkPreconditionsPUT on the PUT path, If-Match/If-None-Match → 412 Precondition Failed) and its erasure-coded multi-node server pools. AGPL-3.0, no CLA, telemetry removed. For existing MinIO deployments this is the no-code-change path — the fork explicitly exists to keep them running (“the fork is a means, not an identity”).

Fit: drop-in for both homelab and production. The main question is bus-factor — one principal maintainer with a strong public commitment record (manifesto: public advisory per fix, tested rollback per release).

rustfs — Swift + Keystone, GA 2026-09-16

The differentiator for us: native OpenStack Swift API with Keystone authentication (dedicated keystone crate, protocols/src/swift — integration-tested). For a COT/Homelab environment that runs Swift at work, this makes rustfs a dev/simulator for Swift and Keystone, not just S3-compatible storage.

Server-side conditional writes are implemented (storage-api/src/object.rs precondition checks), and the team has been actively hardening them — rc.4 “fail incomplete conditional PUT races”, rc.5 “preserve atomic 1 MiB conditional writes”. Version 1.0.0 GA released 2026-09-16. Apache 2.0 (the AGPL-free license is a stated selling point). Distributed erasure coding, MinIO-like architecture.

The GA announcement (2026-09-16) adds two more edges:

  • S3 Tables — an Apache Iceberg REST Catalog built into the storage kernel. rustfs doesn’t just back Iceberg-with-conditional-PUT like the others; it ships the catalog itself (AWS S3 Tables-style), so DuckDB/Spark/Trino point at the rustfs REST endpoint instead of standing up a separate catalog (Polaris/Nessie/REST-catalog container). Deeper Iceberg investment than any other candidate here — SeaweedFS has Iceberg maintenance tasks (compaction), not a catalog.
  • Adoption and pace: first code Feb 2024 → open-sourced Jul 2025 → GA in 2y7m; vendor-claimed 32k+ stars, 10M+ Docker pulls, 2.7M+ deployed instances, 160+ contributors, NVIDIA Inception. Protocol surface beyond S3/Swift: WebDAV, FTP/FTPS, MCP. (Adoption numbers are the vendor’s own GA post — mechanics verified in-repo, scale claims unverifiable.)

Caveats: young security track record — CVE-2026-55189 (HIGH 7.7: any authenticated FTP-listener user could read any object, fixed in beta.12) plus a 2026-07 medium. The FTP surface is turned off by default but it’s a reminder the protocol surface is broad; GA-day-one production claims deserve observation, not automatic trust.

Fit: strong homelab candidate (Swift simulation + S3 + built-in Iceberg catalog + single Rust binary). Production: the GA claim plus adoption is credible for greenfield, but with GA only since 2026-09-16 and a short CVE history, silo remains the lower-risk MinIO replacement.

SeaweedFS — conditional writes shipped, mind the metadata

Oldest project here (2014, single primary maintainer chrislusf, 34k stars, Apache 2.0). The conditional-write story the 2024 GitHub discussion said was missing is done: s3api_object_routed_write.go reduces If-Match/If-None-Match to filer WriteCondition clauses evaluated under the filer lock (routed write for the reducible cases, lock-path evaluation otherwise), plus conditional DELETE with If-Match. Verified present in release 4.47 (2026-09-14). It now even ships native Iceberg table support (weed/worker/tasks/iceberg/) and advertises “billions of files” via O(1) disk access.

The trade-offs are operational: the [object → disk] index lives entirely in memory (startup scan + RAM ∝ object count — hs5’s author flags this as unsuited to fast-restart/testing scenarios), and durability needs per-bucket fsync enabled (Path-Specific Configuration wiki) — without it a crash can lose acknowledged writes.

Fit: good homelab fit for large-file/large-count storage with fsync on. Production is real (many run it), but memory-bound metadata and single-maintainer governance deserve weighing.

hs5 — strongest single-node semantics, deliberately no cluster

C++ (folly/proxygen), LGPL-3.0+, 193 stars, by the UrBackup author. Single-node by design (“scale-up: run it on a better machine”). Two properties stand out:

  • Durability by default: data and metadata are fsynced before the success response returns — power-cut after a 200 means the object is there. AWS-grade read-after-write consistency, no eventual consistency.
  • Conditional PUT checked pre-upload (check_conditional_headers_before_upload, default true) — If-Match/If-None-Match evaluated before the body is read.

Storage is one LMDB index + append-only data0 file (index on SSD, data on HDD works). Missing: POST Object upload, object locks. A 10k-small-file benchmark in-repo puts hs5 ~11s vs rustfs ~13s vs minio ~14.4s vs garage ~16.5s (fsync-compensated).

Fit: the simplest correct local S3 for demos, CI, and single-box homelab use — one binary, two files, no moving parts. Not for production data you can’t lose — one node, no replication.

Garage — excellent geo-storage, wrong tool for Iceberg

The confirmation of the opening concern: maintainer reply on issue #1052 (2025-05-29) — conditional writes are “not possible with our weak-consistency replication model, it would require a full rearchitecting to use a strong consensus algorithm, which would make garage much slower.” Garage is CRDT-based, geo-distributed-first, AGPL, grant-funded (NLnet). Strong read-after-write is provided via quorum reads, but CAS-style atomic writes are architecturally out.

Crash consistency also needs manual care: metadata_fsync/data_fsync are off by default; hs5’s README documents that default-config LMDB “actually runs in a mode where data gets corrupted on hard shutdown” (they recommend LMDB only with replication ≥ 2), and even SQLite metadata wants synchronous = FULL rather than the default NORMAL. v2.3.0 added garage server --single-node --default-bucket for easy single-node setups, but easy setup ≠ safe single-node defaults.

Fit: great for multi-site self-hosted blob storage (its actual design goal). Not for Iceberg/table-format workloads; single-node defaults are a durability trap.

Recommendations (as of 2026-09-16)

  • Replacing MinIO anywhere (homelab or prod): silo — identical semantics, conditional writes inherited, active CVE-patched releases.
  • Dev/simulation of Swift + Keystone + S3 at work: rustfs — the only one with a native Swift/Keystone API, GA as of 2026-09-16; keep an eye on its CVE record.
  • Iceberg without extra moving parts: rustfs again — built-in S3 Tables Iceberg REST catalog means one fewer container vs standing up Polaris/Nessie next to any of the others.
  • Local S3 for demos/CI/homelab with the fewest moving parts: hs5 — strongest crash-consistency story of the lot, but single-node only.
  • Large-scale homelab storage with billions of small files: SeaweedFS with per-bucket fsync on; conditional writes now shipped.
  • Geo-distributed cheap-and-cheerful blobs: Garage — but never for Iceberg, and turn fsync on.

Fewer moving parts ranking (simplest → most): hs5 (1 binary, 2 files) → silo (1 binary, erasure sets) → rustfs (1 binary, distributed EC) → SeaweedFS (master+volume+filer roles) → Garage (cluster layout).

Sources

  • rmoff, Alternatives to MinIO for single-node local S3 (2026-01-14) — single-node bake-off; versions tested are 8 months old
  • HN discussion of the above (2026-09-16) — silo adoption reports, Garage v2.3.0 single-node flag, RustFS CVE sentiment, Versity GW mention
  • Garage issue #1052 (git.deuxfleurs.fr, 2025-05) — maintainer statement on conditional writes
  • SeaweedFS discussion #5299 (2024) + weed/s3api/s3api_object_routed_write.go at 4.47 — conditional-write implementation
  • MinIO blog: conditional writes merged Feb 2023; AWS S3 conditional writes (Aug 2024)
  • Repo source verified 2026-09-16 at: silo RELEASE.2026-09-16, rustfs 1.0.0, seaweedfs 4.47, hs5 main (Aug 2026)