Quick fix
# 1) Record the old MinIO data dir (example: /tmp/trove-minio-test/data)
docker inspect minio --format '{{range .Mounts}}{{.Source}} -> {{.Destination}}{{end}}'
# 2) Stop the old container (data dir stays untouched)
docker stop minio && docker rm minio
# 3) Copy the whole data dir + align ownership (RustFS runs as non-root UID 10001 — chown is mandatory)
cp -a /tmp/trove-minio-test/data /tmp/trove-minio-test/data-rustfs
chown -R 10001:10001 /tmp/trove-minio-test/data-rustfs
# 4) Start RustFS; ports match MinIO exactly (9000 API / 9001 console)
docker run -d --name rustfs -v /tmp/trove-minio-test/data-rustfs:/data \
-e RUSTFS_ACCESS_KEY=rustfs-admin -e RUSTFS_SECRET_KEY='use-a-strong-password' \
-p 9000:9000 -p 9001:9001 rustfs/rustfs:latest
# 5) Verify legacy data is directly visible
docker logs rustfs | tail
What you will run into
MinIO community edition entered maintenance mode in Dec 2025, and the GitHub repository was archived read-only in Feb 2026: no more commits, disclosed CVEs no longer fixed, official resources moved to the commercial AIStor subscription.
Running community edition for a file service (e.g.
a Docker-deployed object store) means sitting on an unpatched black box. Another typical trigger: after upgrading your client, uploads fail with XAmzContentSHA256Mismatch — not a config mistake.
New AWS SDK v2 defaults to streaming chunked signatures; old MinIO cannot parse them, and it will never be fixed.
Why this happens
- The freeze is a fact: MinIO froze the community edition; security fixes are "evaluated case by case"; the repo README says "THIS REPOSITORY IS NO LONGER MAINTAINED".
- The signature break is collateral damage: AWS SDK v2 defaults to streaming chunked signatures; old MinIO only supports classic signatures, so uploads report XAmzContentSHA256Mismatch with no upstream fix ever.
- Why zero-data-loss migration works: on disk, MinIO does not store "one object = one file" — each object is a same-named directory (xl.meta metadata + shards). RustFS (since 1.0.0-alpha.89) reads that disk format directly — an entire-directory
cp -ais enough, data never crosses the network, and keeping the old directory is a natural rollback backup.
The truth of "no longer maintained" is in the ecosystem contract, not the code itself: after MinIO froze its community edition, the upstream (AWS SDK v2) kept evolving;
the new SDK defaults to streaming chunked signing while old MinIO only supports the classic signature — MinIO is not "broken", it just stopped following upstream contract changes, so the signature handshake necessarily fails and the official project will never fix it.
Zero-loss migration rests on the disk format: MinIO is not "one object one file" but an object-named directory (xl.meta metadata + shards);
RustFS directly understands that disk format, so cp -a of the whole directory preserves metadata and shards as-is, and data never crosses the network.
Deterministic takeaway: for S3-compatible services, migrate based on "disk-format compatibility" rather than "API compatibility" (API compatibility can break anytime; disk-format compatibility enables offline migration);
keeping the old directory untouched is a natural rollback backup — verify reads/writes after migration, and if verification fails, switch back;
fixed diagnostic: XAmzContentSHA256Mismatch = signature generation mismatch, not network or config.
Step by step
1. Stop old MinIO, but record the data dir first — Required
From docker inspect, note the Mounts Source (host path) and Destination (container path; usually /data for MinIO). Removing the container is fine — data lives in the host directory.
2. Copy the whole directory (do not cherry-pick files) — Required
ls /data/public shows not image files but same-named object directories (xl.meta + shards), so "copy the objects" does not work. cp -a the whole data dir to a sibling new dir; the old dir stays intact as a rollback backup.
3. chown 10001 before starting (the step where almost everyone breaks) — Required
The MinIO image runs as root and can write anywhere; the RustFS image declares USER 10001 in its Dockerfile and runs non-root. Bind mounts do not translate permissions — when UID 10001 writes /data, the kernel checks the host directory owner.
Without chown the container starts but cannot write.
Do not use chmod 777 (opens write access to every user on the server).
4. Start RustFS and verify — Required
docker psshows Up;docker logs rustfshas no errors.- Console http://server-ip:9001 — sign in with the AK/SK from startup; you should see the old MinIO buckets and files directly.
- API check (S3-compatible):
list_bucketsshows the old bucket, the old object reads back, a new object writes.
5. Switch the client side too (easy to forget) — Required
Old MinIO Service Accounts do not migrate: point the app at the new AK/SK from RustFS startup.
Java/Go/Python SDKs only need endpoint + credentials changed; for direct-IP use enable path-style addressing (pathStyleAccessEnabled(true)), otherwise virtual-host addressing puts the bucket name into the hostname and DNS resolution fails.
How to confirm it works
# S3 API check: old bucket visible + old object readable + new object writable (actual output)
buckets after migration: ['legacy-bucket']
legacy object readable: b'hello from minio legacy'
new object roundtrip: b'written on rustfs after migration'
Old buckets/files visible in the web console = end-to-end migration done.
Other environments
| Environment | Notes |
|---|---|
| Choice boundary | SeaweedFS is the most mature but its Master/Volume/Filer multi-role architecture needs relearning; Garage fits geo-distributed clusters; Ceph fits PB scale. For single-machine/small-medium scale and lowest migration cost, pick RustFS |
| Production scale | This test covers a single-node Docker simulation of the full MinIO → RustFS path; multi-node erasure coding and production concurrency untested; PB-scale core storage → SeaweedFS/Ceph |
| Keeping the old client | Swapping only the server still works with the old MinIO Java SDK (S3 protocol); switching to the AWS SDK standard implementation is recommended so you are not tied to any vendor |
| Vendor-free lesson | Storage choice is not a one-time decision; the S3 protocol abstraction is the real asset — server swaps become config-level changes |
| Client SDK | Java/Go/Python only need endpoint + AK/SK changes; direct-IP access requires path-style addressing (virtual-host addressing breaks DNS) |
Symptom
MinIO community repo archived read-only / README says unmaintained; uploads fail with XAmzContentSHA256Mismatch after SDK upgrade; a Docker-deployed object store needs migration but data loss is feared.
Cause
MinIO moved official resources to commercial AIStor; community CVEs are no longer fixed; AWS SDK v2 defaults to streaming chunked signatures which old MinIO cannot parse (never fixed); RustFS reads the MinIO disk format (≥1.0.0-alpha.89) so a whole-directory copy gives a zero-network migration.
Solution
cp -a the old MinIO data dir → chown -R 10001:10001 → docker run RustFS with the copied dir → update app AK/SK (enable path-style for direct IP) → verify old buckets/objects in console/API plus a new write.
Verification Evidence
local repro log 2026-08-30 (2026-08-30 full local Docker test: bucket+upload → stop old → cp -a + chown → RustFS up → old bucket/object directly visible + new object roundtrip passed)
Applicable tools (3)
This problem is linked from the tool pages below — reachable from either side.
miniorustfsdockerSource & evidence
← Build & Deploy domain | Problem Catalog Experience | Facing another issue? Go to the Helpdesk