[{"data":1,"prerenderedAt":246},["ShallowReactive",2],{"seo-verification":3,"blog-migrate-minio-to-garage-or-rustfs-vps-en":6},{"google":4,"bing":5},"EycwPY2XMyTkVzas3n1ygeNJFGAH513qrMjfDljzsMQ","",{"key":7,"data":8},"blog-migrate-minio-to-garage-or-rustfs-vps-en",{"id":9,"slug":10,"slugs":11,"title":15,"excerpt":16,"readTime":17,"views":18,"isPinned":19,"publishedAt":20,"updatedAt":21,"category":22,"categories":28,"featuredImage":30,"bgImage":31,"posterImage":32,"relatedSolution":30,"intro":33,"sections":34,"ctaTitle":184,"ctaBody":185,"ctaButton":186,"ctaUrl":187,"relatedPosts":188},499,"migrate-minio-to-garage-or-rustfs-vps",{"fr":12,"en":10,"ar":13,"es":14},"migrer-minio-vers-garage-ou-rustfs-vps","ترحيل-minio-إلى-garage-أو-rustfs-على-vps","migrar-minio-a-garage-o-rustfs-vps","Migrate MinIO to Garage or RustFS without losing a current object","MinIO is archived: copy your buckets to Garage or RustFS with rclone, prove nothing is missing and switch your application without breaking your URLs.",13,0,false,"2026-10-23T00:00:00+00:00","2026-10-23T23:43:01+00:00",{"id":23,"name":24,"slug":25,"color":26,"icon":27},7,"Self-hosting","self-hosting","bg-indigo-500\u002F10 text-indigo-400","cloud",[29],{"id":23,"name":24,"slug":25,"color":26,"icon":27},null,"\u002Fblog\u002Fcovers\u002Fbg.svg","\u002Fblog\u002Fcovers\u002Fmigrer-minio-vers-garage-ou-rustfs-vps-poster.svg","The MinIO repository has been archived since 25 April 2026: your S3 storage now runs without upstream security fixes. Garage and RustFS are the two self-hostable targets that keep coming up, but copying the objects is only half the job. IAM policies, version history and presigned URLs do not follow. This guide inventories the source, copies with rclone, proves the result with counts and checks, then switches the application in three phases, with a minimal write cutoff and a rollback ready.",[35,39,49,52,71,111,114,132,150,153,156,174,178,181],{"type":36,"title":37,"body":38},"h2","Why you should not leave MinIO where it is","The `minio\u002Fminio` repository was archived by its owner on 25 April 2026: it is read-only, its README states that the project is no longer maintained and that the binaries already published will not receive updates. In practice, the next flaw found in your S3 server will be fixed by nobody upstream. The `mc` client, also AGPLv3, was archived on 14 July 2026.\n\nYour MinIO keeps running, which is what makes the subject treacherous: nothing breaks on the day of the archive. But object storage is exposed, it holds your media, backups or exports, and it now runs without security patches. Two self-hostable targets keep coming up in discussions: Garage, released under AGPLv3, and RustFS, released under the Apache 2.0 licence.\n\nThis article does not redo the installation of either: they have their own guides, \u003Ca href=\"\u002Fen\u002Fblog\u002Fhost-garage-on-a-vps-s3-object-storage-that-belongs-to-you\">Garage on a VPS\u003C\u002Fa> and \u003Ca href=\"\u002Fen\u002Fblog\u002Fself-host-rustfs-s3-object-storage-vps\">RustFS on a VPS\u003C\u002Fa>. Here we start from a MinIO in production and answer one question: how to move the data, prove nothing is missing and switch the application over without broken URLs.",{"type":40,"title":41,"items":42},"ul","What a bucket copy does not move by itself",[43,44,45,46,47,48],"**Current objects** — the easy part: `rclone` or `mc mirror` copy them, with a check to run afterwards.","**Older versions** — the `mc mirror` documentation states that it only synchronises the current object, with no version information.","**IAM users and policies** — they live in MinIO, not in the buckets. Secrets cannot be exported: you generate new keys on the target.","**Presigned URLs already handed out** — the signature depends on the host, the region and the keys. Changing the endpoint invalidates them.","**S3 compatibility gaps** — ACLs, bucket policies, anonymous access, versioning: each target has its holes, to be read in its documentation rather than assumed.","**Client configuration** — endpoint, region, path-style addressing: everything your application, your `restic` backups or your CDN hard-code.",{"type":36,"title":50,"body":51},"Prerequisites and capacity plan","You need administrator access to MinIO, a workstation or VPS with `mc` and `rclone`, and a target that is already installed and reachable over HTTPS (see the two guides above for the reverse proxy and the certificate). The capacity rule is simple: until you have validated the migration, you keep the source intact, so you pay for the disk twice.\n\nOur editorial recommendation: plan on the target for the total size of the source data, plus a margin of about 30% for metadata and distribution differences. On the same VPS, that means having at least twice the current volume plus the margin; on two VPS, each carries its share. A single Garage node with `replication_factor = 1` keeps only one copy of each object: it is not a backup, and the source still in place is your only safety net during the migration.",{"type":53,"title":54,"steps":55},"steps","Step 1: inventory the source before copying",[56,59,62,65,68],{"title":57,"body":58},"Declare MinIO as an alias","Create an alias `src` with credentials that can read every bucket:\n`mc alias set src https:\u002F\u002Fs3.your-domain.com ACCESS_KEY SECRET_KEY`",{"title":60,"body":61},"Count the objects and the size","For each bucket, note the number of objects and the volume:\n`mc du --recursive src\u002Fmedias`\n`mc ls --recursive src\u002Fmedias | wc -l`\nPut these figures in a before \u002F after table: it is your proof that the migration is complete.",{"title":63,"body":64},"Count the versions","If versioning is enabled, the version list is longer than the object list:\n`mc ls --recursive --versions src\u002Fmedias | wc -l`\nThe `mc ls` documentation specifies that `--versions` requires a service that supports versioning. A large gap between the two counts tells you how much history is at stake.",{"title":66,"body":67},"Export the list of policies and users","Keep a record of what you will have to recreate:\n`mc admin policy list src`\n`mc admin user list src`\nSecrets are not in this output and cannot be: you will regenerate them.",{"title":69,"body":70},"Record the client configuration","In every application that talks to MinIO, note the endpoint, the region, the addressing style and the name of the environment variable that carries them. The final switch will change only these values.",{"type":72,"title":73,"headers":74,"rows":78},"comparison","Garage and RustFS: the gaps to know before choosing",[75,76,77],"Topic","Garage","RustFS",[79,83,87,91,95,99,103,107],[80,81,82],"Licence","AGPLv3 (repository README)","Apache 2.0 (repository README)",[84,85,86],"Versioning","Not supported: `PutBucketVersioning` and `ListObjectVersions` are listed as missing","Listed as available in the README",[88,89,90],"Bucket policies and ACLs","Neither: its own system, per key and per bucket","IAM and bucket policies; ACLs are intentionally unsupported",[92,93,94],"Anonymous access","Refused, with a 403 response","Evaluated against the bucket policy",[96,97,98],"Client region","`s3_region` from `garage.toml`, to be copied into the client","`RUSTFS_REGION`, `us-east-1` by default, to be copied into the client",[100,101,102],"Addressing","Path-style and vhost-style supported","Path-style by default, no DNS configuration",[104,105,106],"Presigned URLs","Supported","Supported; host, scheme, port, region and path must match the signature",[108,109,110],"MinIO admin API","Not applicable: you administer with the `garage` command","Separate surface: S3 works without `mc admin` being guaranteed",{"type":36,"title":112,"body":113},"Versioning: what the copy loses, and what to do","This point decides the choice of target. Garage does not support versioning: its S3 compatibility page says so plainly. If your buckets keep a history that the application reads or that you must keep for business reasons, Garage is not the destination. RustFS lists versioning as available, but copying with `rclone sync` or `mc mirror` will not recreate your history there.\n\nThree routes, to choose bucket by bucket. Accept the loss: only the current version goes across, which suits cases where versioning was only a safety net against human error. Copy version by version: you list the identifiers with `mc ls --versions`, then replay the versions from oldest to newest to a target that supports versioning, after enabling it on the bucket; version dates will be those of the copy, not the originals. Or archive the history separately, in an archive bucket, and migrate only the current objects. In every case, test with an object that has several versions before launching the general copy.",{"type":53,"title":115,"steps":116},"Step 2: prepare the target, Garage or RustFS",[117,120,123,126,129],{"title":118,"body":119},"Garage: assign then apply the layout","No bucket is usable until the layout is applied. Look up the node identifier, then assign it a zone and a capacity:\n`garage status`\n`garage layout assign -z dc1 -c 1G \u003Cnode_id>`\n`garage layout apply --version 1`\nAdjust `-c` to the disk space actually allocated.",{"title":121,"body":122},"Garage: create the key, the bucket and the permissions","Permissions are granted per key and per bucket:\n`garage key create app-key`\n`garage bucket create medias`\n`garage bucket allow --read --write --owner medias --key app-key`\nThe secret is displayed when the key is created: copy it straight into your secrets manager.",{"title":124,"body":125},"Garage: copy the region into the client","The `[s3_api]` section of `garage.toml` defines `s3_region`. Put exactly that value in your clients' configuration, and keep path-style addressing. A different region gives rejected signatures.",{"title":127,"body":128},"RustFS: alias and bucket","With `mc` configured against the target:\n`mc alias set dst http:\u002F\u002F127.0.0.1:9000 ACCESS_KEY SECRET_KEY`\n`mc mb dst\u002Fmedias`\nThe RustFS documentation recommends IAM users or service accounts rather than root keys for applications.",{"title":130,"body":131},"RustFS: recreate users and policies","Take the list exported in step 1 and recreate each user and each policy on the RustFS side, with a fresh key per application. Then align the region: `RUSTFS_REGION` is `us-east-1` by default, and the client must use the same one.",{"type":53,"title":133,"steps":134},"Step 3: copy with rclone and prove the result",[135,138,141,144,147],{"title":136,"body":137},"Declare the two remotes","Use the `Minio` provider for the source and `Other` for the target:\n`rclone config create src s3 provider=Minio access_key_id=ACCESS_KEY secret_access_key=SECRET_KEY endpoint=https:\u002F\u002Fs3.your-domain.com`\n`rclone config create dst s3 provider=Other access_key_id=ACCESS_KEY secret_access_key=SECRET_KEY endpoint=http:\u002F\u002F127.0.0.1:3900 region=garage`\n`region=garage` assumes `s3_region = \"garage\"`, the value in Garage's example: use yours if it differs. For RustFS, replace the endpoint with the target's and the region with the value of `RUSTFS_REGION`.",{"title":139,"body":140},"Do a dry run","`rclone sync` changes the destination so that it mirrors the source, deletions included. Never reverse it, and run the dry run first:\n`rclone sync src:medias dst:medias --checksum --metadata --transfers 16 --dry-run`",{"title":142,"body":143},"Run the live copy","MinIO stays in service during this first pass:\n`rclone sync src:medias dst:medias --checksum --metadata --transfers 16 --progress`\n`--metadata` asks rclone to preserve metadata: check on a test object that the content type arrived intact.",{"title":145,"body":146},"Check the copy","Check that everything in the source exists in the target:\n`rclone check src:medias dst:medias --one-way --combined \u002Ftmp\u002Fcheck-medias.txt`\nThe file lists each path with a symbol: `=` identical, `+` missing from the destination, `*` different, `!` error. Zero `+`, `*` or `!` lines is the goal.",{"title":148,"body":149},"Recount after the copy","Replay `mc du --recursive` and the object count on the target, with the `dst` alias, and fill in the \"after\" column of your table. The two figures must be identical, except for gaps explained by versions.",{"type":36,"title":151,"body":152},"ETag multipart and mc mirror: reading the gaps correctly","The ETag of a small object sent in a single request is its MD5, and `rclone` uses it as a checksum. For a multipart object, the rclone documentation points out that the ETag is no longer the MD5 of the data: it is the MD5 of the sequence of each part's MD5, followed by the number of parts. It therefore depends on the part size, which the client chose at upload time.\n\nYour MinIO received the object with one split, `rclone` sends it to the target with another: the content is identical byte for byte, the ETag is not. A `rclone check` that reports these objects as different is not necessarily right. Replay it on them with `--download`, which downloads both sides and compares the data itself: slower, but unambiguous. The same caution applies to `mc mirror`: its documentation specifies that composite checksums can differ simply because the split into parts has changed.\n\nIf you prefer to stay within the `mc` ecosystem, the copy is written `mc mirror --preserve src\u002Fmedias dst\u002Fmedias`. `--preserve` keeps the source's attributes and bucket policy rules; the latter only makes sense if the target can read them, which rules out Garage. Without `--overwrite`, an object already on the target is not copied again: the error is logged and the copy continues. With `--remove`, target objects missing from the source are deleted. And `mc mirror` only synchronises the current object, without versions. Its comparison covers name, size, date and checksum, without reading the content. The `mc` repository is archived too: the tool works, but it will not move.",{"type":36,"title":154,"body":155},"Presigned URLs: why they fail at the switch","A presigned URL contains a signature computed from the secret key, the host, the region and the path. The RustFS documentation says it: the host, scheme, port, region and request path must match those used to sign. Changing the endpoint or the key therefore invalidates every link already issued, without exception, and nothing redirects them.\n\nThe consequence is a matter of sequencing. Links your application generates on the fly regenerate themselves as soon as it is reconnected to the new target. Those you sent by e-mail or stored in a database will die at the switch. So shorten the lifetime of links a few days beforehand, so that few long-lived links are in circulation, and warn the teams who hand them out manually.",{"type":53,"title":157,"steps":158},"Step 4: switch in three phases, without a hard cut",[159,162,165,168,171],{"title":160,"body":161},"Initial live sync","This is the copy from step 3, run while MinIO still serves traffic. It takes as long as it takes and bothers nobody. Note the end time.",{"title":163,"body":164},"Freeze writes","Put the application in read-only mode or in a short maintenance window, or enable dual writing if your code allows it. Reads can continue on MinIO while the final sync runs.",{"title":166,"body":167},"Final sync with --update","Transfer only what has changed since:\n`rclone sync src:medias dst:medias --update --metadata --transfers 16 --progress`\n`rclone check src:medias dst:medias --one-way`\nRecount the objects and the size, and compare with the table from step 1.",{"title":169,"body":170},"Change the endpoint through an environment variable","Your application must read the endpoint, region and keys from its environment, never from code. Change these variables, restart the service, then test an upload, a read and a deletion on a test object. Regenerate the presigned links that matter.",{"title":172,"body":173},"Monitor, and only then dismantle","Leave MinIO in place, stopped or read-only, long enough to see a full cycle of your backups and scheduled jobs go by. Point your `restic` backups at the target again, see the guide on \u003Ca href=\"\u002Fen\u002Fblog\u002Fautomate-your-vps-backups-with-restic\">restic backups\u003C\u002Fa>.",{"type":175,"title":176,"body":177},"tip","Rolling back comes down to one variable","As long as MinIO has not been deleted and the last sync was done in read-only mode, going back means putting the old endpoint in the environment and restarting. If writes have happened on the target since the switch, bring them back to the source first with an `rclone copy` in the other direction (never a `sync`, which would delete), then go back. Erase MinIO only after an observation period that you set in advance.",{"type":36,"title":179,"body":180},"Troubleshooting: the symptoms that keep coming back","An unsigned call on Garage answers 403 with the message `Garage does not support anonymous access yet`. That is the expected behaviour of Garage, and the request for anonymous access is still in the project tracker as a feature that was never prioritised. If your application served public files without a signature, you will need presigned URLs or a proxy in front of the bucket.\n\nIf writes are rejected with signature errors right after the switch, compare the client's region with the target's (`s3_region` for Garage, `RUSTFS_REGION` for RustFS) and check that the client uses path-style addressing. If `rclone check` reports multipart objects as different although their sizes are equal, replay it with `--download`. If a rerun of `mc mirror` does not copy a modified object, the cause is the missing `--overwrite`. Finally, if a bucket is empty on the target although the sync succeeded, check that the key was given `--read --write` on that bucket on the Garage side, or the right policy on the RustFS side.",{"type":36,"title":182,"body":183},"The official documentation","Check these sources again before acting, because these projects move: the github.com\u002Fminio\u002Fminio repository for MinIO's status and licence; github.com\u002Frustfs\u002Frustfs and docs.rustfs.com for RustFS; garagehq.deuxfleurs.fr for Garage's S3 compatibility; rclone.org for `sync` and `check`; MinIO's documentation for `mc mirror`. To back up this storage, see \u003Ca href=\"\u002Fen\u002Fblog\u002Frestic-s3-3-2-1-backup-strategy-vps\">the 3-2-1 article with restic and S3\u003C\u002Fa>.","A second VPS to test the target before cutting off MinIO","A second root VPS with an NVMe disk hosts the target during the migration: you test alongside production and only cut MinIO once the object counts are validated. The switch happens without touching the original machine.","See the Cloud VPS","\u002Fvps-cloud",[189,208,228],{"id":190,"slug":191,"slugs":192,"title":196,"excerpt":197,"readTime":23,"views":18,"isPinned":19,"publishedAt":198,"updatedAt":21,"category":199,"categories":205,"featuredImage":30,"bgImage":31,"posterImage":207,"relatedSolution":30},70,"hosting-minio-on-a-vps",{"fr":193,"en":191,"ar":194,"es":195},"heberger-minio-vps","استضافة-minio-على-خادم-vps","alojar-minio-en-un-vps","Hosting MinIO on a VPS","MinIO on a VPS: buckets, console, TLS, backups. The Community edition is archived, so here is how to decide between staying and migrating.","2026-04-11T00:00:00+00:00",{"id":200,"name":201,"slug":202,"color":203,"icon":204},6,"Databases","bases-de-donnees","bg-teal-500\u002F10 text-teal-400","database",[206],{"id":200,"name":201,"slug":202,"color":203,"icon":204},"\u002Fblog\u002Fcovers\u002Fheberger-minio-vps-poster.svg",{"id":209,"slug":210,"slugs":211,"title":215,"excerpt":216,"readTime":217,"views":218,"isPinned":19,"publishedAt":219,"updatedAt":220,"category":221,"categories":222,"featuredImage":30,"bgImage":31,"posterImage":224,"relatedSolution":225},245,"host-garage-on-a-vps-s3-object-storage-that-belongs-to-you",{"fr":212,"en":210,"ar":213,"es":214},"heberger-garage-s3-vps","استضافة-garage-على-خادم-vps-تخزين-كائنات-s3-يملكه-أنت","alojar-garage-s3-en-un-vps","Host Garage on a VPS: your own S3-compatible storage bucket","Deploy Garage on a VPS for a personal S3 storage bucket — backups, app files, static site origin — with no dependency on a third-party cloud.",8,2,"2026-08-10T00:00:00+00:00","2026-09-07T11:26:10+00:00",{"id":23,"name":24,"slug":25,"color":26,"icon":27},[223],{"id":23,"name":24,"slug":25,"color":26,"icon":27},"\u002Fblog\u002Fcovers\u002Fheberger-garage-s3-vps-poster.svg",{"categorySlug":226,"appSlug":227},"databases","garage",{"id":229,"slug":230,"slugs":231,"title":235,"excerpt":236,"readTime":237,"views":18,"isPinned":19,"publishedAt":238,"updatedAt":239,"category":240,"categories":241,"featuredImage":30,"bgImage":31,"posterImage":243,"relatedSolution":244},407,"self-host-rustfs-s3-object-storage-vps",{"fr":232,"en":230,"ar":233,"es":234},"self-host-rustfs-vps","استضافة-rustfs-على-vps-تخزين-كائنات-s3","autoalojar-rustfs-en-vps-almacenamiento-objetos-s3","Self-Host RustFS on a VPS: S3-Compatible Object Storage","Step-by-step guide to self-hosting RustFS on a VPS: web console, generated S3 credentials, persistent volumes — a MinIO and Garage alternative.",5,"2026-10-01T00:00:00+00:00","2026-10-02T21:10:18+00:00",{"id":23,"name":24,"slug":25,"color":26,"icon":27},[242],{"id":23,"name":24,"slug":25,"color":26,"icon":27},"\u002Fblog\u002Fcovers\u002Fself-host-rustfs-vps-poster.svg",{"categorySlug":226,"appSlug":245},"rustfs",1792799399885]