SSD Nodes Learn Hosting plans →
Guides Matt ConnorBy Matt Connor

MinIO is archived: self-hosted S3 alternatives

MinIO is now archived and unpatched. Compare SeaweedFS, Garage, RustFS and Ceph for a storage VPS, then move your buckets with rclone and verify every object.

Why you need self-hosted MinIO alternatives now

If you run MinIO on a storage VPS, the self-hosted MinIO alternatives worth moving to are SeaweedFS and Garage. RustFS looks promising, but it is too new to trust with data you cannot lose. Move your buckets with rclone. Before your app touches the new server, check that the object counts and total sizes match.

The reason is a dated record, collected by pinggy.io in a post from 2026-09-15. MinIO removed admin features from the community console in May 2025. It stopped publishing binaries and container images in October 2025. The project went into maintenance mode on 2025-12-03. It was marked as no longer maintained on 2026-02-12, and the GitHub repository was archived read only on 2026-04-25.

An archived repository accepts no fixes. So a MinIO box on a storage VPS today runs unmaintained software. The next security bug in its S3 API or its signature checks will not get a patch. The image tag you pull will not move either: minio/minio:latest points at whatever was last pushed before October 2025. A fresh install today is already out of date on the day you install it.

If you followed our earlier guide to self-hosted MinIO object storage, your setup still works. Nothing switches off. The risk is that it never changes, while the list of known bugs in it keeps growing.

What a replacement has to do on one or two VPSes

Most readers here run object storage on one storage VPS, or on two. The app lives on a main VPS and talks to the storage box over S3 (the Amazon Simple Storage Service API, which every server in this guide copies). That setup is covered in pairing a storage VPS with your main VPS. It sets the bar for a replacement:

  • It must support enough of the S3 API for your app. Bucket and object calls, listing, multipart upload and presigned URLs cover most apps.
  • It must run well as one process on one box, and it must be able to add a second box without a rebuild.
  • It must be actively maintained, with tagged releases you can pin.
  • Its storage format must be something you can back up and understand.

Large clusters change the answer. They are not the subject of this guide.

SeaweedFS: volume servers plus a filer

SeaweedFS uses the Apache-2.0 license and has been in active development since 2012. It is built from separate parts. Once you know them, the rest of the setup makes sense.

The master tracks which volume holds which data. The volume servers store the object bytes. They pack many objects into large volume files and mostly append to them, so even a flood of small uploads becomes mostly sequential disk writes. The filer maps names (bucket and key) to file IDs inside those volumes. The S3 gateway sits on top of the filer and speaks the S3 API. On a single VPS, one weed server process can run all of these parts together.

The part to plan is the filer's metadata store, which SeaweedFS lets you choose. The default is an embedded store on local disk. You can point the filer at an external database instead. The choice lives in a filer.toml file. This command prints a commented template of it:

weed scaffold -config=filer

Read the template and the SeaweedFS wiki before you change the store. Moving filer metadata from one store to another later is a migration of its own.

Split metadata from data, as Klara does

Klara, a ZFS consultancy, describes a SeaweedFS layout on OpenZFS with two tiers. Filer metadata goes on fast media such as NVMe, because listing a bucket or looking up a key reads metadata, not object bytes. Object volumes go on their own dataset on the bulk pool. The dataset for volume data uses large records and compression:

sudo zfs create -o compression=zstd -o recordsize=1M -o xattr=sa -o atime=off tank/seaweed-data
sudo zfs create -o compression=zstd -o atime=off fast/seaweed-filer

The 1M record size suits the volume files, because SeaweedFS writes them as large sequential files. The filer dataset keeps the default record size. tank and fast are example pool names, so use your own. If ZFS is new to you, running ZFS on FreeBSD and Linux covers pools, datasets and the properties above.

SeaweedFS suits you if you want the most room to grow. You can add more volume servers, or back the filer with a real database. The cost is more moving parts than Garage has.

Garage: a small binary that replicates

Garage uses the AGPL-3.0 license and ships as one static binary. The official image is dxflrs/garage. Garage was built for small clusters spread across sites, run by people who have no storage team.

Garage keeps whole copies of each object on several nodes. It does not use erasure coding, which splits data into fragments plus parity blocks. Replication is easier to understand, but it needs more raw disk for the same protection. The number of copies is the replication_factor value in /etc/garage.toml. With one node the factor is 1. Garage then gives no redundancy of its own, so your protection comes from the disk layer and your backups.

After the server starts, you tell Garage which node holds how much data. That is the cluster layout, and changing it takes two steps:

garage layout assign -z dc1 -c 1G "$NODE_ID"
garage layout apply --version 1

$NODE_ID is the node ID that garage status prints. assign stages the change, and apply makes it live. Until you apply a layout, the cluster has nowhere to put data, so S3 requests fail. You make buckets and access keys with the garage command, through garage bucket create and garage key create. A key can reach only the buckets you grant it. The Garage quick start covers both steps. As of October 2026 it pins its example image to the tag dxflrs/garage:v2.3.0.

Garage is designed for three or more nodes across zones. With one or two nodes, read the replication section of its docs before you pick a factor. Garage suits you if you want a small footprint and one config file, and you accept the AGPL.

RustFS: watch it, do not bet on it yet

RustFS uses the Apache-2.0 license and is written in Rust. It aims to be a drop-in replacement for MinIO. It spent a long time in release candidates. The 1.0.0 release was tagged on 2026-09-16, about two weeks before this guide was written. The tags since then are 1.0.1 previews.

Two weeks is not a production record. A storage system earns trust through months of real data and real disk failures, and RustFS has not had that time yet. The project publishes its own speed comparisons with MinIO. We have not tested them, so we do not repeat them here.

If you try RustFS, try it on a copy of your data. Keep the old MinIO box and your backups until RustFS has a longer track record.

When Ceph RGW is the honest answer

Ceph's RADOS Gateway (RGW) is the S3 front end of Ceph. It is the mature choice at scale. Ceph expects several hosts. A healthy cluster runs monitors and managers plus one storage daemon (OSD, object storage daemon) per disk, spread across machines. That way it can lose a whole host and keep serving.

On one VPS, that design gives you the full cost of Ceph and none of its protection. Ceph RGW is the honest answer when you have at least three servers and someone who will own Ceph upgrades. It also fits when you need block or file storage from the same cluster. For one or two storage VPSes it usually is not the right tool.

Which one to pick for a storage VPS

  • Pick SeaweedFS for one large box that may grow, when you want a filer with a store you choose.
  • Pick Garage for one to three small boxes, perhaps in different places, when simple operations matter most.
  • Keep RustFS on a test box until it has run in production for a while.
  • Choose Ceph RGW only when you are building a cluster, not a storage VPS.

Also decide where the bytes will live. Block volumes, storage VPSes and hosted object stores all cost different amounts per terabyte. The storage VPS vs block vs object comparison works through that choice. If you go with a volume, adding a block storage volume to a VPS shows how to attach and mount it.

How to migrate buckets from MinIO with rclone

This is the part most comparisons skip. The plan has four steps. First, copy every bucket while the app still runs. Second, stop writes to MinIO. Third, copy again to catch the last changes, then verify the result. Fourth, point the app at the new server.

Configure both endpoints

Install rclone on a machine that can reach both servers. The new storage VPS works well for this.

sudo apt update && sudo apt install -y rclone

Add two remotes to ~/.config/rclone/rclone.conf. Use the real endpoint URLs and keys for your servers.

[old]
type = s3
provider = Minio
endpoint = https://minio.example.com
access_key_id = OLD_ACCESS_KEY
secret_access_key = OLD_SECRET_KEY

[new]
type = s3
provider = Other
endpoint = https://s3.example.com
access_key_id = NEW_ACCESS_KEY
secret_access_key = NEW_SECRET_KEY
region = garage

The region line matters for Garage, because Garage checks the region name inside each request signature. It must match the s3_region value in your garage.toml. For SeaweedFS, follow its S3 docs for this value. Now list the buckets on each side:

rclone lsd old:
rclone lsd new:

Both commands should print a bucket list with no errors. A SignatureDoesNotMatch error means the secret key or the region is wrong.

Copy every bucket

for b in $(rclone lsf old: --dirs-only | tr -d /); do
  rclone mkdir "new:$b"
  rclone copy "old:$b" "new:$b" --progress
done

Use copy, not sync. copy never deletes anything on the target, so a typo in a remote name cannot erase data. If rclone mkdir fails on Garage, the key is not allowed to create buckets. Create the buckets with garage bucket create, grant the key access, and run the loop again. A second run is cheap, because rclone skips objects that already match.

Keep MinIO read only until cutover

Stop the app from writing before the final pass. The simplest way is to stop the app or put it in maintenance mode. If other clients also write, remove write access on MinIO itself. The community console lost its admin pages in May 2025, so use the mc client you already have:

mc admin policy detach myminio readwrite --user appuser
mc admin policy attach myminio readonly --user appuser

myminio is your mc alias, and appuser is the app's MinIO user. Then run the copy loop one more time. This pass copies only what changed since the first one.

Verify counts and sizes before you cut over

For each bucket, compare both sides:

rclone size old:photos
rclone size new:photos
rclone check old:photos new:photos --one-way

rclone size prints a Total objects line and a Total size line. Both lines must be identical on the two sides. rclone check compares sizes and hashes. On a clean copy it reports 0 differences found.

check may also report that some hashes could not be checked. That happens because an object uploaded in parts gets an ETag that is not the MD5 of the whole object, so rclone has no MD5 to compare. Run the check again with --download for that bucket. rclone then reads both copies and compares the bytes. This is slow, but it is exact.

Do not move on while any count, size or difference is off. Fix the cause, copy again, and check again.

Point the app at the new endpoint

Change the endpoint URL and the access keys in the app's config, then restart the app. Test one upload and one download. Presigned URLs made before the switch are signed for the old host, so they stop working. Most apps simply create new ones.

Keep MinIO running in read only mode for a while after cutover. If something turns out to be missing, you can still copy it across. Delete the old box only after a full backup cycle has run against the new server.

FAQ

Is MinIO still safe to run after the archive?

It still runs, but it no longer gets fixes. Maintenance mode began on 2025-12-03 and the repository was archived read only on 2026-04-25, so any security bug found from now on stays open. Keep it off the public internet, reach it only over a private network or VPN, and plan a move to a maintained server such as SeaweedFS or Garage.

Which MinIO alternative is best for a single storage VPS?

SeaweedFS fits one large box that may grow, because you can add volume servers and back the filer with a database without a rebuild. Garage fits one to three small boxes and keeps operations simple, with one binary and one config file. RustFS only tagged 1.0.0 on 2026-09-16, so test it on a copy of your data first. Ceph RGW expects several hosts and rarely makes sense on one VPS.

How do I move buckets from MinIO to another S3 server?

Add both servers as rclone S3 remotes. Run rclone copy for each bucket while the app still runs. Then make MinIO read only and copy again to catch late changes. Compare rclone size on both sides and run rclone check before you point the app at the new endpoint. Use copy, not sync, so nothing on the target is ever deleted.

Why does rclone check say some hashes could not be checked?

An object uploaded in several parts gets an ETag that is not the MD5 of the whole object, so rclone has no matching MD5 to compare. Run rclone check again with --download. rclone then reads both copies and compares the bytes directly.