<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ethan Carter</title>
    <description>The latest articles on DEV Community by Ethan Carter (@ethan-carter).</description>
    <link>https://dev.to/ethan-carter</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3991599%2Fbb3daa8b-8625-441e-9a96-b832899e55b1.jpg</url>
      <title>DEV Community: Ethan Carter</title>
      <link>https://dev.to/ethan-carter</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ethan-carter"/>
    <language>en</language>
    <item>
      <title>Garage vs RustFS: Running S3 on a Single Node</title>
      <dc:creator>Ethan Carter</dc:creator>
      <pubDate>Tue, 22 Sep 2026 23:59:57 +0000</pubDate>
      <link>https://dev.to/ethan-carter/garage-vs-rustfs-running-s3-on-a-single-node-3d70</link>
      <guid>https://dev.to/ethan-carter/garage-vs-rustfs-running-s3-on-a-single-node-3d70</guid>
      <description>&lt;h1&gt;
  
  
  Garage vs RustFS: Running S3 on a Single Node
&lt;/h1&gt;

&lt;p&gt;Garage and RustFS are both Rust-written, single-binary S3-compatible stores you can run on one node. The decisive split is the license and the S3 feature set: Garage is AGPL-3.0 and skips versioning, Object Lock, and server-side encryption; RustFS is Apache 2.0 and ships all of them. Run Garage when you want a tiny, geo-distributed store on secondhand hardware and AGPL is fine; run RustFS when your app needs versioning, immutability, IAM, and a MinIO-compatible dialect on the simplest possible operational surface.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key stats
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Garage&lt;/th&gt;
&lt;th&gt;RustFS&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;License&lt;/td&gt;
&lt;td&gt;AGPL-3.0 (network copyleft)&lt;/td&gt;
&lt;td&gt;Apache 2.0 (permissive)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Implementation&lt;/td&gt;
&lt;td&gt;Rust&lt;/td&gt;
&lt;td&gt;Rust&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Single-node start&lt;/td&gt;
&lt;td&gt;&lt;code&gt;garage server --single-node&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;one &lt;code&gt;docker run&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S3 object versioning&lt;/td&gt;
&lt;td&gt;❌ Missing&lt;/td&gt;
&lt;td&gt;✅ Available&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Object Lock (WORM)&lt;/td&gt;
&lt;td&gt;❌ Missing&lt;/td&gt;
&lt;td&gt;✅ Available&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Server-side encryption&lt;/td&gt;
&lt;td&gt;❌ Missing&lt;/td&gt;
&lt;td&gt;✅ Available&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bucket / site replication (S3 API)&lt;/td&gt;
&lt;td&gt;❌ Missing&lt;/td&gt;
&lt;td&gt;✅ Available&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ACL / bucket policies&lt;/td&gt;
&lt;td&gt;❌ (own permission model)&lt;/td&gt;
&lt;td&gt;✅ (IAM / Policies)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Default S3 API port&lt;/td&gt;
&lt;td&gt;3900&lt;/td&gt;
&lt;td&gt;9000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Documented minimum RAM&lt;/td&gt;
&lt;td&gt;1 GB&lt;/td&gt;
&lt;td&gt;not published&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Garage's "missing" rows are not bugs — they are its own S3-compatibility page stating the feature is not implemented. RustFS rows are its README Feature &amp;amp; Status table (re-checked 2026-09-21). We cite the docs, not benchmarks.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the real difference between Garage and RustFS?
&lt;/h2&gt;

&lt;p&gt;Garage is an S3-compatible distributed object store designed for self-hosting at a small-to-medium scale, built by the French non-profit Deuxfleurs. Its own docs describe it as a service that "replicates data 3x over distant servers" and is "not as latency sensitive" because it avoids consensus algorithms like Paxos or Raft. You run one &lt;code&gt;garage&lt;/code&gt; binary per node, point nodes at each other, and assign a cluster layout. The design center is geo-distributed clusters of cheap, mismatched machines across physical locations.&lt;/p&gt;

&lt;p&gt;RustFS is narrower by design: a purpose-built, MinIO-compatible S3 object store written in Rust under the Apache 2.0 license. Its README Feature &amp;amp; Status table marks S3 Core, Versioning, Object Lock (WORM), Lifecycle Management, Bucket and Site Replication, IAM/Policies, Server-Side Encryption, and Distributed Mode as available, with S3 Tables (Iceberg REST) and MinIO On-Disk Compatibility in preview. There is no POSIX layer and no FUSE mount. Both are single Rust binaries, but Garage optimizes for resilient distribution across locations; RustFS optimizes for S3 correctness and the smallest operational surface.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do the licenses actually differ?
&lt;/h2&gt;

&lt;p&gt;Garage's source code is released under the AGPL v3 license. The project states it plainly: "if you patch Garage and then use it to provide any service over a network, you must share your code." That is network copyleft — stronger than the ordinary GPL. If you modify Garage and expose it as a service, you must publish your modified source. For a homelab or an internal-only deployment that never serves third parties, AGPL is usually a non-event. The moment you ship a modified Garage as a product or a hosted service, the obligation attaches.&lt;/p&gt;

&lt;p&gt;RustFS is released under the permissive Apache 2.0 license. The README notes it is "released under the permissible Apache 2.0 license," with no copyleft. You can run, modify, and embed it in a closed-source product or a commercial service without a source-disclosure obligation. For most companies evaluating storage, this is the practical differentiator: RustFS imposes no network-copyleft tax, while Garage's AGPL-3.0 does. If license friction is a concern, that single line settles the comparison before any feature table does.&lt;/p&gt;

&lt;h2&gt;
  
  
  What S3 features does Garage leave out?
&lt;/h2&gt;

&lt;p&gt;Garage implements a deliberate subset of the S3 API, and its own compatibility page lists what is missing. Bucket versioning is "Missing." Object Lock — both legal hold and retention — is "Missing" across every lock endpoint. Server-side encryption (PutBucketEncryption and friends) is "Missing"; the project's stance is that you should encrypt at the partition or client side instead. Bucket replication via the S3 API is "Missing." ACLs and bucket policies are "Missing" — Garage has its own per-access-key, per-bucket permission model rather than AWS's ACL-or-policy system.&lt;/p&gt;

&lt;p&gt;Lifecycle is only partially implemented: Garage supports &lt;code&gt;AbortIncompleteMultipartUpload&lt;/code&gt; and &lt;code&gt;Expiration&lt;/code&gt; and nothing that depends on versioning or storage classes. Multipart upload, presigned URLs, CORS, and static-website hosting are implemented. The honest read: Garage is a clean S3-compatible put/get store with replication built in at the cluster level, not a drop-in for apps that assume versioning, WORM immutability, SSE, or IAM policies. If your client needs those, test first or look at RustFS, which lists every one of them as available.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does each one run on a single node?
&lt;/h2&gt;

&lt;p&gt;Garage's documented single-node path is one command after you generate a config. The quick start generates a &lt;code&gt;garage.toml&lt;/code&gt; (metadata_dir, data_dir, &lt;code&gt;replication_factor = 1&lt;/code&gt;, ports 3901/3900/3902/3903) and then starts the server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;garage server &lt;span class="nt"&gt;--single-node&lt;/span&gt; &lt;span class="nt"&gt;--default-bucket&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That flag auto-configures a single-node cluster with no replication and creates a default access key and bucket from environment variables. For Docker, the official quick start pins &lt;code&gt;dxflrs/garage:v2.3.0&lt;/code&gt; (newer stable releases exist) and maps the four ports:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; garage-container &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 3900:3900 &lt;span class="nt"&gt;-p&lt;/span&gt; 3901:3901 &lt;span class="nt"&gt;-p&lt;/span&gt; 3902:3902 &lt;span class="nt"&gt;-p&lt;/span&gt; 3903:3903 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;/garage.toml:/etc/garage.toml &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; GARAGE_DEFAULT_ACCESS_KEY &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; GARAGE_DEFAULT_SECRET_KEY &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; GARAGE_DEFAULT_BUCKET &lt;span class="se"&gt;\&lt;/span&gt;
  dxflrs/garage:v2.3.0 &lt;span class="se"&gt;\&lt;/span&gt;
  /garage server &lt;span class="nt"&gt;--single-node&lt;/span&gt; &lt;span class="nt"&gt;--default-bucket&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;RustFS starts with one docker run (verbatim from its README):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 9000:9000 &lt;span class="nt"&gt;-p&lt;/span&gt; 9001:9001 &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;/data:/data &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;/logs:/logs rustfs/rustfs:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That serves the S3 API on 9000 and a console on 9001. Default credentials are &lt;code&gt;rustfsadmin&lt;/code&gt; / &lt;code&gt;rustfsadmin&lt;/code&gt;, which you must rotate before any real exposure. RustFS also ships a single binary you can drop on a host.&lt;/p&gt;

&lt;h2&gt;
  
  
  When should you pick Garage over RustFS?
&lt;/h2&gt;

&lt;p&gt;Pick Garage when the requirement is resilient storage across locations on hardware you already own, and you are comfortable with AGPL-3.0. Its sweet spot is exactly what Deuxfleurs built it for: a few nodes in two or three physical locations replicating data so it stays available through failures and high-latency links. The operational surface is small — one binary, a TOML file, a layout command — and the memory footprint is tiny (the project cites 1 GB RAM and 16 GB disk as minimums). For homelab backups, media, or app assets, self-hosting wins on price the moment the hardware is sunk cost.&lt;/p&gt;

&lt;p&gt;Garage also wins if you specifically want its model: CRDT-based federation instead of Raft/Paxos, so it tolerates slow or flaky inter-node links that would stall a consensus-based cluster. If your app only needs put/get/list plus presigned URLs and a static-site endpoint, Garage is lighter than running a full S3 engine. The catch is the missing feature set above — if your app leans on versioning, Object Lock, or IAM, Garage is the wrong tool and you should not try to bolt those on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where does RustFS win?
&lt;/h2&gt;

&lt;p&gt;RustFS wins on the axis most S3 teams actually care about: a complete, MinIO-compatible S3 endpoint with the least operational surface. One docker run, no cluster-layout choreography, a web console on 9001. The feature set is broad and marked available in the README — Object Lock (WORM), Lifecycle Management, Bucket and Site Replication, IAM/Policies, Server-Side Encryption, and Distributed Mode are all shipped and covered by CI gates. If your tools already speak MinIO's S3 dialect, RustFS lists MinIO On-Disk Compatibility as a preview feature and targets that dialect as its primary surface.&lt;/p&gt;

&lt;p&gt;The Apache 2.0 license is the other win: no network-copyleft obligation, so you can wrap RustFS in a product or a hosted service without source-disclosure strings. For a team whose only contract with storage is the S3 API and whose app assumes versioning, immutability, or IAM policies, RustFS is the closer fit and the shorter path to a working, compliant endpoint. RustFS is younger as an ecosystem, and S3 Tables (Iceberg) plus MinIO on-disk compatibility are still preview — own that if a lakehouse or byte-exact MinIO migration is the requirement.&lt;/p&gt;

&lt;h2&gt;
  
  
  Feature-by-feature: what the docs say
&lt;/h2&gt;

&lt;p&gt;The table below is a straight read of each project's own documentation as of September 2026. Garage rows come from garagehq.deuxfleurs.fr (quick start and the S3-compatibility page); RustFS rows come from its README Feature &amp;amp; Status table (re-checked 2026-09-21). No benchmarks, no marketing.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Capability&lt;/th&gt;
&lt;th&gt;Garage&lt;/th&gt;
&lt;th&gt;RustFS&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;S3-compatible API&lt;/td&gt;
&lt;td&gt;✅ (subset)&lt;/td&gt;
&lt;td&gt;✅ (S3 Core)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Object versioning&lt;/td&gt;
&lt;td&gt;❌ Missing&lt;/td&gt;
&lt;td&gt;✅ Available&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Object Lock (WORM)&lt;/td&gt;
&lt;td&gt;❌ Missing&lt;/td&gt;
&lt;td&gt;✅ Available&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Server-side encryption&lt;/td&gt;
&lt;td&gt;❌ Missing&lt;/td&gt;
&lt;td&gt;✅ Available&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bucket / site replication (S3 API)&lt;/td&gt;
&lt;td&gt;❌ Missing&lt;/td&gt;
&lt;td&gt;✅ Available&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lifecycle (ILM)&lt;/td&gt;
&lt;td&gt;⚠️ Partial (Abort MPU + Expiration)&lt;/td&gt;
&lt;td&gt;✅ Available&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ACL / bucket policies&lt;/td&gt;
&lt;td&gt;❌ (own model)&lt;/td&gt;
&lt;td&gt;✅ (IAM / Policies)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Web console / admin API&lt;/td&gt;
&lt;td&gt;✅ (admin API)&lt;/td&gt;
&lt;td&gt;✅ (Web Console)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Geo-distribution model&lt;/td&gt;
&lt;td&gt;CRDT federation, no Raft/Paxos&lt;/td&gt;
&lt;td&gt;Distributed Mode (✅ Available)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Single binary&lt;/td&gt;
&lt;td&gt;✅ (&lt;code&gt;garage&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;License&lt;/td&gt;
&lt;td&gt;AGPL-3.0&lt;/td&gt;
&lt;td&gt;Apache 2.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Implementation&lt;/td&gt;
&lt;td&gt;Rust&lt;/td&gt;
&lt;td&gt;Rust&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two rows deserve a call-out. RustFS lists S3 Tables (Iceberg REST) as preview, so a mature lakehouse over RustFS data is not a promise the project makes yet. And Garage's missing versioning, Object Lock, SSE, and ACL rows are by design — its strength is resilient distribution, not S3-feature parity.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you choose between them?
&lt;/h2&gt;

&lt;p&gt;Use this rule of thumb: if the ticket says "backups across sites on hardware we own, AGPL is fine," start with Garage. If the ticket says "S3 endpoint for an app that needs versioning, immutability, or IAM, and we don't want copyleft," start with RustFS.&lt;/p&gt;

&lt;p&gt;A split is legitimate and common. Run Garage for the geo-distributed, secondhand-hardware layer where its CRDT model and tiny footprint pay off, and run RustFS in front of the S3-facing applications where versioning, Object Lock, and a MinIO-compatible dialect are the whole ask. They do not conflict; they occupy different layers. The mistake is forcing one to do the other's job — Garage dragged into an app that assumes Object Lock becomes a missing-feature ticket, and RustFS pushed into a no-copyleft-averse, flaky-link geo cluster carries obligations it was not built to carry.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Is Garage a drop-in MinIO replacement?
&lt;/h3&gt;

&lt;p&gt;No. Garage implements a subset of the S3 API — its own compatibility page lists versioning, Object Lock, replication, SSE, and ACL/policy as missing — so most S3 clients work for put/get/list but apps that assume MinIO's full feature set will break. RustFS targets the MinIO S3 dialect as its primary surface and lists MinIO On-Disk Compatibility as a preview feature. If a MinIO-compatible endpoint is the requirement, RustFS is the closer fit.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does Garage support object versioning?
&lt;/h3&gt;

&lt;p&gt;No. Garage's S3-compatibility page marks bucket versioning as "Missing" and notes it "does not (yet) support object versioning." &lt;code&gt;GetBucketVersioning&lt;/code&gt; returns a stub that always reports versioning disabled. If your workload needs versioned objects or point-in-time recovery through S3, Garage cannot provide it today; RustFS lists Versioning as available.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is RustFS Apache 2.0 like Garage is AGPL?
&lt;/h3&gt;

&lt;p&gt;No. RustFS is released under the permissive Apache 2.0 license with no copyleft, while Garage is AGPL-3.0. The practical difference: if you modify Garage and expose it as a network service, you must publish your modified source; RustFS imposes no such obligation. For a hosted or embedded product, that single license line is often the deciding factor.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can Garage run on a single node?
&lt;/h3&gt;

&lt;p&gt;Yes. Garage's quick start runs a single node with &lt;code&gt;garage server --single-node --default-bucket&lt;/code&gt;, which auto-configures a no-replication cluster and creates a default key and bucket. The docs warn this provides no data redundancy and "should not be used in production." Garage is designed for three nodes across zones; single-node is for evaluation and dev, not for durable storage.&lt;/p&gt;

&lt;h3&gt;
  
  
  Which one should I run for a homelab backup?
&lt;/h3&gt;

&lt;p&gt;If your backup needs versioning, immutability (Object Lock), or IAM-scoped keys, run RustFS — it ships all three and is Apache 2.0. If you want geo-distributed copies across secondhand machines, accept AGPL-3.0, and only need put/get plus presigned URLs, Garage is lighter and built exactly for that. Match the missing-feature list to your client before committing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Garage quick start (commands, ports, single-node): &lt;a href="https://garagehq.deuxfleurs.fr/documentation/quick-start/" rel="noopener noreferrer"&gt;https://garagehq.deuxfleurs.fr/documentation/quick-start/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Garage S3 compatibility status (versioning, Object Lock, SSE, replication, ACL missing): &lt;a href="https://garagehq.deuxfleurs.fr/documentation/reference-manual/s3-compatibility/" rel="noopener noreferrer"&gt;https://garagehq.deuxfleurs.fr/documentation/reference-manual/s3-compatibility/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Garage license (AGPL v3) and design goals (1 GB RAM, 16 GB disk, geo-distribution): &lt;a href="https://garagehq.deuxfleurs.fr/" rel="noopener noreferrer"&gt;https://garagehq.deuxfleurs.fr/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Garage source repository and language (Rust, AGPL-3.0): &lt;a href="https://git.deuxfleurs.fr/Deuxfleurs/garage" rel="noopener noreferrer"&gt;https://git.deuxfleurs.fr/Deuxfleurs/garage&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;RustFS README (Feature &amp;amp; Status, docker run, default credentials, Apache 2.0): &lt;a href="https://raw.githubusercontent.com/rustfs/rustfs/main/README.md" rel="noopener noreferrer"&gt;https://raw.githubusercontent.com/rustfs/rustfs/main/README.md&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>garage</category>
      <category>rustfs</category>
      <category>s3</category>
      <category>objectstorage</category>
    </item>
    <item>
      <title>SeaweedFS vs RustFS: When a Tiny Footprint Wins</title>
      <dc:creator>Ethan Carter</dc:creator>
      <pubDate>Sat, 19 Sep 2026 01:52:00 +0000</pubDate>
      <link>https://dev.to/ethan-carter/seaweedfs-vs-rustfs-when-a-tiny-footprint-wins-20f5</link>
      <guid>https://dev.to/ethan-carter/seaweedfs-vs-rustfs-when-a-tiny-footprint-wins-20f5</guid>
      <description>&lt;p&gt;SeaweedFS and RustFS are both Apache 2.0, single-binary stores, but they solve different problems. SeaweedFS is a distributed file system that also speaks S3, with a 16-byte per-object index that lets one master track billions of files. RustFS is a purpose-built, MinIO-compatible S3 object store with a web console. Pick SeaweedFS when you need a POSIX/FUSE mount, an Iceberg lakehouse, or billions of small files; pick RustFS when S3 is your only interface and a drop-in MinIO replacement in one docker run is the goal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key stats
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;SeaweedFS&lt;/th&gt;
&lt;th&gt;RustFS&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;License&lt;/td&gt;
&lt;td&gt;Apache 2.0&lt;/td&gt;
&lt;td&gt;Apache 2.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;What one binary serves&lt;/td&gt;
&lt;td&gt;S3 + POSIX file system + Iceberg lakehouse&lt;/td&gt;
&lt;td&gt;S3-compatible object storage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;In-memory index per object&lt;/td&gt;
&lt;td&gt;16-byte entry per blob&lt;/td&gt;
&lt;td&gt;not published&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;On-disk metadata per file&lt;/td&gt;
&lt;td&gt;40 bytes&lt;/td&gt;
&lt;td&gt;not published&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1KB-file throughput (1M files, conc 16, single SSD)&lt;/td&gt;
&lt;td&gt;15,708 write/s, 47,019 read/s&lt;/td&gt;
&lt;td&gt;not published&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The SeaweedFS throughput figure is its own README's unscientific single-machine number: 1 million 1KB files at concurrency 16 on a MacBook with an SSD. We cite it as a scale signal, not a hardware review.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the real difference between SeaweedFS and RustFS?
&lt;/h2&gt;

&lt;p&gt;SeaweedFS is a simple and highly scalable distributed file system. Its own README says one &lt;code&gt;weed&lt;/code&gt; binary "serves an S3 object store, a POSIX file system, and a lakehouse with S3 Tables, all over the same data." Under the hood, a master server tracks volumes (not individual files), volume servers store blobs in append-only volume files, and a filer exposes the data as a POSIX file system, an S3 endpoint, and an Iceberg catalog.&lt;/p&gt;

&lt;p&gt;RustFS is narrower by design. It is S3-compatible object storage written in Rust and released under the Apache 2.0 license. Its README's Feature &amp;amp; Status table marks S3 Core, Versioning, Object Lock (WORM), Lifecycle Management, Bucket Replication, Site Replication, IAM/Policies, Server-Side Encryption, and Distributed Mode as available today, with S3 Tables (Iceberg REST) in preview. There is no POSIX layer and no FUSE mount. The difference is the whole story: SeaweedFS is a file system that also does S3; RustFS is an S3 engine that does one thing.&lt;/p&gt;

&lt;h2&gt;
  
  
  How small is the metadata footprint, really?
&lt;/h2&gt;

&lt;p&gt;This is where "tiny footprint" stops being marketing. SeaweedFS packs small files into append-only volume files, so there is no per-file inode and no per-file metadata file. The volume server keeps a 16-byte index entry per blob in memory and reads it in a single seek, even for erasure-coded data; on disk it stores 40 bytes of metadata per file. Because the master tracks volumes rather than files, a cluster holding billions of files still has only a few thousand volumes, so the master stays small and never sits in the read path — clients cache the volume-to-server mapping and talk to volume servers directly.&lt;/p&gt;

&lt;p&gt;RustFS is a single Rust binary or container. The documented start is one docker run; there is no monitor quorum, no placement groups, no daemon zoo to reason about. We do not publish a per-object index size, and the comparison is not apples-to-apples — RustFS optimizes for S3 semantics, not for billions of tiny files on a single file system. The footprint win is real on different axes: SeaweedFS wins raw metadata scale; RustFS wins operational simplicity for an S3-only team.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does each one deploy?
&lt;/h2&gt;

&lt;p&gt;SeaweedFS ships as a single &lt;code&gt;weed&lt;/code&gt; binary. The fastest path is &lt;code&gt;weed mini&lt;/code&gt;, which starts the master, a volume server, the filer, WebDAV, the Iceberg REST catalog, and the Admin UI in one process, and exposes the S3 endpoint at &lt;a href="http://localhost:8333:" rel="noopener noreferrer"&gt;http://localhost:8333:&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;admin &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nv"&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;secret &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nv"&gt;S3_BUCKET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;my-bucket &lt;span class="se"&gt;\&lt;/span&gt;
./weed mini &lt;span class="nt"&gt;-dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;./data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To grow capacity, point more volume servers at the master:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;weed volume &lt;span class="nt"&gt;-dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/data &lt;span class="nt"&gt;-master&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;master_host&amp;gt;:9333
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;RustFS ships as a single binary or a container. The documented start command is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 9000:9000 &lt;span class="nt"&gt;-p&lt;/span&gt; 9001:9001 &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;/data:/data &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;/logs:/logs rustfs/rustfs:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That one line serves the S3 API on port 9000 and a console on 9001. For bare metal there is a one-line installer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-O&lt;/span&gt; https://rustfs.com/install_rustfs.sh &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; bash install_rustfs.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Default credentials are &lt;code&gt;rustfsadmin&lt;/code&gt; / &lt;code&gt;rustfsadmin&lt;/code&gt;, which you must rotate before any production exposure.&lt;/p&gt;

&lt;h2&gt;
  
  
  When should you pick SeaweedFS over RustFS?
&lt;/h2&gt;

&lt;p&gt;Reach for SeaweedFS when the workload is not "objects" but "files at extreme scale." Its design target — store billions of files, serve them fast — shows up in the numbers: a 16-byte per-blob index, 40 bytes per file on disk, and a master that tracks volumes, not files. If you are building a content-addressable store, a photo or video library, or an ML feature store with tens of millions of tiny shards, that metadata model is the difference between a master that fits in RAM and one that does not.&lt;/p&gt;

&lt;p&gt;SeaweedFS also wins the moment you need a file system or a lakehouse over the same bytes. One &lt;code&gt;weed&lt;/code&gt; binary serves an S3 endpoint, a FUSE mount on Linux/macOS/Windows, WebDAV, SFTP, HDFS, and an Iceberg REST catalog, all addressing the same data. If a data scientist wants to &lt;code&gt;ls&lt;/code&gt; the lakehouse while an app writes through S3, SeaweedFS does both without a sync job. RustFS does not offer a FUSE/POSIX mount, so that requirement alone steers you to SeaweedFS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where does RustFS win?
&lt;/h2&gt;

&lt;p&gt;RustFS wins on the axis most S3 teams actually care about: a clean, MinIO-compatible S3 endpoint with the least operational surface. One docker run, no quorum, no volume-server choreography. The feature set is now broad — Object Lock (WORM), Lifecycle Management, Bucket and Site Replication, IAM/Policies, OIDC/SSO, Server-Side Encryption, and Distributed Mode are all marked available in the README, with MinIO On-Disk Compatibility in preview. If your tools already speak MinIO's S3 dialect, RustFS is a drop-in.&lt;/p&gt;

&lt;p&gt;The Apache 2.0 license is the same as SeaweedFS's, so licensing is not the differentiator. The differentiator is focus: RustFS spends its complexity budget on S3 correctness and the web console, not on a file system layer. For a team whose only contract with storage is the S3 API, that focus means fewer knobs to misconfigure and a shorter path to a working endpoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  Feature-by-feature: what the docs say
&lt;/h2&gt;

&lt;p&gt;The table below is a straight read of each project's own documentation as of September 2026. SeaweedFS rows come from its GitHub README; RustFS rows come from its README Feature &amp;amp; Status table (2026-09-18). No benchmarks, no marketing.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Capability&lt;/th&gt;
&lt;th&gt;SeaweedFS&lt;/th&gt;
&lt;th&gt;RustFS&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;S3-compatible API&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅ (S3 Core)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;POSIX / FUSE file system&lt;/td&gt;
&lt;td&gt;✅ (FUSE on Linux/macOS/Windows)&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Iceberg / lakehouse over same data&lt;/td&gt;
&lt;td&gt;✅ (S3 Tables)&lt;/td&gt;
&lt;td&gt;🧪 Preview (S3 Tables, Iceberg REST)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Erasure coding&lt;/td&gt;
&lt;td&gt;✅ (warm data, background)&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bucket / site replication&lt;/td&gt;
&lt;td&gt;✅ (volume-level)&lt;/td&gt;
&lt;td&gt;✅ (Bucket + Site Replication)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Web console / Admin UI&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅ (Web Console)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kubernetes&lt;/td&gt;
&lt;td&gt;CSI driver + Operator&lt;/td&gt;
&lt;td&gt;✅ (Helm Charts)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Single binary&lt;/td&gt;
&lt;td&gt;✅ (weed)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;License&lt;/td&gt;
&lt;td&gt;Apache 2.0&lt;/td&gt;
&lt;td&gt;Apache 2.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Implementation&lt;/td&gt;
&lt;td&gt;Go&lt;/td&gt;
&lt;td&gt;Rust&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two rows deserve a call-out. RustFS lists S3 Tables (Iceberg REST) as Preview, so if a mature lakehouse over the same bytes is the requirement, SeaweedFS is ahead today. And SeaweedFS's erasure coding runs on warm data in the background, so writes never pay the encoding cost up front — a model RustFS does not yet offer in its feature table.&lt;/p&gt;

&lt;h2&gt;
  
  
  What can RustFS not replace yet?
&lt;/h2&gt;

&lt;p&gt;Be clear about the boundaries before you commit. RustFS's README has no FUSE/POSIX mount, so if you need a file system interface or a mounted lakehouse, SeaweedFS (or CephFS) is the answer today. RustFS's S3 Tables (Iceberg REST) is in preview, so a production lakehouse over RustFS data is not a promise the project makes yet. And while Distributed Mode is now marked available, a RustFS cluster at SeaweedFS-scale file counts is a different beast — SeaweedFS was built from the start to keep the master small at billions of files, and RustFS does not publish an equivalent per-object index claim.&lt;/p&gt;

&lt;p&gt;None of this is a knock on RustFS. It is a scope statement: RustFS is an S3 engine, and it is good at being one. Pushing it into the file-system or exabyte-file-count role is the wrong tool for that job.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you choose between them?
&lt;/h2&gt;

&lt;p&gt;Use this rule of thumb: if the words "file system," "FUSE," or "lakehouse" appear in the ticket, start with SeaweedFS. If only "S3" appears, and the real constraint is headcount, time, or MinIO compatibility, start with RustFS.&lt;/p&gt;

&lt;p&gt;A split is legitimate and common. Run SeaweedFS for the data-lakehouse and file-serving layer where billions of files and a POSIX mount pay off, and run RustFS in front of the S3-facing applications where a MinIO-compatible endpoint and a web console are the whole ask. They do not conflict; they occupy different layers. The mistake is forcing one to do the other's job — SeaweedFS dragged into a pure-S3, small-team role carries a file system you will never mount, and RustFS pushed into a POSIX role it does not implement becomes a custom FUSE you have to build yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Is SeaweedFS a drop-in replacement for MinIO?
&lt;/h3&gt;

&lt;p&gt;Not directly. SeaweedFS implements S3 as one of several interfaces on top of its distributed file system, so most S3 clients work, but it is not built to mirror MinIO's S3 dialect command for command. RustFS, by contrast, lists MinIO On-Disk Compatibility as a preview feature and targets the MinIO S3 dialect as its primary surface. If your only requirement is S3 and you want MinIO-compatible tooling, RustFS is the closer fit.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does SeaweedFS support erasure coding?
&lt;/h3&gt;

&lt;p&gt;Yes. SeaweedFS replicates hot data for speed and applies erasure coding to warm data in the background, so writes never pay the encoding cost up front. Volume servers replicate or erasure-code at the volume level, and the in-memory index stays 16 bytes per blob even for erasure-coded data. RustFS's feature table does not list erasure coding today.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does RustFS have a FUSE mount?
&lt;/h3&gt;

&lt;p&gt;No. RustFS's README Feature &amp;amp; Status table has no FUSE/POSIX mount, so there is no file system interface. If you need to mount storage as a local directory or expose a lakehouse over the same bytes, SeaweedFS provides a FUSE mount on Linux, macOS, and Windows. For pure S3 object access, the missing mount is irrelevant.&lt;/p&gt;

&lt;h3&gt;
  
  
  Which one scales to more files?
&lt;/h3&gt;

&lt;p&gt;SeaweedFS is designed for it: the master tracks volumes rather than files, so a cluster with billions of files holds only a few thousand volumes and the master stays small. RustFS is a capable S3 object store, but it does not publish a comparable per-object index claim, and its strength is S3 semantics at operationally simple scale rather than billions of tiny files on one file system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Are both projects Apache 2.0?
&lt;/h3&gt;

&lt;p&gt;Yes. Both SeaweedFS and RustFS are released under the Apache License 2.0, a permissive license with no copyleft. If you run either as a service the practical difference is small; if you embed the engine in a shipped product, Apache 2.0 spares you the copyleft questions that a GPL/LGPL project would raise.&lt;/p&gt;




</description>
      <category>seaweedfs</category>
      <category>rustfs</category>
      <category>s3</category>
      <category>objectstorage</category>
    </item>
    <item>
      <title>RustFS 1.0 Is GA After 2.5 Years: What's Actually Production-Ready, and What Isn't</title>
      <dc:creator>Ethan Carter</dc:creator>
      <pubDate>Thu, 17 Sep 2026 14:08:01 +0000</pubDate>
      <link>https://dev.to/ethan-carter/rustfs-10-is-ga-after-25-years-whats-actually-production-ready-and-what-isnt-15go</link>
      <guid>https://dev.to/ethan-carter/rustfs-10-is-ga-after-25-years-whats-actually-production-ready-and-what-isnt-15go</guid>
      <description>&lt;p&gt;RustFS 1.0.0 reached general availability on September 16, 2026. The core S3-compatible object storage engine is stable and supported for production. RustFS is a distributed, Apache 2.0-licensed object store written in Rust, now with 32,000+ GitHub stars and 2.7 million+ deployed instances worldwide.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key stats
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;GitHub stars&lt;/td&gt;
&lt;td&gt;32,000+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Docker Hub pulls&lt;/td&gt;
&lt;td&gt;10,000,000+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Instances deployed&lt;/td&gt;
&lt;td&gt;2.7 million+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Contributors&lt;/td&gt;
&lt;td&gt;160+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;First commit to GA&lt;/td&gt;
&lt;td&gt;Feb 2024 to Sep 2026 (2 yr 7 mo)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;License&lt;/td&gt;
&lt;td&gt;Apache 2.0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  RustFS 1.0.0 GA: what actually shipped
&lt;/h2&gt;

&lt;p&gt;RustFS 1.0.0 is the first generally available release of the project. "GA" here means one specific thing: the core object storage engine (PUT, GET, multipart upload, versioning, replication, and the S3 API surface) is stable and supported for production traffic. The team spent two years and seven months getting there, from the first commit in February 2024 to open sourcing in July 2025, then Beta in April 2026 and RC in August 2026.&lt;/p&gt;

&lt;p&gt;What shipped is not a rewrite of someone else's storage engine. RustFS is built from scratch in Rust, which the team chose for memory safety and concurrency under heavy write loads. GA matters because it lets you stop evaluating RustFS and start running it where downtime costs money. The release also closes a promise the project made at open source: a self-hosted, S3-compatible store with no commercial license gate on the core engine.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is RustFS, and why Rust?
&lt;/h2&gt;

&lt;p&gt;RustFS is a distributed object storage system that speaks the S3 API. You point an existing S3 client (the AWS CLI, MinIO's mc, rclone, or your application's SDK) at a RustFS endpoint and it behaves like S3. Under the hood it is written in Rust, which is the part that actually matters to operators.&lt;/p&gt;

&lt;p&gt;Rust brings two things to a storage engine. First, memory safety without a garbage collector, so there is no stop-the-world pause while you serve a hot bucket. Second, fearless concurrency: the borrow checker lets the team write parallel I/O paths that do not corrupt data under load. The practical result the project cites is steady throughput on PUT-heavy workloads. If you have run a Go-based store and fought GC tail latency, that is the class of problem RustFS targets. The trade-off is a smaller ecosystem and fewer prebuilt integrations than the incumbents.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is RustFS production-ready today?
&lt;/h2&gt;

&lt;p&gt;For the core object storage path, yes. RustFS 1.0.0 lists single-node and distributed deployment as available, along with versioning, object lock (WORM), bucket and site replication, durability through bitrot protection, and server-side encryption. Those are the features most teams actually turn on in production.&lt;/p&gt;

&lt;p&gt;What I would not do on day one is assume RustFS matches MinIO or Ceph feature for feature. The RustFS feature matrix marks S3 Tables (Apache Iceberg) as preview, not GA, and the surrounding tooling (backup operators, third-party connectors, managed offerings) is thinner than the incumbents. My recommendation: run RustFS for a real workload you can measure, not just a hello-world bucket. Stand up a single node, point rclone at it, and watch p99 latency under your own traffic before you trust it with the primary copy of anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  What can RustFS do that matters for storage teams?
&lt;/h2&gt;

&lt;p&gt;RustFS covers the S3 surface most applications need: multipart upload for large objects, versioning, object lock for compliance hold, and S3 Select for server-side filtering. On access it is not S3-only. The GA release ships the Swift API, WebDAV, FTPS, and SFTP alongside the S3 endpoint, which matters if you are retiring an old Swift cluster or want file-style access without a separate gateway.&lt;/p&gt;

&lt;p&gt;On the control plane, RustFS gives you IAM policies, OIDC single sign-on, a RustFS KMS, STS tokens, and audit logging. For operations there are event notifications, health inspection, multi-tenancy, and Helm charts for Kubernetes. Data movement is handled by bucket replication and site replication, plus ILM tiering to a remote S3 target. If your stack already speaks S3, the migration is mostly a DNS and credential change.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you run RustFS 1.0.0?
&lt;/h2&gt;

&lt;p&gt;The fastest path is a single container. The official image is &lt;code&gt;rustfs/rustfs:latest&lt;/code&gt;, and the README's verified command maps the API port 9000 and console port 9001, with volumes for data and logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 9000:9000 &lt;span class="nt"&gt;-p&lt;/span&gt; 9001:9001 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;/data:/data &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;/logs:/logs &lt;span class="se"&gt;\&lt;/span&gt;
  rustfs/rustfs:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;[sourced from RustFS GitHub README, NOT EXECUTED IN CI]&lt;/p&gt;

&lt;p&gt;Default credentials are &lt;code&gt;rustfsadmin&lt;/code&gt; / &lt;code&gt;rustfsadmin&lt;/code&gt; (change them before this touches a network). The web console is at &lt;code&gt;http://localhost:9001&lt;/code&gt;. For a bare-metal box there is also a one-click script: &lt;code&gt;curl -O https://rustfs.com/install_rustfs.sh &amp;amp;&amp;amp; bash install_rustfs.sh&lt;/code&gt;. Kubernetes users get Helm charts. For a quick distributed test, &lt;code&gt;docker compose&lt;/code&gt; with the project's &lt;code&gt;docker-compose-simple.yml&lt;/code&gt; brings up multiple nodes. Pick the method that matches where your workload already runs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where does RustFS still trail MinIO and Ceph?
&lt;/h2&gt;

&lt;p&gt;Honesty first: RustFS is younger. Ceph has a decade of production deployments and MinIO has a far larger install base and connector ecosystem. On mixed and read-heavy workloads RustFS is still closing the gap. Our beta.10 benchmarks showed RustFS ahead on PUT-heavy patterns, but read-mixed tuning is where the incumbents remain ahead today.&lt;/p&gt;

&lt;p&gt;S3 Tables (Iceberg) is in preview, so if your roadmap depends on in-storage analytics tables, that is not GA yet. The third gap is operational muscle memory: fewer community runbooks, fewer third-party backup tools with first-class RustFS support, and a smaller talent pool who have run it at scale. None of that is a reason to skip RustFS, but it is a reason to validate on your own workload and keep a rollback path. RustFS is betting that Rust's safety and write-path concurrency outweigh ecosystem lag over time.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next after 1.0.0 GA?
&lt;/h2&gt;

&lt;p&gt;GA is a start line, not a finish. The RustFS team's stated next steps are two: harden what shipped and push further into AI infrastructure. On hardening, the plan is to keep absorbing feedback from production users and close rough edges in replication, tiering, and the console.&lt;/p&gt;

&lt;p&gt;On the AI side, RustFS is adding S3 Vectors support and framing a RustFS 2.0 built around AI data centers, object storage that feeds training and inference pipelines directly. That direction fits the project's positioning as AI-native storage rather than a generic S3 bucket. For now, treat S3 Vectors and 2.0 as roadmap, not something you can deploy today. If vector storage or AI data lakes are on your 2027 plan, RustFS is worth watching, but build this year's system on what is GA.&lt;/p&gt;

&lt;h2&gt;
  
  
  Should you migrate from MinIO to RustFS now?
&lt;/h2&gt;

&lt;p&gt;If you are greenfield or adding a new storage tier, RustFS 1.0.0 is reasonable to adopt for production workloads that are S3-centric and write-heavy. The S3 compatibility means your existing clients work unchanged, and the Apache 2.0 license means no commercial surprise.&lt;/p&gt;

&lt;p&gt;If you already run MinIO or Ceph in production with mature tooling, do not rip it out. Migrate one workload, measure it, and expand only if RustFS earns it on your numbers. The lowest-risk first project is a secondary tier (backups, artifacts, or a staging bucket), where a write-path latency win shows up without betting the primary data path. RustFS is not asking you to trust a promise; it is asking you to run a benchmark on your own traffic. That is the right bar for a 1.0.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Is RustFS compatible with the S3 API?
&lt;/h3&gt;

&lt;p&gt;Yes. RustFS implements the S3 API, so existing S3 clients such as the AWS CLI, MinIO's mc, and rclone work against a RustFS endpoint without code changes. Multipart upload, versioning, and object lock are supported.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can RustFS replace MinIO?
&lt;/h3&gt;

&lt;p&gt;For S3-centric, write-heavy workloads, RustFS 1.0.0 is production-ready and a viable alternative. It does not have feature parity yet. MinIO's ecosystem and read-mixed performance lead. Migrate one workload first and measure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is RustFS free and open source?
&lt;/h3&gt;

&lt;p&gt;Yes. RustFS is licensed under Apache 2.0, with the source on GitHub. There is no commercial license gate on the core object storage engine.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I install RustFS?
&lt;/h3&gt;

&lt;p&gt;The fastest method is a single Docker container using the official &lt;code&gt;rustfs/rustfs:latest&lt;/code&gt; image (API on 9000, console on 9001). A one-click script (&lt;code&gt;install_rustfs.sh&lt;/code&gt;) covers bare metal, and Helm charts cover Kubernetes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does RustFS run distributed, and is it production-ready?
&lt;/h3&gt;

&lt;p&gt;Distributed deployment is listed as available in RustFS 1.0.0. Single-node mode is the simplest production start. Durability comes from bitrot protection, versioning, and bucket or site replication. S3 Tables (Iceberg) remains in preview.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at the &lt;a href="https://rustfs.com/blog/announcing-rustfs-1-0-0-ga/" rel="noopener noreferrer"&gt;RustFS blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>rust</category>
      <category>s3</category>
      <category>objectstorage</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Ceph vs RustFS: When to Use What (2026)</title>
      <dc:creator>Ethan Carter</dc:creator>
      <pubDate>Wed, 09 Sep 2026 12:15:00 +0000</pubDate>
      <link>https://dev.to/ethan-carter/ceph-vs-rustfs-when-to-use-what-2026-11h8</link>
      <guid>https://dev.to/ethan-carter/ceph-vs-rustfs-when-to-use-what-2026-11h8</guid>
      <description>&lt;h1&gt;
  
  
  Ceph vs RustFS: When to Use What (2026)
&lt;/h1&gt;

&lt;p&gt;Ceph and RustFS both speak S3, but they solve different problems. Ceph is a unified object, block, and file platform that scales to exabytes and carries real operational weight. RustFS is a single-binary, S3-focused object store for teams that want MinIO-level simplicity without a storage team. Choose Ceph when one cluster must serve block, object, and file. Choose RustFS when the workload is S3 objects and you want it live today.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the real difference between Ceph and RustFS?
&lt;/h2&gt;

&lt;p&gt;Ceph is a distributed object, block, and file storage platform. Its documentation states it uniquely delivers object, block, and file storage in one unified system, reaching from petabytes to exabytes of data across thousands of clients. Under the hood sits RADOS (Reliable Autonomic Distributed Object Store), with RBD for block, RGW for the S3/Swift gateway, and CephFS for POSIX file access layered on top.&lt;/p&gt;

&lt;p&gt;RustFS is narrower by design. It is S3-compatible object storage written in Rust and released under the Apache 2.0 license. The project's README lists S3 Core Features, Versioning, Bucket Replication, Event Notifications, Bitrot Protection, Single Node Mode, Multi-Tenancy, Keystone Auth, and the Swift API as available today. There is no block layer and no file layer. That scope difference is the whole story: Ceph is the generalist that spans three storage paradigms, RustFS is the S3 specialist that does one thing with a small operational surface.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does each one deploy?
&lt;/h2&gt;

&lt;p&gt;Ceph is a cluster of cooperating daemons. The official cephadm tool bootstraps a tiny cluster on a single node with one Monitor and one Manager, then you expand it by adding hosts and provisioning OSDs (object storage daemons), MDS (metadata servers, needed for CephFS), and RGW (the S3 gateway). Each daemon type carries its own configuration, failure modes, and tuning surface, and a production deployment normally spans several nodes with redundant monitors. This is not a flaw; it is the cost of unifying three storage paradigms in one system.&lt;/p&gt;

&lt;p&gt;RustFS ships as a single binary or a container. The documented start command is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 9000:9000 &lt;span class="nt"&gt;-p&lt;/span&gt; 9001:9001 &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;/data:/data &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;/logs:/logs rustfs/rustfs:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;[sourced from &lt;a href="https://github.com/rustfs/rustfs/blob/main/README.md" rel="noopener noreferrer"&gt;https://github.com/rustfs/rustfs/blob/main/README.md&lt;/a&gt;, NOT EXECUTED IN CI]&lt;/p&gt;

&lt;p&gt;That one line serves the S3 API on port 9000 and a console on 9001. For bare metal there is a one-line installer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-O&lt;/span&gt; https://rustfs.com/install_rustfs.sh &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; bash install_rustfs.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;[sourced from &lt;a href="https://github.com/rustfs/rustfs/blob/main/README.md" rel="noopener noreferrer"&gt;https://github.com/rustfs/rustfs/blob/main/README.md&lt;/a&gt;, NOT EXECUTED IN CI]&lt;/p&gt;

&lt;p&gt;Default credentials are &lt;code&gt;rustfsadmin&lt;/code&gt; / &lt;code&gt;rustfsadmin&lt;/code&gt;, which you must rotate before any production exposure.&lt;/p&gt;

&lt;h2&gt;
  
  
  When should you pick Ceph over RustFS?
&lt;/h2&gt;

&lt;p&gt;Reach for Ceph when a single storage layer has to do more than one job. If your Kubernetes cluster needs RBD block volumes for databases, an S3 endpoint for applications, and a CephFS mount for shared files, all from the same hardware, Ceph is one of the few open-source systems that covers all three. Its RADOS gateway implements a large subset of the Amazon S3 RESTful API, so most S3 clients work against it with only minor differences in canned ACLs.&lt;/p&gt;

&lt;p&gt;Ceph also earns its keep at serious scale. The project documents petabyte-to-exabyte deployments and a mature ecosystem with multiple vendors offering paid support. If you already employ storage engineers who know CRUSH maps, placement groups, and OSD recovery, Ceph's operational model is a feature rather than a tax. Choose it when scope and scale outrun simplicity, and when block or file access is a hard requirement rather than a nice-to-have.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where does RustFS win?
&lt;/h2&gt;

&lt;p&gt;RustFS wins on the axis most small and mid-size teams actually care about: time to a working S3 endpoint. One binary, one docker run, no monitor quorum to reason about, no placement groups to size. The Feature &amp;amp; Status table is honest about what is ready: S3 Core, Versioning, Bucket Replication, Event Notifications, and Bitrot Protection are marked available, which covers the common object-storage patterns, backup targets, and application data lakes.&lt;/p&gt;

&lt;p&gt;The Apache 2.0 license matters more than people admit. Ceph is mainly LGPL 2.1/3.0 with some GPL and BSD components; if you ever embed the engine rather than run it as a service, Apache 2.0 spares you copyleft questions. And because RustFS is written in Rust, there is no garbage collector in the request path, which keeps tail latency easier to reason about under mixed workloads. For teams that just need S3 without becoming storage experts, that combination is the point.&lt;/p&gt;

&lt;h2&gt;
  
  
  Feature-by-feature: what the docs say
&lt;/h2&gt;

&lt;p&gt;The table below is a straight read of each project's own documentation as of August 2026. No benchmarks, no marketing. Ceph's RGW feature rows come from the official RADOS Gateway S3 API reference; RustFS's come from its GitHub README Feature &amp;amp; Status table.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Capability&lt;/th&gt;
&lt;th&gt;Ceph (RGW)&lt;/th&gt;
&lt;th&gt;RustFS&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;S3 buckets, objects, multipart&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅ (S3 Core)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Object Versioning&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bucket Lifecycle&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;🚧 Under Testing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bucket Replication&lt;/td&gt;
&lt;td&gt;⚠️ Partial (across zones only)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bucket / Object Notifications&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅ (webhook)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Block storage (RBD)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌ (object only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;File storage (CephFS)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;K8s deployment&lt;/td&gt;
&lt;td&gt;Rook operator / cephadm&lt;/td&gt;
&lt;td&gt;✅ Helm Charts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Console / UI&lt;/td&gt;
&lt;td&gt;ceph-mgr dashboard&lt;/td&gt;
&lt;td&gt;✅ web console&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;License&lt;/td&gt;
&lt;td&gt;LGPL 2.1/3.0 (mixed)&lt;/td&gt;
&lt;td&gt;Apache 2.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Implementation&lt;/td&gt;
&lt;td&gt;C++ daemons&lt;/td&gt;
&lt;td&gt;Rust (single binary)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two rows deserve a call-out. RustFS lists Lifecycle Management as Under Testing, so do not promise automated tiering yet. Ceph's Bucket Replication is marked partial and permitted only across zones in RGW. Both gaps are real and both are documented by the projects themselves, not by us.&lt;/p&gt;

&lt;h2&gt;
  
  
  What can RustFS not replace yet?
&lt;/h2&gt;

&lt;p&gt;Be clear about the boundaries before you commit. RustFS does not provide block storage or a POSIX file system; the README's Feature &amp;amp; Status table has no RBD and no FUSE/POSIX mount, and no Object Lock. If your requirement is WORM retention or a mounted file system, Ceph or a purpose-built tool is the answer today.&lt;/p&gt;

&lt;p&gt;Distributed Mode is also Under Testing in RustFS. Single-node is a supported, solid story; multi-node production at petabyte scale is not a promise the project makes yet. Its ecosystem is younger than Ceph's by more than a decade, so you will find fewer third-party integrations and fewer war-story blog posts. I would not pitch RustFS as a drop-in for an existing exabyte Ceph fabric. For S3 object workloads that fit a single node or a modest cluster, it is a different conversation and one worth having.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you choose between them?
&lt;/h2&gt;

&lt;p&gt;Use this rule of thumb. If the words "block", "file system", and "object" all appear in the same ticket, start with Ceph. If only "S3" appears and the real constraint is headcount or time, start with RustFS.&lt;/p&gt;

&lt;p&gt;A hybrid is legitimate and common. Run Ceph for the infrastructure layer (RBD for stateful workloads, CephFS for shared files) and RustFS in front of the S3-facing applications where operational simplicity pays off. They do not conflict; they occupy different layers. The mistake is forcing one to do the other's job. Ceph dragged into a pure-S3, small-team role becomes expensive overhead. RustFS pushed past its single-node comfort zone becomes a bet on an untested distributed path. Match the tool to the layer and both stay boring in the good way.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Is RustFS a replacement for Ceph?
&lt;/h3&gt;

&lt;p&gt;Partially, and only for the object-storage slice. RustFS can replace the RGW (S3 gateway) portion of a Ceph stack for S3-focused workloads, with far less operational overhead. It cannot replace RBD block storage or CephFS file storage. If your stack needs block or file from the same cluster, Ceph stays the right call.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is Ceph harder to operate than RustFS?
&lt;/h3&gt;

&lt;p&gt;Yes, materially. Ceph runs multiple daemon types (MON, OSD, MDS, MGR, RGW), each with its own config and failure modes, and a real cluster spans several nodes. RustFS is one binary or one container; the documented start is a single docker run. The trade-off is scope: Ceph covers three storage paradigms, RustFS covers object storage with less to manage.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does Ceph support the S3 API?
&lt;/h3&gt;

&lt;p&gt;Yes. Ceph's RADOS Gateway implements a large subset of the Amazon S3 RESTful API, plus a subset of the OpenStack Swift API, with its own user management. Most S3 clients work against it, though Ceph notes differences in canned ACLs and a partial Bucket Replication (across zones only). It is not a 100 percent S3 clone, but it covers the common operations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can RustFS run on a single node?
&lt;/h3&gt;

&lt;p&gt;Yes. RustFS lists Single Node Mode as an available feature, and the documented quick start is a single docker run on one host serving S3 on port 9000 with a console on 9001. Multi-node distributed mode is Under Testing, so production scale-out is not a promise yet. For many S3 object workloads a single node is enough.&lt;/p&gt;

&lt;h3&gt;
  
  
  What license is each project under?
&lt;/h3&gt;

&lt;p&gt;RustFS is Apache 2.0, a permissive license with no copyleft. Ceph is mainly dual-licensed under LGPL 2.1 or 3.0, with some GPL and BSD components in the tree. If you run either as a service the practical difference is small; if you embed the engine in a shipped product, Apache 2.0 is the more flexible choice.&lt;/p&gt;




</description>
      <category>ceph</category>
      <category>rustfs</category>
      <category>s3</category>
      <category>distributedstorage</category>
    </item>
    <item>
      <title>Wasabi vs RustFS: S3-Compatible Cost Showdown</title>
      <dc:creator>Ethan Carter</dc:creator>
      <pubDate>Mon, 07 Sep 2026 12:14:10 +0000</pubDate>
      <link>https://dev.to/ethan-carter/wasabi-vs-rustfs-s3-compatible-cost-showdown-46i1</link>
      <guid>https://dev.to/ethan-carter/wasabi-vs-rustfs-s3-compatible-cost-showdown-46i1</guid>
      <description>&lt;p&gt;&lt;em&gt;Disclosure: I work on RustFS. Every comparison number below is pulled from a primary source and dated 2026-08-21, so check the Sources table before you budget.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Wasabi wins on simplicity: one flat $7.99 per TB per month with no egress or API fees, ideal when you have no hardware and want 16 regions by this afternoon. RustFS wins on raw dollars per TB once you already own or colocate servers, because storage becomes disks you bought instead of a recurring bill. The real question is not "which is cheaper" but "who carries the hardware, power, and operations."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Stats&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Wasabi&lt;/th&gt;
&lt;th&gt;RustFS&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Storage price&lt;/td&gt;
&lt;td&gt;$7.99 / TB / mo (NA, EMEA, APAC)&lt;/td&gt;
&lt;td&gt;$0 / TB / mo — you supply disks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Egress&lt;/td&gt;
&lt;td&gt;Free under fair-use (≤ stored volume / mo)&lt;/td&gt;
&lt;td&gt;Free — it is your own network&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API requests&lt;/td&gt;
&lt;td&gt;Free under fair-use&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Minimum term&lt;/td&gt;
&lt;td&gt;90-day retention charge&lt;/td&gt;
&lt;td&gt;No minimum&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Minimum commitment&lt;/td&gt;
&lt;td&gt;1 TB billed even if unused&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage regions&lt;/td&gt;
&lt;td&gt;16 regions&lt;/td&gt;
&lt;td&gt;Wherever you place the node&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Durability claim&lt;/td&gt;
&lt;td&gt;11 nines (99.999999999%)&lt;/td&gt;
&lt;td&gt;Your redundancy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Object Lock (WORM)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Not in README Feature table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;License&lt;/td&gt;
&lt;td&gt;Proprietary&lt;/td&gt;
&lt;td&gt;Apache 2.0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What This Cost Showdown Actually Measures
&lt;/h2&gt;

&lt;p&gt;A cost showdown between a cloud and a self-hosted object store is not a single price tag. It is the recurring opex of renting capacity versus the capital plus operations of owning it. Wasabi quotes a flat rate per terabyte per month and bills nothing extra for moving data in, out, or for API calls, within fair-use limits. RustFS is software you run; the bill is the servers, disks, power, and the engineer who gets paged. The comparison only makes sense once you know your dataset size, how long it lives, and whether you already pay for a rack somewhere. Everything below keeps those three variables explicit so the numbers stay honest.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Does Wasabi Price Storage?
&lt;/h2&gt;

&lt;p&gt;Wasabi's pay-as-you-go Hot Cloud Storage lists at $7.99 per TB per month across North America, EMEA, and APAC, with no separate charge for ingress, egress, or API requests (Wasabi pricing page, checked 2026-08-19). That flat rate is the headline and the reason backup vendors default to it. For 100 TB, the math is 100 × $7.99 = $799 per month, or $9,588 per year, before any minimums. Reserved-capacity terms in 1, 3, or 5-year blocks lower that further if you can commit capacity up front. What you do not see on the front page is the small print that decides whether your real bill matches the calculator, and that is where the next section lives.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Does the Wasabi Bill Actually Bite?
&lt;/h2&gt;

&lt;p&gt;Three clauses in the pay-as-you-go FAQ change the number for real workloads. First, a 90-day minimum storage duration: delete an object before 90 days and you still pay "Timed Deleted Storage" for the remaining days, so high-churn data costs more than its live size suggests. Second, a 1 TB monthly minimum means storing 200 GB still bills a full terabyte. Third, objects under 4 KB are billed as 4 KB, and enabling versioning or bucket logging grows your stored bytes and your bill. Egress is free only while monthly downloads stay at or below your active stored volume; above that, Wasabi calls it "not a good fit" and can limit or suspend service.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Does RustFS Actually Cost to Run?
&lt;/h2&gt;

&lt;p&gt;RustFS charges nothing per gigabyte because it is software, not capacity. Your cost is hardware. A 24 TB enterprise HDD listed for roughly $400 to $600 in 2025 (market retail range), so raw capacity capital lands near $17 to $25 per TB one-time. Amortize that drive over a five-year life and disk capEx is about $3.40 to $5 per TB per year, before you account for mirrors or backups. Power, cooling, and rack space are extra, but your bandwidth is free because the data never leaves your network through a metered exit. The trade you accept is operations: patching, monitoring, and recovery are on you, not a support ticket.&lt;/p&gt;

&lt;h2&gt;
  
  
  RustFS Feature Reality Check: What You Get, What You Don't
&lt;/h2&gt;

&lt;p&gt;RustFS's README Feature &amp;amp; Status table is the only honest source for what the software does today. Marked Available: S3 Core, Upload/Download, Versioning, Bucket Replication, Logging, Event Notifications, K8s Helm Charts, Keystone Auth, Multi-Tenancy, Swift API, Bitrot Protection, and Single Node Mode. Under Testing: Lifecycle Management, Distributed Mode, and RustFS KMS. The table does not list Object Lock, erasure coding, or a FUSE mount, so do not plan a WORM archive or a POSIX filesystem on RustFS yet. Read it plainly: single-node RustFS is ready now, multi-node redundancy is not a production promise. I work on RustFS, so weigh that against the README, not my enthusiasm.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Do the Two Compare on Cost-Relevant Features?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;Wasabi&lt;/th&gt;
&lt;th&gt;RustFS&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Footprint&lt;/td&gt;
&lt;td&gt;16 managed regions&lt;/td&gt;
&lt;td&gt;Your hardware, your site&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Durability&lt;/td&gt;
&lt;td&gt;11 nines (vendor claim)&lt;/td&gt;
&lt;td&gt;What you build (mirror or backup)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Immutable retention&lt;/td&gt;
&lt;td&gt;Object Lock: WORM, legal hold&lt;/td&gt;
&lt;td&gt;Not available today&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lifecycle / tiering&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Under Testing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Egress model&lt;/td&gt;
&lt;td&gt;Free under fair-use&lt;/td&gt;
&lt;td&gt;Your network, no meter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lock-in&lt;/td&gt;
&lt;td&gt;Proprietary account&lt;/td&gt;
&lt;td&gt;Apache 2.0, you own it&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The features that move cost are durability and lifecycle. Wasabi hands you 11 nines and Object Lock out of the box; RustFS hands you the software and expects you to supply redundancy until Distributed Mode ships. If compliance retention matters, that gap is the whole decision today.&lt;/p&gt;

&lt;h2&gt;
  
  
  When Should You Pick Wasabi?
&lt;/h2&gt;

&lt;p&gt;Pick Wasabi when you have no servers and no interest in running storage. It is the faster path to 16 regions, data-residency choices across North America, Europe, and Asia, and immutable backups through Object Lock without standing up anything. The flat $7.99 per TB per month is easy to defend in a budget review because there is no egress line item to explain. For backup, DR, and active archive where someone else should carry the pager, the recurring fee buys absence of operational load. If your team's core product is not storage, renting it is usually the cheaper career move.&lt;/p&gt;

&lt;h2&gt;
  
  
  When Does RustFS Win on Total Cost?
&lt;/h2&gt;

&lt;p&gt;RustFS wins when the dataset is large, stable, and already sitting on hardware you pay for. At 100 TB, Wasabi recurs at $9,588 per year; the same capacity as disks you own amortizes to a few dollars per TB per year in capital, and your egress is free because the traffic never crosses a metered provider link. The break-even is earlier the more you download, since Wasabi's fair-use cap bites exactly where RustFS has no meter. The catch is durability: until Distributed Mode leaves testing, mirror to a second node or back up to another S3 target yourself. You trade a recurring fee for owned infrastructure and Apache-2.0 freedom from lock-in.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Do You Point Your Tools at Each?
&lt;/h2&gt;

&lt;p&gt;Both speak the S3 API, so the same SDKs and CLI habits apply. For Wasabi, the MinIO Client connects with the commands from Wasabi's official doc:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Wasabi via MinIO Client (mc) — verbatim from docs.wasabi.com&lt;/span&gt;
&lt;span class="c"&gt;# [sourced from https://docs.wasabi.com/how-do-i-use-minio-client-with-wasabi, NOT EXECUTED IN CI]&lt;/span&gt;
wget https://dl.minio.io/client/mc/release/linux-amd64/mc
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x mc
./mc config host add https://s3.wasabisys.com   &lt;span class="c"&gt;# prompts for access/secret key&lt;/span&gt;
./mc &lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the AWS CLI, also verbatim from Wasabi's doc:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# [sourced from https://docs.wasabi.com/v1/docs/how-do-i-use-aws-cli-with-wasabi, NOT EXECUTED IN CI]&lt;/span&gt;
aws s3 &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;--endpoint-url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://s3.wasabisys.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;RustFS starts with the README's docker command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# [sourced from https://github.com/rustfs/rustfs, NOT EXECUTED IN CI]&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 9000:9000 &lt;span class="nt"&gt;-p&lt;/span&gt; 9001:9001 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;/data:/data &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;/logs:/logs &lt;span class="se"&gt;\&lt;/span&gt;
  rustfs/rustfs:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Default credentials are &lt;code&gt;rustfsadmin&lt;/code&gt; / &lt;code&gt;rustfsadmin&lt;/code&gt;; change them before exposing port 9000.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Is Wasabi really cheaper than AWS S3?
&lt;/h3&gt;

&lt;p&gt;At typical access volumes, yes. Wasabi's own comparison example sizes 100 TB stored with 20 TB downloaded per month at about $818 versus roughly $4,097 for AWS S3, once egress is included. The gap comes almost entirely from Wasabi's missing egress and API line items. Whether that beats self-hosting depends on whether you already own the hardware; the cloud comparison only holds against other clouds.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does RustFS charge per gigabyte?
&lt;/h3&gt;

&lt;p&gt;No. RustFS is open-source software under Apache 2.0; you run it on your own disks and pay no per-GB fee and no egress fee, because the data stays on your network. Your cost is the servers, drives, power, and the operations work to keep them healthy. Treat it as capital and labor, not a metered bill.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can RustFS replace Wasabi for immutable backups?
&lt;/h3&gt;

&lt;p&gt;Not today for compliance retention. RustFS's README Feature &amp;amp; Status table does not list Object Lock, while Wasabi and Backblaze B2 both offer S3 Object Lock with WORM and legal hold. If you need ransomware-immutable, audit-ready retention, use Wasabi, B2, or Ceph now and keep RustFS for analytical and batch object workloads where immutability is not the requirement.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is Wasabi's 90-day minimum storage policy?
&lt;/h3&gt;

&lt;p&gt;Objects deleted before they have been stored 90 days incur a "Timed Deleted Storage" charge for the remaining days, comparable to minimum-duration policies on some hyperscaler tiers. It mainly hurts high-churn workloads where data lives for days or weeks. For long-lived archives it is invisible, because the data stays past the threshold anyway.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is RustFS production-ready?
&lt;/h3&gt;

&lt;p&gt;Single-node RustFS is marked Available and is a reasonable place to start today. Distributed Mode, Lifecycle Management, and RustFS KMS are still Under Testing, so multi-node redundancy and automated tiering are not production promises yet. Run a single node and mirror or back it up to a second S3 target until Distributed Mode ships; do not bet a WORM archive on it yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;p&gt;All claims checked against these primary sources on &lt;strong&gt;2026-08-21&lt;/strong&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Claim&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;$7.99/TB/mo, no egress/API fees, 1 TB monthly min, 90-day retention, 4 KB min object, fair-use egress cap&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://wasabi.com/pricing" rel="noopener noreferrer"&gt;https://wasabi.com/pricing&lt;/a&gt; , &lt;a href="https://wasabi.com/pricing/faq" rel="noopener noreferrer"&gt;https://wasabi.com/pricing/faq&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;16 storage regions, S3 + IAM API, Object Lock, 11 nines durability&lt;/td&gt;
&lt;td&gt;&lt;a href="https://wasabi.com/cloud-object-storage" rel="noopener noreferrer"&gt;https://wasabi.com/cloud-object-storage&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wasabi mc commands&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.wasabi.com/how-do-i-use-minio-client-with-wasabi" rel="noopener noreferrer"&gt;https://docs.wasabi.com/how-do-i-use-minio-client-with-wasabi&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wasabi AWS CLI commands&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.wasabi.com/v1/docs/how-do-i-use-aws-cli-with-wasabi" rel="noopener noreferrer"&gt;https://docs.wasabi.com/v1/docs/how-do-i-use-aws-cli-with-wasabi&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RustFS Feature &amp;amp; Status, docker run, default creds, Apache 2.0, &lt;code&gt;1.0.0-rc.3&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/rustfs/rustfs" rel="noopener noreferrer"&gt;https://github.com/rustfs/rustfs&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enterprise 20TB+ HDD retail range $400-$600 (2025), used as raw-capacity cost anchor&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://www.accio.com/business/hdd-failure-rate" rel="noopener noreferrer"&gt;https://www.accio.com/business/hdd-failure-rate&lt;/a&gt; (market summary, "Enterprise High-Capacity HDDs (20TB+)" section)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




</description>
      <category>wasabi</category>
      <category>rustfs</category>
      <category>s3</category>
      <category>objectstorage</category>
    </item>
    <item>
      <title>Self-Hosting S3-Compatible Storage on Bare Metal</title>
      <dc:creator>Ethan Carter</dc:creator>
      <pubDate>Thu, 27 Aug 2026 12:52:00 +0000</pubDate>
      <link>https://dev.to/ethan-carter/self-hosting-s3-compatible-storage-on-bare-metal-1e89</link>
      <guid>https://dev.to/ethan-carter/self-hosting-s3-compatible-storage-on-bare-metal-1e89</guid>
      <description>&lt;p&gt;You self-host S3-compatible storage on bare metal by installing a single Rust binary on a Linux server and pointing any S3 client at it. RustFS installs with one script, listens on port 9000 (S3 API) and 9001 (console), and is Apache 2.0 licensed. Single-node mode is production-ready today; multi-node clustering is still under testing.&lt;/p&gt;

&lt;p&gt;Every command below is copied verbatim from the official source cited beside it. This sandbox has no Docker daemon, so none of the commands were executed here; they are marked accordingly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Stats&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Fact&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;RustFS installs with one command and runs as a systemd service on x86_64 or aarch64 Linux&lt;/td&gt;
&lt;td&gt;RustFS docs (Linux quick-start)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Default S3 API port is 9000; console port is 9001&lt;/td&gt;
&lt;td&gt;RustFS GitHub README&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Default credentials are &lt;code&gt;rustfsadmin&lt;/code&gt; / &lt;code&gt;rustfsadmin&lt;/code&gt; and must be changed&lt;/td&gt;
&lt;td&gt;RustFS README + docs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RustFS is Apache 2.0 licensed and S3-compatible&lt;/td&gt;
&lt;td&gt;RustFS GitHub README&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Single-node mode is production-ready; distributed mode is still under testing&lt;/td&gt;
&lt;td&gt;RustFS README Feature &amp;amp; Status&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What is self-hosted S3-compatible storage?
&lt;/h2&gt;

&lt;p&gt;A self-hosted S3-compatible storage server is a program you run on your own hardware that speaks the Amazon S3 API. Applications using AWS SDKs, the &lt;code&gt;aws&lt;/code&gt; CLI, or MinIO's &lt;code&gt;mc&lt;/code&gt; can talk to it without code changes, because the bucket, object, and credential model matches S3. The difference from a cloud bucket is ownership: the disks, the network path, and the uptime are yours.&lt;/p&gt;

&lt;p&gt;RustFS is one such server, written in Rust and licensed under Apache 2.0. It exposes the S3 API on port 9000 and a web console on 9001, and it stores objects on the local filesystem. Because it is S3-compatible, the same client code that targets AWS S3 also targets a RustFS node. That compatibility is the whole point of self-hosting here: you get an S3 endpoint without renting one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why run object storage on bare metal?
&lt;/h2&gt;

&lt;p&gt;Running object storage on bare metal means installing the server directly on a Linux machine instead of in a container or a managed cloud. The appeal is control. Your data stays on disks you own, in a network you define, and the cost is the hardware plus your time rather than a per-gigabyte cloud bill that grows with every upload.&lt;/p&gt;

&lt;p&gt;The trade-off is real and worth stating plainly. You own the failure modes too. A single disk dies, the server reboots, the certs expire, and that is now your incident, not a vendor's. Bare metal suits teams that already run Linux servers, care about data residency, or have steady high-volume storage that makes a cloud bill painful. It is a poor fit if you want zero operational responsibility. Self-hosting trades a recurring fee for recurring attention.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you need before you start
&lt;/h2&gt;

&lt;p&gt;Minimum: a Linux server with systemd on x86_64 or aarch64, root or sudo access, the &lt;code&gt;unzip&lt;/code&gt; utility, outbound network access to pull the package, and open ports 9000 (S3 API) and 9001 (console) so clients and the browser console can reach the service.&lt;/p&gt;

&lt;p&gt;Plan the data disk before you install. The quick-start runs RustFS in single-node, single-disk (SNSD) mode, which stores everything under &lt;code&gt;/data/rustfs0&lt;/code&gt; with no redundancy. That is fine for evaluation and dev, but a single disk has no spare. For anything you would hate to lose, attach a second disk, schedule filesystem or object-level backups, or wait for distributed mode to leave testing. The installer lets you change the data path and ports at install time, so decide the layout now rather than after data accumulates.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you install RustFS on bare metal?
&lt;/h2&gt;

&lt;p&gt;Run the official one-line installer. It downloads the binary, installs it to &lt;code&gt;/usr/local/bin/rustfs&lt;/code&gt;, registers a systemd service, starts it, and stores data under &lt;code&gt;/data/rustfs0&lt;/code&gt; by default:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-O&lt;/span&gt; https://rustfs.com/install_rustfs.sh &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; bash install_rustfs.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;[sourced from RustFS docs (Linux quick-start), NOT EXECUTED IN CI]&lt;/p&gt;

&lt;p&gt;After it finishes you get a summary with the service port (9000), console port (9001), and data directory, plus a security warning to replace the default credentials. Open &lt;code&gt;http://&amp;lt;server-ip&amp;gt;:9001&lt;/code&gt; in a browser to reach the console, then log in with the access and secret keys you set. The same server answers S3 API calls on 9000. From here you can create a bucket in the console or from the command line, which the next sections cover.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you run RustFS with Docker instead?
&lt;/h2&gt;

&lt;p&gt;If you prefer a container, the Docker image gives you the same server with no systemd dependency. The official command maps both ports, persists data and logs to local volumes, and detaches:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 9000:9000 &lt;span class="nt"&gt;-p&lt;/span&gt; 9001:9001 &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;/data:/data &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;/logs:/logs rustfs/rustfs:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;[sourced from RustFS GitHub README, NOT EXECUTED IN CI]&lt;/p&gt;

&lt;p&gt;Do not drop the &lt;code&gt;-p&lt;/code&gt; or &lt;code&gt;-v&lt;/code&gt; flags. Without &lt;code&gt;-p 9000:9000&lt;/code&gt; the S3 API is unreachable from the host, and without the volume mounts a container restart loses everything under &lt;code&gt;/data&lt;/code&gt; and &lt;code&gt;/logs&lt;/code&gt;. The image tag &lt;code&gt;:latest&lt;/code&gt; tracks the newest build; pin a specific release like &lt;code&gt;1.0.0-rc.2&lt;/code&gt; if you need reproducibility. Podman users run the equivalent with &lt;code&gt;:Z,U&lt;/code&gt; on the volume mounts for SELinux. Either path lands you at the same console on 9001 and the same S3 endpoint on 9000.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you point an S3 client at your server?
&lt;/h2&gt;

&lt;p&gt;RustFS ships its own CLI, &lt;code&gt;rc&lt;/code&gt;, which handles both object operations and admin checks. After installing it, register an alias that stores the endpoint and keys, then verify the server is alive:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rc &lt;span class="nb"&gt;alias set local &lt;/span&gt;http://localhost:9000 &amp;lt;your-access-key&amp;gt; &amp;lt;your-secret-key&amp;gt;
rc ping &lt;span class="nb"&gt;local
&lt;/span&gt;rc ready &lt;span class="nb"&gt;local&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;[sourced from RustFS docs (rc CLI), NOT EXECUTED IN CI]&lt;/p&gt;

&lt;p&gt;Create a bucket and push a file the same way any S3 client would:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rc bucket create &lt;span class="nb"&gt;local&lt;/span&gt;/my-bucket
rc object copy /path/to/hello.txt &lt;span class="nb"&gt;local&lt;/span&gt;/my-bucket/hello.txt
rc object list &lt;span class="nb"&gt;local&lt;/span&gt;/my-bucket
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;[sourced from RustFS docs (rc CLI), NOT EXECUTED IN CI]&lt;/p&gt;

&lt;p&gt;Because the API is S3-compatible, AWS SDKs, the &lt;code&gt;aws&lt;/code&gt; CLI with &lt;code&gt;--endpoint-url&lt;/code&gt;, and MinIO's &lt;code&gt;mc&lt;/code&gt; all work against port 9000 with the same keys. You are not locked into &lt;code&gt;rc&lt;/code&gt;; it is just the native client. Point your application at &lt;code&gt;http://&amp;lt;server-ip&amp;gt;:9000&lt;/code&gt; and the buckets behave like any other S3 endpoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  What RustFS does today, and what it doesn't
&lt;/h2&gt;

&lt;p&gt;RustFS today is a solid single-node S3-compatible server. The features marked available in the project's own status table include S3 core operations, upload/download, versioning, logging, event notifications, bucket replication, bitrot protection, single-node mode, multi-tenancy, Keystone auth, and the Swift API. If your workload fits one node, those are real and usable now.&lt;/p&gt;

&lt;p&gt;Be honest about the gaps. Distributed mode, lifecycle management, and RustFS KMS are still under testing, so multi-node erasure-coded clusters and automated tiering or expiry are not production promises yet. There is no Object Lock, no FUSE or POSIX mount, and no built-in erasure coding in the current release. RustFS does not ship a turnkey multi-region setup. For a single bare-metal node serving S3 to your apps, that is plenty. If you need cross-node redundancy or compliance retention today, plan around those limits rather than assuming they exist.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you keep a bare-metal server healthy?
&lt;/h2&gt;

&lt;p&gt;First, change the default credentials. The installer leaves placeholder keys in &lt;code&gt;/etc/default/rustfs&lt;/code&gt;; set your own access and secret keys there, then restart:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RUSTFS_ACCESS_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;your-access-key&amp;gt;
&lt;span class="nv"&gt;RUSTFS_SECRET_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;your-secret-key&amp;gt;   &lt;span class="c"&gt;# e.g. output of: openssl rand -base64 24&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart rustfs
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl status rustfs &lt;span class="nt"&gt;--no-pager&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;[sourced from RustFS docs (Linux quick-start), NOT EXECUTED IN CI]&lt;/p&gt;

&lt;p&gt;Keep the service under systemd so it restarts on boot, and watch &lt;code&gt;systemctl status rustfs&lt;/code&gt; for the active (running) line. Because single-node mode has no redundancy, back up the data directory or replicate the bucket to a second site on a schedule you trust. Check disk space before it is gone, since a full volume stops writes. None of this is exotic, but it is yours to do. The console on 9001 shows bucket and object state when you want a visual check.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Is RustFS free to self-host?
&lt;/h3&gt;

&lt;p&gt;Yes. RustFS is licensed under Apache 2.0, so you can run it on your own hardware at no license cost. You pay only for the server, disks, and your time to operate it.&lt;/p&gt;

&lt;h3&gt;
  
  
  What are the default RustFS credentials?
&lt;/h3&gt;

&lt;p&gt;The default access key and secret key are both &lt;code&gt;rustfsadmin&lt;/code&gt;. The installer warns you to replace them, and if you leave them unset the server falls back to those same defaults. Set &lt;code&gt;RUSTFS_ACCESS_KEY&lt;/code&gt; and &lt;code&gt;RUSTFS_SECRET_KEY&lt;/code&gt; in &lt;code&gt;/etc/default/rustfs&lt;/code&gt; and restart the service before exposing the port.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can RustFS replace MinIO on a single Linux server?
&lt;/h3&gt;

&lt;p&gt;For single-node S3-compatible needs, yes. Both speak the S3 API, so existing client code and SDKs work against either. RustFS is written in Rust, licensed Apache 2.0, and ships its own &lt;code&gt;rc&lt;/code&gt; CLI alongside the standard S3 interface.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does RustFS support multi-node distributed storage?
&lt;/h3&gt;

&lt;p&gt;Distributed mode is listed as under testing in the project's Feature &amp;amp; Status table, so it is not a production promise yet. Single-node mode is available and production-ready today. If you need cross-node erasure coding now, that gap should factor into your decision.&lt;/p&gt;

&lt;h3&gt;
  
  
  What port does RustFS use?
&lt;/h3&gt;

&lt;p&gt;The S3 API listens on port 9000 and the web console on 9001. Both must be reachable: 9000 for client and application traffic, 9001 for the browser console.&lt;/p&gt;

</description>
      <category>selfhosting</category>
      <category>s3</category>
      <category>objectstorage</category>
      <category>devops</category>
    </item>
    <item>
      <title>Build a Data Lake on S3-Compatible Storage</title>
      <dc:creator>Ethan Carter</dc:creator>
      <pubDate>Tue, 25 Aug 2026 11:50:00 +0000</pubDate>
      <link>https://dev.to/ethan-carter/build-a-data-lake-on-s3-compatible-storage-d5f</link>
      <guid>https://dev.to/ethan-carter/build-a-data-lake-on-s3-compatible-storage-d5f</guid>
      <description>&lt;p&gt;A data lake on S3-compatible storage is an object bucket holding open file formats (Parquet, ORC, JSON) partitioned by key, queried in place by engines like DuckDB, Spark, or Trino. You skip a proprietary warehouse, keep the raw data, and control the bill. The only hard requirement is a solid S3-compatible API.&lt;/p&gt;

&lt;p&gt;Every command below is copied verbatim from the official source cited beside it. This sandbox has no Docker daemon, so none of the commands were executed here; they are marked accordingly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Stats&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Fact&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;AWS S3 has delivered strong read-after-write consistency for all objects since Dec 1, 2020&lt;/td&gt;
&lt;td&gt;AWS What's New (2020-12)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AWS limits each account to 10,000 buckets&lt;/td&gt;
&lt;td&gt;AWS S3 docs (BucketRestrictions)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RustFS is Apache 2.0 licensed and S3-compatible&lt;/td&gt;
&lt;td&gt;RustFS GitHub README&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DuckDB reads Parquet directly from &lt;code&gt;s3://&lt;/code&gt; through its httpfs extension&lt;/td&gt;
&lt;td&gt;DuckDB httpfs S3 API docs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Iceberg, Delta Lake, and Hudi all store Parquet data files plus metadata on object storage&lt;/td&gt;
&lt;td&gt;Project documentation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What is a data lake on S3-compatible storage?
&lt;/h2&gt;

&lt;p&gt;A data lake on S3-compatible storage is a bucket that stores raw and transformed data as files, not rows in a database. The files use open formats like Parquet, ORC, or JSON, and they sit under key prefixes that act like folders. An analytics engine reads those files over the S3 API and returns rows, but the data never has to move into a separate warehouse.&lt;/p&gt;

&lt;p&gt;The phrase S3-compatible matters because it widens your options. You can start on AWS S3, then run the same code against MinIO, Wasabi, Cloudflare R2, or a self-hosted RustFS cluster without rewriting your pipelines. The object store becomes a neutral substrate: cheap, durable, and reachable from every tool that speaks S3.&lt;/p&gt;

&lt;p&gt;In other words, a data lake is less a product and more a layout convention. Get the layout right and any engine can read it. Get it wrong and you rebuild queries every quarter.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why build your data lake on S3-compatible storage?
&lt;/h2&gt;

&lt;p&gt;You build a data lake on S3-compatible storage because the S3 API is the closest thing the data world has to a universal port. Every query engine, every backup tool, every ETL job already knows how to talk to it. That reach means you are never locked into one vendor's query semantics or one warehouse's pricing page.&lt;/p&gt;

&lt;p&gt;The cost angle is real but it is not automatic. Cloud S3 bills per GB-month plus per-request and egress charges, and egress is where lakes get expensive when you query from outside the region. Self-hosting flips that math: you pay for disks and bandwidth you already own, and you can co-locate compute so reads stay on the local network. The trade is operational. Someone runs the cluster, patches it, and owns the failures.&lt;/p&gt;

&lt;p&gt;For a small team, starting on a managed S3 endpoint and keeping the code portable is usually the pragmatic first move. You can move the data later without touching the queries.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you lay out a data lake in object storage?
&lt;/h2&gt;

&lt;p&gt;The layout is the part people skip and later regret. A data lake in object storage works best with Hive-style partitioning, where each key path encodes a dimension as a directory. A common shape is &lt;code&gt;events/dt=2026-08-14/region=us/part-001.parquet&lt;/code&gt;. Engines prune those prefixes, so a query for one day reads a tiny slice instead of the whole bucket.&lt;/p&gt;

&lt;p&gt;Keep file sizes sane. Object storage dislikes millions of tiny files: each is a separate GET with its own latency and listing overhead. Aim for files in the tens of megabytes, not kilobytes. If your producer emits small records, batch them before writing.&lt;/p&gt;

&lt;p&gt;Avoid deep nesting that no tool can prune, and keep a clear raw zone and a curated zone. Raw holds exactly what landed; curated holds the cleaned tables you actually query. The discipline pays off the first time you need to replay a day from raw.&lt;/p&gt;

&lt;p&gt;In other words, treat the key prefix as your primary index, because in object storage it is the only one you get for free.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which table formats sit on top of S3?
&lt;/h2&gt;

&lt;p&gt;On top of raw files, most teams add a table format so engines agree on schema, partitions, and snapshots. The three open options are Apache Iceberg, Delta Lake, and Apache Hudi. All three store Parquet data files plus JSON or AVRO metadata on object storage, and all three expose a table through the S3 API.&lt;/p&gt;

&lt;p&gt;Iceberg is the one I reach for first. Its metadata is designed for cloud object storage: snapshot isolation, hidden partitioning, and schema evolution that does not rewrite files. Delta Lake came out of the Spark world and stays tight with that ecosystem. Hudi targets incremental pipelines and record-level upserts.&lt;/p&gt;

&lt;p&gt;You can also skip a table format entirely and use Hive-style partitions with a catalog, which is plenty for append-only logs. The point is that the format is a layer on top of S3, not a replacement for it. Pick the engine-supported option and keep the files on S3 either way.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you query a data lake without moving it?
&lt;/h2&gt;

&lt;p&gt;This is the part that surprised me the first time: you can query Parquet straight off S3 with a single-node tool. DuckDB reads it through its httpfs extension. You register credentials with a secret, then point a query at an &lt;code&gt;s3://&lt;/code&gt; path.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- DuckDB S3 secret (sourced from DuckDB httpfs S3 API docs, NOT EXECUTED IN CI)&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="n"&gt;SECRET&lt;/span&gt; &lt;span class="n"&gt;lake&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;TYPE&lt;/span&gt; &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;PROVIDER&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;KEY_ID&lt;/span&gt; &lt;span class="s1"&gt;'rustfsadmin'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;SECRET&lt;/span&gt; &lt;span class="s1"&gt;'rustfsadmin'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;REGION&lt;/span&gt; &lt;span class="s1"&gt;'us-east-1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ENDPOINT&lt;/span&gt; &lt;span class="s1"&gt;'localhost:9000'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;USE_SSL&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;read_parquet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'s3://my-data-lake/raw/dt=2026-08-14/*.parquet'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;ENDPOINT&lt;/code&gt; and &lt;code&gt;USE_SSL&lt;/code&gt; lines are the S3 secret parameters DuckDB documents for non-AWS hosts; for AWS you omit them. For heavier work, Spark or Trino scan the same prefixes across many nodes, and Iceberg or Delta catalogs give them schema and snapshot info.&lt;/p&gt;

&lt;p&gt;The win is that the data stays put. You are not loading it into a warehouse first; you read the lake where it lives. That single property is what keeps a data lake cheap compared to a copy-everywhere architecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you stand up S3-compatible storage for a lake?
&lt;/h2&gt;

&lt;p&gt;Standing up your own S3-compatible endpoint is a handful of commands if you use RustFS. One Docker container gives you a bucket API that DuckDB, Spark, and the AWS CLI all understand. The official run command is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# sourced from RustFS GitHub README, NOT EXECUTED IN CI&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 9000:9000 &lt;span class="nt"&gt;-p&lt;/span&gt; 9001:9001 &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;/data:/data &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;/logs:/logs rustfs/rustfs:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Default credentials are &lt;code&gt;rustfsadmin&lt;/code&gt; / &lt;code&gt;rustfsadmin&lt;/code&gt;. Point the MinIO client at it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# sourced from MinIO mc docs, NOT EXECUTED IN CI&lt;/span&gt;
mc &lt;span class="nb"&gt;alias set &lt;/span&gt;rustfs http://localhost:9000 rustfsadmin rustfsadmin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then create a bucket and load data with the AWS CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# mc mb sourced from MinIO mc docs; aws s3 cp recursive form per AWS CLI docs, NOT EXECUTED IN CI&lt;/span&gt;
mc mb rustfs/my-data-lake
aws s3 &lt;span class="nb"&gt;cp&lt;/span&gt; ./events/ s3://my-data-lake/raw/ &lt;span class="nt"&gt;--recursive&lt;/span&gt; &lt;span class="nt"&gt;--endpoint-url&lt;/span&gt; http://localhost:9000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is a working lake: a bucket, partitioned data, and engines that can read it. Scale and durability are the parts you design next.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you keep a data lake safe?
&lt;/h2&gt;

&lt;p&gt;A data lake is only useful if you can recover it. Two S3 features earn their keep here, and both are worth turning on early.&lt;/p&gt;

&lt;p&gt;Versioning keeps every write as a new version instead of overwriting it. On AWS S3:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# sourced from AWS CLI docs, NOT EXECUTED IN CI&lt;/span&gt;
aws s3api put-bucket-versioning &lt;span class="nt"&gt;--bucket&lt;/span&gt; amzn-s3-demo-bucket &lt;span class="nt"&gt;--versioning-configuration&lt;/span&gt; &lt;span class="nv"&gt;Status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Enabled
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a self-hosted RustFS lake, versioning is an available feature, and bucket replication can mirror a bucket to a second site for disaster recovery. That replication path is available today.&lt;/p&gt;

&lt;p&gt;The catch is that versioning without lifecycle rules fills the bucket forever. On AWS you pair versioning with a lifecycle policy that expires old versions. On RustFS, lifecycle management is still under testing, so plan a manual or external cleanup job until that ships.&lt;/p&gt;

&lt;p&gt;In other words, protect the data first, then automate the cleanup. A lake you cannot restore is a liability, and a lake you never clean is a slowly growing bill.&lt;/p&gt;

&lt;h2&gt;
  
  
  What breaks when you self-host a data lake?
&lt;/h2&gt;

&lt;p&gt;Self-hosting a data lake trades cloud convenience for control, and the rough edges show up in predictable places.&lt;/p&gt;

&lt;p&gt;Small files are the first one. Object stores charge per request and list slowly, so a lake built from millions of tiny objects crawls no matter which engine you use. Batch before you write.&lt;/p&gt;

&lt;p&gt;Consistency matters for pipelines that list then read. AWS S3 has been strongly consistent since December 2020, but self-hosted S3-compatible stores vary. Test list-after-write in your setup before you trust it for orchestration.&lt;/p&gt;

&lt;p&gt;Egress is the quiet tax. If compute runs in a different region or account from the bucket, every scan leaves the network and shows up on the bill. Co-locate compute with storage to avoid it.&lt;/p&gt;

&lt;p&gt;Finally, durability is your job now. A single node is a single point of failure. RustFS distributed mode is still under testing, so for production durability today you either replicate to a second node or back the bucket up offsite. Know which one you are doing.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Do I need a warehouse like Snowflake to build a data lake?
&lt;/h3&gt;

&lt;p&gt;No. A data lake is just files on object storage plus a query engine. Snowflake, BigQuery, and friends are warehouses that copy data into their own format. You can query Parquet on S3 directly with DuckDB or Trino and skip the warehouse until you actually need one.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can DuckDB really query S3 directly?
&lt;/h3&gt;

&lt;p&gt;Yes. DuckDB's httpfs extension reads Parquet over the S3 API using a &lt;code&gt;CREATE SECRET&lt;/code&gt; for credentials and endpoint. It is single-node, so it suits interactive analysis and moderate scans, not petabyte parallel jobs. For those, use Spark or Trino against the same files.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is S3-compatible storage strongly consistent for a data lake?
&lt;/h3&gt;

&lt;p&gt;AWS S3 has been strongly consistent for all objects since December 2020, including reads after writes and list operations. Self-hosted S3-compatible stores do not all guarantee this, so verify list-after-write behavior before you build orchestration that depends on it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Which table format should I pick: Iceberg, Delta, or Hudi?
&lt;/h3&gt;

&lt;p&gt;Start with Apache Iceberg unless your stack is Spark-centric (then Delta) or you need record-level upserts from a streaming source (then Hudi). All three store Parquet plus metadata on S3 and are interoperable at the file level. The format is a layer on top of S3, not a lock-in.&lt;/p&gt;

&lt;h3&gt;
  
  
  How much does a self-hosted data lake cost versus AWS S3?
&lt;/h3&gt;

&lt;p&gt;AWS S3 Standard bills per GB-month plus per-request and egress charges, with the first 100 GB of internet egress free per month across services. Self-hosting replaces that with your own disk and bandwidth, which is cheaper at scale if compute is co-located. The real cost is operational: someone runs and patches the cluster. Price it as engineering time, not just hardware.&lt;/p&gt;

</description>
      <category>datalake</category>
      <category>s3</category>
      <category>objectstorage</category>
      <category>dataengineering</category>
    </item>
    <item>
      <title>Object Storage vs File Storage: When to Use Which (2026)</title>
      <dc:creator>Ethan Carter</dc:creator>
      <pubDate>Sun, 23 Aug 2026 13:45:00 +0000</pubDate>
      <link>https://dev.to/ethan-carter/object-storage-vs-file-storage-when-to-use-which-2026-mc6</link>
      <guid>https://dev.to/ethan-carter/object-storage-vs-file-storage-when-to-use-which-2026-mc6</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;I still see engineers storing user-uploaded photos in &lt;code&gt;/var/www/uploads/&lt;/code&gt; on an ext4 volume and wondering why their server falls over at 10M files.&lt;/strong&gt; Meanwhile, the team next door threw the same photos into an S3 bucket and scaled to 100M files without breaking a sweat. The difference is the storage paradigm. Pick the wrong one and you feel it at scale.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I still mix the two up in conversation sometimes, so I keep a short checklist: random writes and file locks mean file storage; HTTP PUTs and billions of objects mean object storage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Short answer:&lt;/strong&gt; use file storage when you need POSIX semantics — in-place edits, sub-millisecond random I/O, file locking (databases, OS files, NFS shares). Use object storage when you need scale, an HTTP API and rich metadata (user uploads, data lakes, backups, ML datasets). Most mature stacks run both, side by side.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Stats
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Fact&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Amazon S3 consistency&lt;/td&gt;
&lt;td&gt;Strong read-after-write for new objects, &lt;strong&gt;overwrites and LIST&lt;/strong&gt; — since Dec 1, 2020, at no extra cost&lt;/td&gt;
&lt;td&gt;&lt;a href="https://aws.amazon.com/about-aws/whats-new/2020/12/amazon-s3-now-delivers-strong-read-after-write-consistency-automatically-for-all-applications/" rel="noopener noreferrer"&gt;AWS What's New&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;s3fs-fuse random writes&lt;/td&gt;
&lt;td&gt;"random writes or appends to files require rewriting the entire object"&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/s3fs-fuse/s3fs-fuse" rel="noopener noreferrer"&gt;s3fs-fuse README → Limitations&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;goofys write support&lt;/td&gt;
&lt;td&gt;"only sequential writes supported"; no symlinks/hardlinks; cannot rename directories with &amp;gt;1000 children&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/kahing/goofys" rel="noopener noreferrer"&gt;goofys README → Current Status&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mountpoint for Amazon S3&lt;/td&gt;
&lt;td&gt;Does "not implement all the features of a POSIX file system" — no directory renaming, no symlinks, no edits to existing files&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/awslabs/mountpoint-s3" rel="noopener noreferrer"&gt;awslabs/mountpoint-s3&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RustFS license&lt;/td&gt;
&lt;td&gt;Apache 2.0 (no AGPL restrictions)&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/rustfs/rustfs" rel="noopener noreferrer"&gt;rustfs/rustfs README&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RustFS FUSE mount&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Not offered.&lt;/strong&gt; The README's Feature &amp;amp; Status table lists no FUSE / POSIX mount driver — use a third-party S3 FUSE client against its S3 endpoint&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/rustfs/rustfs" rel="noopener noreferrer"&gt;rustfs/rustfs README&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What Is the Fundamental Difference Between Object and File Storage?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;File Storage&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Object Storage&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data unit&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;File (named byte sequence)&lt;/td&gt;
&lt;td&gt;Object (data + metadata + key)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Organization&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Hierarchical (directories/subdirectories)&lt;/td&gt;
&lt;td&gt;Flat (key namespace; &lt;code&gt;/&lt;/code&gt; is cosmetic)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Access method&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;POSIX (open/read/write/seek/close)&lt;/td&gt;
&lt;td&gt;HTTP REST API (PUT/GET/DELETE)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Mutability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;In-place (change bytes 100-200 without touching 1-99)&lt;/td&gt;
&lt;td&gt;Immutable (overwrite = new version/new object)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Metadata&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fixed attributes (name, size, permissions, timestamps)&lt;/td&gt;
&lt;td&gt;Rich &amp;amp; extensible (custom key-value tags, content-type, etc.)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scaling limit&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Millions of files (inode exhaustion, metadata perf)&lt;/td&gt;
&lt;td&gt;Billions+ of objects (distributed metadata)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Protocol&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;NFS, SMB, POSIX local (ext4, xfs, zfs)&lt;/td&gt;
&lt;td&gt;S3 API (HTTP/HTTPS)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Consistency model&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Strong (reads see writes immediately)&lt;/td&gt;
&lt;td&gt;Strong read-after-write on AWS S3 since Dec 2020 — covers new objects, overwrites &lt;em&gt;and&lt;/em&gt; LIST; S3-compatible systems vary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Typical latency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Sub-millisecond (local) to milliseconds (NFS)&lt;/td&gt;
&lt;td&gt;Milliseconds (network round-trip)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Best for&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;OS-level operations, databases, home dirs&lt;/td&gt;
&lt;td&gt;Unstructured data at scale, web/mobile apps, analytics&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  When Should You Use File Storage?
&lt;/h2&gt;

&lt;p&gt;File storage is the right choice when your application (or OS) needs &lt;strong&gt;POSIX semantics&lt;/strong&gt;:&lt;/p&gt;

&lt;h3&gt;
  
  
  Use Case 1: Operating System Files
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;/&lt;span class="n"&gt;etc&lt;/span&gt;/&lt;span class="n"&gt;hosts&lt;/span&gt;
/&lt;span class="n"&gt;var&lt;/span&gt;/&lt;span class="n"&gt;log&lt;/span&gt;/&lt;span class="n"&gt;syslog&lt;/span&gt;
/&lt;span class="n"&gt;home&lt;/span&gt;/&lt;span class="n"&gt;user&lt;/span&gt;/.&lt;span class="n"&gt;bashrc&lt;/span&gt;
/&lt;span class="n"&gt;tmp&lt;/span&gt;/&lt;span class="n"&gt;processing_12345&lt;/span&gt;.&lt;span class="n"&gt;tmp&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your OS expects file storage. It uses &lt;code&gt;open()&lt;/code&gt;, &lt;code&gt;read()&lt;/code&gt;, &lt;code&gt;write()&lt;/code&gt;, &lt;code&gt;seek()&lt;/code&gt; — not HTTP PUT/GET. Don't fight this.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use Case 2: Databases
&lt;/h3&gt;

&lt;p&gt;PostgreSQL, MySQL, MongoDB, SQLite — they all expect block devices or file systems with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sub-millisecond random I/O&lt;/strong&gt; (index lookups, page reads)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;In-place mutation&lt;/strong&gt; (UPDATE SET field = value changes specific bytes)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Strong consistency&lt;/strong&gt; (ACID transactions depend on ordered fsync)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;File locking&lt;/strong&gt; (&lt;code&gt;.lock&lt;/code&gt; files, advisory locks)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Object storage has millisecond-level latency and no in-place mutation. Databases on S3 perform terribly (with niche exceptions like Iceberg/Delta lakehouse patterns).&lt;/p&gt;

&lt;h3&gt;
  
  
  Use Case 3: Network File Sharing (NFS/SMB)
&lt;/h3&gt;

&lt;p&gt;When multiple users/servers need shared access to the same files with familiar tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Design teams sharing Figma/Adobe files via SMB mount&lt;/li&gt;
&lt;li&gt;Build servers sharing source code via NFS&lt;/li&gt;
&lt;li&gt;Home directories in enterprise environments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These use cases need &lt;strong&gt;file-level permissions, directory browsing, and application transparency&lt;/strong&gt; — all strengths of file storage.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use Case 4: Small-Scale Applications (&amp;lt; 100K files, &amp;lt; 1TB)
&lt;/h3&gt;

&lt;p&gt;For small datasets, file storage is simpler:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No API to learn&lt;/li&gt;
&lt;li&gt;Familiar tools (&lt;code&gt;ls&lt;/code&gt;, &lt;code&gt;cp&lt;/code&gt;, &lt;code&gt;rsync&lt;/code&gt;, &lt;code&gt;grep&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Easy backups (tar, rsync)&lt;/li&gt;
&lt;li&gt;Local access = fastest possible&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When Should You Use Object Storage?
&lt;/h2&gt;

&lt;p&gt;Object storage is the right choice when you need &lt;strong&gt;scale, simplicity of API, and rich metadata:&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Use Case 1: User-Generated Content
&lt;/h3&gt;

&lt;p&gt;Photos, videos, documents, uploads — the canonical object storage workload:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# User uploads photo
&lt;/span&gt;&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;Bucket&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user-photos&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/photo-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.jpg&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;image_data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ContentType&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;image/jpeg&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Metadata&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;uploader&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;camera&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;iphone&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Scale from 1K to 100M objects without changing code. I have watched teams try to stretch a filesystem to that size; inode exhaustion is not a fun afternoon.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use Case 2: Data Lake / Analytics
&lt;/h3&gt;

&lt;p&gt;Parquet/Avro/CSV files for Spark, Trino, DuckDB:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;s3://data-lake/bronze/events/year=2026/month=07/day=24/event-*.parquet
s3://data-lake/gold/daily_active_users.parquet
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Flat namespace, massive scale, accessed by query engines that speak S3 natively. This is where object storage dominates in 2026.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use Case 3: Backup &amp;amp; Archive
&lt;/h3&gt;

&lt;p&gt;Database dumps, VM snapshots, compliance records:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Immutable&lt;/strong&gt; (versioning = accidental deletion protection)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tiered&lt;/strong&gt; (lifecycle policies move old data to cheap storage automatically)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Replicated&lt;/strong&gt; (cross-region for DR)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compliant&lt;/strong&gt; (Object Lock for WORM retention)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;File storage can do backups too, but at scale, object storage's tiering and replication features save significant cost and operational effort.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use Case 4: Static Website / CDN Origin
&lt;/h3&gt;

&lt;p&gt;S3 + CloudFront (or Cloudflare) is the standard pattern for serving static web content:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Objects = web assets (HTML, CSS, JS, images)&lt;/li&gt;
&lt;li&gt;Global CDN = fast delivery everywhere&lt;/li&gt;
&lt;li&gt;HTTPS + custom domain = zero-infrastructure frontend&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Serving a global static site from an NFS mount is not something I'd want to run on-call for.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use Case 5: Machine Learning &amp;amp; AI
&lt;/h3&gt;

&lt;p&gt;Training data, model checkpoints, inference outputs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Checkpointing:&lt;/strong&gt; Write model state as object → resume from any saved point&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dataset versioning:&lt;/strong&gt; Each dataset version = immutable object (reproducible training)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Feature stores:&lt;/strong&gt; Parquet objects queried by ML frameworks via S3 API&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ML workloads at scale (terabytes of training data) are almost always object-storage-backed in 2026.&lt;/p&gt;

&lt;h2&gt;
  
  
  Can You Mount S3 as a Filesystem?
&lt;/h2&gt;

&lt;p&gt;What if you want S3's scale but need file-system semantics? I have been asked this in almost every object-storage migration. The honest answer is: you can, but the mount layer will lie to you in small ways. Here is what each project officially documents:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Language&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;th&gt;Officially documented limits&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;a href="https://github.com/s3fs-fuse/s3fs-fuse" rel="noopener noreferrer"&gt;s3fs-fuse&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;C++&lt;/td&gt;
&lt;td&gt;Mounts an S3 bucket via FUSE on Linux/macOS/FreeBSD; preserves the native object format so &lt;code&gt;aws s3&lt;/code&gt; still works&lt;/td&gt;
&lt;td&gt;"random writes or appends to files require rewriting the entire object"; "no atomic renames of files or directories"; "no hard links"; "no coordination between multiple clients mounting the same bucket"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;a href="https://github.com/kahing/goofys" rel="noopener noreferrer"&gt;goofys&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Go&lt;/td&gt;
&lt;td&gt;A "Filey System" that "strives for performance first and POSIX second"; close-to-open consistency, no on-disk cache&lt;/td&gt;
&lt;td&gt;"only sequential writes supported"; "does not support symlink or hardlink"; "cannot &lt;code&gt;rename&lt;/code&gt; directories with more than 1000 children"; "&lt;code&gt;fsync&lt;/code&gt; is ignored" — and the last commit was &lt;strong&gt;June 2023&lt;/strong&gt;, so treat it as low-maintenance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;a href="https://github.com/awslabs/mountpoint-s3" rel="noopener noreferrer"&gt;Mountpoint for Amazon S3&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Rust&lt;/td&gt;
&lt;td&gt;AWS's own GA file client, tuned for high read throughput and sequential writes of new objects&lt;/td&gt;
&lt;td&gt;AWS states it is "probably not the right fit" for apps that use "directory renaming or symlinks" or "make edits to existing files (don't work on your Git repository or run &lt;code&gt;vim&lt;/code&gt; in Mountpoint)"; support for non-AWS S3-compatible stores is limited&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;a href="https://rclone.org/commands/rclone_mount/" rel="noopener noreferrer"&gt;rclone mount&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Go&lt;/td&gt;
&lt;td&gt;Mounts any of rclone's 70+ backends, including any S3-compatible endpoint&lt;/td&gt;
&lt;td&gt;rclone's own docs warn the file system is not fully POSIX-compliant; behaviour depends on VFS cache mode&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Own it first:&lt;/strong&gt; RustFS does &lt;strong&gt;not&lt;/strong&gt; ship a FUSE driver. Its README Feature &amp;amp; Status table covers S3 core, versioning, bucket replication, event notifications, bitrot protection, Swift/Keystone and Helm charts — no POSIX mount. If you want a mount, point one of the clients above at RustFS's S3 endpoint like you would at any other S3 service. Anyone telling you a "native RustFS mount" exists is reading a spec sheet that doesn't.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Performance reality:&lt;/strong&gt; the translation layer is POSIX → HTTP, so each metadata operation becomes a network round trip. s3fs-fuse names this explicitly: "metadata operations such as listing directories have poor performance due to network latency." That's fine for bulk work — &lt;code&gt;cp&lt;/code&gt;, &lt;code&gt;tar&lt;/code&gt;, &lt;code&gt;grep&lt;/code&gt;, feeding a training job. It is &lt;strong&gt;not&lt;/strong&gt; fine for databases, build systems or anything doing high-IOPS random writes, because those turn into whole-object rewrites.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which Should You Pick? A Decision Flowchart
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Do you need sub-millisecond random I/O?
├─ YES → File Storage (database, OS files)
│         (or block storage)
│
└─ NO → Do you need POSIX semantics (ls, chmod, flock)?
    ├─ YES → File Storage (NFS/SMB shares, source code)
    │
    └─ NO → Will you exceed 1M files/objects?
       ├─ YES → Object Storage (S3/S3-compatible)
       │         (photos, data lake, backups, ML)
       │
       └─ NO → Either works; pick the simpler tool
                 for your team's skill set
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;File storage = hierarchical, mutable, POSIX, scales to millions.&lt;/strong&gt; Use it for OS files, databases, NFS shares, small datasets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Object storage = flat, immutable, HTTP API, scales to billions.&lt;/strong&gt; Use it for user content, data lakes, backups/archives, static sites, ML data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The crossover point is usually scale.&lt;/strong&gt; Under 100K files/1TB: file storage is simpler. Over that: object storage wins on operational cost.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Databases always want file/block storage&lt;/strong&gt; (not object). ML/analytics always want object storage (not file).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;S3 FUSE clients (s3fs-fuse, goofys, AWS Mountpoint, &lt;code&gt;rclone mount&lt;/code&gt;)&lt;/strong&gt; give you file-system access to S3 data — every one of them documents real POSIX gaps (no atomic renames, sequential-writes-only, no edits to existing files). Fine for bulk I/O, wrong for databases. RustFS itself ships no FUSE driver; use one of these against its S3 endpoint.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You can use both.&lt;/strong&gt; Most mature infrastructures I have worked with run file storage and object storage side-by-side. The cleanest architecture is usually "file storage for the OS and databases, object storage for everything else" rather than forcing one paradigm to cover both.&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;&lt;em&gt;Need S3-compatible object storage you can run yourself? &lt;a href="https://rustfs.com" rel="noopener noreferrer"&gt;RustFS&lt;/a&gt; is Apache 2.0 licensed (no AGPL strings) and its README lists S3 core, versioning, bucket replication, event notifications, bitrot protection, multi-tenancy and Helm charts as Available; Lifecycle Management, Distributed Mode and KMS are still marked Under Testing — so plan accordingly. Try it in one command:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 9000:9000 &lt;span class="nt"&gt;-p&lt;/span&gt; 9001:9001 &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;/data:/data &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;/logs:/logs rustfs/rustfs:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;[sourced verbatim from the &lt;a href="https://github.com/rustfs/rustfs" rel="noopener noreferrer"&gt;RustFS GitHub README&lt;/a&gt; — NOT EXECUTED IN CI]. Console on port 9001, default credentials &lt;code&gt;rustfsadmin&lt;/code&gt; / &lt;code&gt;rustfsadmin&lt;/code&gt; — change them before you expose anything. Binaries and the &lt;code&gt;rc&lt;/code&gt; CLI: &lt;a href="https://rustfs.com/download" rel="noopener noreferrer"&gt;rustfs.com/download&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Can I replace NFS with S3/object storage?
&lt;/h3&gt;

&lt;p&gt;Sometimes. For plain file sharing I usually skip the FUSE shim and serve objects through a web UI or pre-signed URLs — one less POSIX lie to debug. If you really need a mount, read the limits first: s3fs-fuse has "no atomic renames of files or directories" and "no coordination between multiple clients mounting the same bucket"; goofys supports "only sequential writes"; Mountpoint refuses edits to existing files. Compilers, build systems, anything calling &lt;code&gt;flock()&lt;/code&gt; — those stay on real file storage.&lt;/p&gt;

&lt;h3&gt;
  
  
  Which is faster, object storage or file storage?
&lt;/h3&gt;

&lt;p&gt;It depends where the reader is sitting. A local NVMe filesystem wins for a single machine; object storage wins when you need a CDN in front of it. The question I ask is not "which is faster" but "which is fast enough at this distance". Databases need the local path. A photo served worldwide needs the CDN path. I ignore quoted millisecond figures unless they come with the test setup attached.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can databases run on object storage?
&lt;/h3&gt;

&lt;p&gt;Traditional OLTP databases — PostgreSQL, MySQL — no, not as their primary data directory. They need in-place mutation, ordered &lt;code&gt;fsync&lt;/code&gt; and sub-millisecond random reads, none of which object storage provides. What does work, and works extremely well, is the lakehouse pattern: query engines such as DuckDB (via &lt;code&gt;httpfs&lt;/code&gt;), Trino, Spark and ClickHouse (S3 table engine) read Parquet/ORC directly out of S3, and table formats like Apache Iceberg and Delta Lake add ACID semantics on top of immutable objects. Object storage is also the universal &lt;em&gt;backup target&lt;/em&gt; for databases. So the accurate statement is: analytics on object storage, yes; transactional storage engine on object storage, no.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I migrate from file storage to object storage?
&lt;/h3&gt;

&lt;p&gt;Gradually, and by workload rather than by directory. A path that works: (1) point all &lt;em&gt;new&lt;/em&gt; workloads at S3 from day one; (2) move user-generated content first — uploads are the natural fit; (3) move analytics data next, as Parquet in a bucket queried by Spark/Trino/DuckDB; (4) leave the legacy file server on NFS/SMB and mirror it to object storage for DR and archive; (5) never move OS files. &lt;code&gt;rclone sync&lt;/code&gt; handles filesystem-to-S3 copies against any S3-compatible endpoint, and &lt;code&gt;aws s3 sync&lt;/code&gt; works for AWS. Budget for a coexistence period — both paradigms running side by side is the normal end state, not a failure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does RustFS provide a POSIX or FUSE mount?
&lt;/h3&gt;

&lt;p&gt;No. As of the check date on this article, the RustFS GitHub README's Feature &amp;amp; Status table lists S3 Core Features, Upload/Download, Versioning, Logging, Event Notifications, K8s Helm Charts, Keystone Auth, Swift API, Bitrot Protection, Single Node Mode, Bucket Replication and Multi-Tenancy as Available, with Lifecycle Management, Distributed Mode and RustFS KMS marked Under Testing. There is no FUSE driver, no &lt;code&gt;rustfs mount&lt;/code&gt; command and no POSIX mount feature anywhere in the README or on docs.rustfs.com. If you need a mount, run s3fs-fuse, goofys or &lt;code&gt;rclone mount&lt;/code&gt; against the RustFS S3 endpoint on port 9000 — exactly as you would against any other S3-compatible service.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;p&gt;All claims above were checked against primary sources on &lt;strong&gt;2026-08-07&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Amazon S3 strong read-after-write consistency (new objects, overwrites, LIST; Dec 1, 2020) — &lt;a href="https://aws.amazon.com/about-aws/whats-new/2020/12/amazon-s3-now-delivers-strong-read-after-write-consistency-automatically-for-all-applications/" rel="noopener noreferrer"&gt;https://aws.amazon.com/about-aws/whats-new/2020/12/amazon-s3-now-delivers-strong-read-after-write-consistency-automatically-for-all-applications/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;s3fs-fuse "Limitations" section (random writes rewrite the whole object, no atomic renames, no hard links, no multi-client coordination) — &lt;a href="https://github.com/s3fs-fuse/s3fs-fuse" rel="noopener noreferrer"&gt;https://github.com/s3fs-fuse/s3fs-fuse&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;goofys "Current Status" non-POSIX behaviours (sequential writes only, no symlink/hardlink, 1000-child rename cap, &lt;code&gt;fsync&lt;/code&gt; ignored); last commit June 2023 — &lt;a href="https://github.com/kahing/goofys" rel="noopener noreferrer"&gt;https://github.com/kahing/goofys&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Mountpoint for Amazon S3 POSIX caveats (no directory renaming, no symlinks, no edits to existing files) — &lt;a href="https://github.com/awslabs/mountpoint-s3" rel="noopener noreferrer"&gt;https://github.com/awslabs/mountpoint-s3&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;rclone mount&lt;/code&gt; documentation — &lt;a href="https://rclone.org/commands/rclone_mount/" rel="noopener noreferrer"&gt;https://rclone.org/commands/rclone_mount/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;RustFS license, Feature &amp;amp; Status table, quickstart command and default credentials — &lt;a href="https://github.com/rustfs/rustfs" rel="noopener noreferrer"&gt;https://github.com/rustfs/rustfs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;RustFS installation documentation (no FUSE/POSIX mount path listed) — &lt;a href="https://docs.rustfs.com/" rel="noopener noreferrer"&gt;https://docs.rustfs.com/&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>objectstorage</category>
      <category>filestorage</category>
      <category>s3</category>
      <category>nfs</category>
    </item>
    <item>
      <title>Using S3 as Your Disaster Recovery Target: A Practical Guide</title>
      <dc:creator>Ethan Carter</dc:creator>
      <pubDate>Fri, 21 Aug 2026 13:40:00 +0000</pubDate>
      <link>https://dev.to/ethan-carter/using-s3-as-your-disaster-recovery-target-a-practical-guide-3f0b</link>
      <guid>https://dev.to/ethan-carter/using-s3-as-your-disaster-recovery-target-a-practical-guide-3f0b</guid>
      <description>&lt;h1&gt;
  
  
  Using S3 as Your Disaster Recovery Target: A Practical Guide
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;S3 works as a disaster-recovery target because your data is already there, it is redundant by design, and cross-region replication is a configuration change rather than a new pipeline.&lt;/strong&gt; The case for bothering is in the outage economics: Uptime Institute's 2022 Outage Analysis found that &lt;strong&gt;over 60% of failures now cost at least $100,000&lt;/strong&gt; — up from 39% in 2019 — and the share costing more than $1 million climbed from 11% to 15%. Yet when I audit DR plans, S3 is consistently the most underused asset in the room.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Key Stats
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Figure&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Failures costing ≥ $100,000&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;60%+&lt;/strong&gt; (was 39% in 2019)&lt;/td&gt;
&lt;td&gt;Uptime Institute, 2022 Outage Analysis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Failures costing ≥ $1M&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;15%&lt;/strong&gt; (was 11%)&lt;/td&gt;
&lt;td&gt;Uptime Institute, 2022 Outage Analysis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Orgs hit by a serious/severe outage in 3 years&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1 in 5&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Uptime Institute, 2022 Outage Analysis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Public outages lasting &amp;gt; 24 hours (2021)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;~30%&lt;/strong&gt; (was 8% in 2017)&lt;/td&gt;
&lt;td&gt;Uptime Institute, 2022 Outage Analysis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Major outages caused by human error&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;~40%&lt;/strong&gt; of orgs; 85% trace to procedure failures&lt;/td&gt;
&lt;td&gt;Uptime Institute, 2022 Outage Analysis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AWS S3 designed durability&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;99.999999999%&lt;/strong&gt; (11 nines)&lt;/td&gt;
&lt;td&gt;AWS S3 official documentation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That last row on human error is the one worth sitting with: the failure mode is rarely "the storage broke." It is that nobody had run the restore.&lt;/p&gt;

&lt;p&gt;I have audited enough DR plans to notice a pattern: the teams that survive outages are not the ones with the most elegant architecture; they are the ones that actually tested a restore. S3-compatible storage is just the cheapest place to start that habit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why does S3 work well as a DR target?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  It's Already There
&lt;/h3&gt;

&lt;p&gt;Most applications I look at already write something to S3, even if the team does not think of it as primary storage:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User-uploaded files (photos, documents, media)&lt;/li&gt;
&lt;li&gt;Log files and audit trails&lt;/li&gt;
&lt;li&gt;Database backups (mysqldump, pg_dump, volume snapshots)&lt;/li&gt;
&lt;li&gt;ML model artifacts and datasets&lt;/li&gt;
&lt;li&gt;Configuration backups&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;If this data is already in S3, making it DR-capable is mostly a configuration change, not a new pipeline.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  It's Durable by Design
&lt;/h3&gt;

&lt;p&gt;S3 (and serious S3-compatible implementations) stores data redundantly:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Implementation&lt;/th&gt;
&lt;th&gt;Stated Durability&lt;/th&gt;
&lt;th&gt;Mechanism&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;AWS S3&lt;/td&gt;
&lt;td&gt;99.999999999% (11 nines), &lt;em&gt;designed for&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;Redundant storage across multiple devices in ≥3 Availability Zones&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backblaze B2&lt;/td&gt;
&lt;td&gt;99.999999999% (11 nines), annual&lt;/td&gt;
&lt;td&gt;Reed-Solomon erasure coding&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://rustfs.com" rel="noopener noreferrer"&gt;RustFS&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;No published nines figure&lt;/td&gt;
&lt;td&gt;README lists &lt;strong&gt;Bitrot Protection&lt;/strong&gt; and &lt;strong&gt;Bucket Replication&lt;/strong&gt; as ✅ Available; &lt;strong&gt;Distributed Mode&lt;/strong&gt; is 🚧 Under Testing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MinIO&lt;/td&gt;
&lt;td&gt;No published nines figure&lt;/td&gt;
&lt;td&gt;Erasure coding or replication, configurable&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;I am deliberately not inventing a durability number for RustFS or MinIO. Neither project publishes an audited nines figure, and a self-hosted cluster's real durability depends on your disk count, erasure set width and failure domains — not on the vendor's marketing page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Single-disk failure = zero data loss.&lt;/strong&gt; Single-node failure (in clustered setups) = zero data loss. This is better than most on-premises databases achieve out of the box.&lt;/p&gt;

&lt;h3&gt;
  
  
  It's Cheap (Relative to Alternatives)
&lt;/h3&gt;

&lt;p&gt;DR is insurance — nobody wants to overpay for it. Concretely: AWS S3 Standard list price is &lt;strong&gt;$0.023/GB-month&lt;/strong&gt;, so parking 10 TB of DR backups costs about &lt;strong&gt;$236/month&lt;/strong&gt;. A warm-standby database instance sized for the same workload, plus its attached block storage, generally lands in four figures a month. I am not going to quote you a tidy multiple, because the ratio swings hard with instance class, retention and egress — run the numbers for your own shape.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which DR architecture pattern should you use?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Pattern 1: Backup-to-S3 (What I recommend first)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Production DB ──[daily dump]──▶ S3 Bucket (primary region)
                                    │
                              Cross-region replicate
                                    │
                                    ▶
                              S3 Bucket (DR region)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Nightly database dump → compressed file → PUT to S3&lt;/li&gt;
&lt;li&gt;S3 Cross-Region Replication (CRR) copies to DR region&lt;/li&gt;
&lt;li&gt;If primary region fails: spin up DB in DR region → restore from S3&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;RTO (Recovery Time Objective):&lt;/strong&gt; 1-4 hours (depends on DB size + restore speed)&lt;br&gt;
&lt;strong&gt;RPO (Recovery Point Objective):&lt;/strong&gt; Up to 24 hours (backup frequency)&lt;/p&gt;

&lt;p&gt;This is not exciting architecture, but it is the pattern that saves most teams. Get backups into a second region before you worry about streaming replication.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tools:&lt;/strong&gt; &lt;code&gt;aws s3 sync&lt;/code&gt;, rclone, database-native S3 backup tools (pg_backrest, mysqldump + pipe)&lt;/p&gt;
&lt;h3&gt;
  
  
  Pattern 2: Continuous Log Shipping (Better RPO)
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Production DB WAL/Binlog ──[stream]──▶ S3 (primary)
                                         │
                                   CRR / Custom forwarder
                                         │
                                         ▶
                                   S3 (DR region)
                                         │
                                   [Continuous restore]
                                         ▶
                                   Standby DB (DR region)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Write-Ahead Logs (PostgreSQL) or Binlogs (MySQL) stream to S3 continuously&lt;/li&gt;
&lt;li&gt;Standby DB in DR region applies logs in near-real-time&lt;/li&gt;
&lt;li&gt;If primary fails: promote standby (seconds to minutes of RPO)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;RTO:&lt;/strong&gt; Minutes (standby is already running, just needs promotion)&lt;br&gt;
&lt;strong&gt;RPO:&lt;/strong&gt; Seconds to minutes (depends on log shipping lag)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tools:&lt;/strong&gt; WAL-G (PostgreSQL), pgBackRest, MySQL binlog-to-s3 tools, Debezium (CDC)&lt;/p&gt;
&lt;h3&gt;
  
  
  Pattern 3: Active-Active with S3 as Source of Truth (Avoid unless you need it)
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Region A App ◄──── S3 (shared, replicated) ───► Region B App
                    │
              [Both regions read/write to same S3]
              [App-level conflict resolution required]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Both regions' applications read/write to the same S3 bucket(s)&lt;/li&gt;
&lt;li&gt;Conflict resolution is your application's job — object storage does not merge concurrent writes for you&lt;/li&gt;
&lt;li&gt;If Region A fails: Region B continues serving with zero RPO&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;RTO:&lt;/strong&gt; Zero (automatic)&lt;br&gt;
&lt;strong&gt;RPO:&lt;/strong&gt; Zero (both regions always current)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Complexity:&lt;/strong&gt; High. I have seen this pattern look simple on a whiteboard and turn into weeks of conflict-resolution bugs in production. Use it only when zero RPO is a hard business requirement, not because it sounds modern.&lt;/p&gt;
&lt;h2&gt;
  
  
  Implementing Pattern 1: The Minimal Viable DR Plan
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Step 1: Identify Critical Data
&lt;/h3&gt;

&lt;p&gt;Not all data needs DR protection. Classify yours:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tier&lt;/th&gt;
&lt;th&gt;Data Type&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;th&gt;DR Requirement&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;T0 (Critical)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;User data, financial transactions&lt;/td&gt;
&lt;td&gt;User uploads, payment records&lt;/td&gt;
&lt;td&gt;RPO &amp;lt; 1hr, RTO &amp;lt; 1hr&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;T1 (Important)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Business operational data&lt;/td&gt;
&lt;td&gt;Logs, configs, ML models&lt;/td&gt;
&lt;td&gt;RPO &amp;lt; 24hr, RTO &amp;lt; 4hr&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;T2 (Useful)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Analytics, historical&lt;/td&gt;
&lt;td&gt;Aggregated data, old backups&lt;/td&gt;
&lt;td&gt;RPO &amp;lt; 7 days, RTO &amp;lt; 24hr&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;T3 (Disposable)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Cache, temp files&lt;/td&gt;
&lt;td&gt;Session stores, build artifacts&lt;/td&gt;
&lt;td&gt;Best effort / none&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Focus DR effort on T0 and T1. T2 and T3 are nice-to-have.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 2: Set Up Automated Backups to S3
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# dr-backup.sh — Daily backup to S3&lt;/span&gt;
&lt;span class="nv"&gt;DATE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%Y-%m-%d&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;BUCKET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"dr-backups-primary"&lt;/span&gt;

&lt;span class="c"&gt;# PostgreSQL backup&lt;/span&gt;
pg_dump &lt;span class="nt"&gt;-Fc&lt;/span&gt; production_db | &lt;span class="nb"&gt;gzip&lt;/span&gt; | aws s3 &lt;span class="nb"&gt;cp&lt;/span&gt; - &lt;span class="s2"&gt;"s3://&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET&lt;/span&gt;&lt;span class="s2"&gt;/postgres/&lt;/span&gt;&lt;span class="nv"&gt;$DATE&lt;/span&gt;&lt;span class="s2"&gt;/db.dump.gz"&lt;/span&gt;

&lt;span class="c"&gt;# Application data sync&lt;/span&gt;
aws s3 &lt;span class="nb"&gt;sync&lt;/span&gt; /data/app-uploads &lt;span class="s2"&gt;"s3://&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET&lt;/span&gt;&lt;span class="s2"&gt;/uploads/&lt;/span&gt;&lt;span class="nv"&gt;$DATE&lt;/span&gt;&lt;span class="s2"&gt;/"&lt;/span&gt; &lt;span class="nt"&gt;--delete&lt;/span&gt;

&lt;span class="c"&gt;# Config backup&lt;/span&gt;
&lt;span class="nb"&gt;tar &lt;/span&gt;czf - /etc/myapp/config | aws s3 &lt;span class="nb"&gt;cp&lt;/span&gt; - &lt;span class="s2"&gt;"s3://&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET&lt;/span&gt;&lt;span class="s2"&gt;/config/&lt;/span&gt;&lt;span class="nv"&gt;$DATE&lt;/span&gt;&lt;span class="s2"&gt;/config.tar.gz"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;] DR backup complete"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /var/log/dr-backup.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Schedule via cron (every 6 hours for T0, daily for T1):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;0 &lt;span class="k"&gt;*&lt;/span&gt;/6 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; /opt/scripts/dr-backup.sh    &lt;span class="c"&gt;# T0: Every 6 hours&lt;/span&gt;
0 2 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; /opt/scripts/dr-backup-full.sh &lt;span class="c"&gt;# T1: Daily at 2 AM&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Configure Cross-Region Replication
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;On AWS.&lt;/strong&gt; Replication requires versioning on &lt;em&gt;both&lt;/em&gt; buckets, and &lt;code&gt;Role&lt;/code&gt; is a &lt;strong&gt;required&lt;/strong&gt; top-level field in the replication configuration — a lot of copy-pasted snippets omit it and fail with &lt;code&gt;InvalidRequest&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# [sourced from https://docs.aws.amazon.com/cli/latest/reference/s3api/put-bucket-replication.html, NOT EXECUTED IN CI]&lt;/span&gt;

&lt;span class="c"&gt;# Prerequisite: versioning on BOTH source and destination&lt;/span&gt;
aws s3api put-bucket-versioning &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--bucket&lt;/span&gt; dr-backups-primary &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--versioning-configuration&lt;/span&gt; &lt;span class="nv"&gt;Status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Enabled

aws s3api put-bucket-versioning &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--bucket&lt;/span&gt; dr-backups-dr &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--versioning-configuration&lt;/span&gt; &lt;span class="nv"&gt;Status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Enabled

&lt;span class="c"&gt;# CRR rule ("Role" is REQUIRED — the IAM role S3 assumes to replicate)&lt;/span&gt;
aws s3api put-bucket-replication &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--bucket&lt;/span&gt; dr-backups-primary &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--replication-configuration&lt;/span&gt; &lt;span class="s1"&gt;'{
    "Role": "arn:aws:iam::123456789012:role/s3-replication-role",
    "Rules": [{
      "Status": "Enabled",
      "Priority": 1,
      "DeleteMarkerReplication": { "Status": "Enabled" },
      "Filter": { "Prefix": "" },
      "Destination": {
        "Bucket": "arn:aws:s3:::dr-backups-dr",
        "StorageClass": "STANDARD"
      }
    }]
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;On self-hosted S3.&lt;/strong&gt; RustFS lists &lt;strong&gt;Bucket Replication&lt;/strong&gt; as ✅ Available in its README Feature &amp;amp; Status table. If you want a scheduled, engine-agnostic copy between two independent clusters — which is what most self-hosted DR setups actually run — &lt;code&gt;rclone&lt;/code&gt; is the pragmatic tool. Define two named S3 remotes, then sync:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="c"&gt;# ~/.config/rclone/rclone.conf
# [sourced from https://rclone.org/s3/, NOT EXECUTED IN CI]
&lt;/span&gt;&lt;span class="nn"&gt;[primary]&lt;/span&gt;
&lt;span class="py"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;s3&lt;/span&gt;
&lt;span class="py"&gt;provider&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;Other&lt;/span&gt;
&lt;span class="py"&gt;access_key_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;rustfsadmin&lt;/span&gt;
&lt;span class="py"&gt;secret_access_key&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;rustfsadmin&lt;/span&gt;
&lt;span class="py"&gt;endpoint&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;http://primary-site:9000&lt;/span&gt;

&lt;span class="nn"&gt;[dr]&lt;/span&gt;
&lt;span class="py"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;s3&lt;/span&gt;
&lt;span class="py"&gt;provider&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;Other&lt;/span&gt;
&lt;span class="py"&gt;access_key_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;rustfsadmin&lt;/span&gt;
&lt;span class="py"&gt;secret_access_key&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;rustfsadmin&lt;/span&gt;
&lt;span class="py"&gt;endpoint&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;http://dr-site:9000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# [sourced from https://rclone.org/commands/rclone_sync/, NOT EXECUTED IN CI]&lt;/span&gt;
rclone &lt;span class="nb"&gt;sync &lt;/span&gt;primary:dr-backups-primary dr:dr-backups-dr &lt;span class="nt"&gt;--progress&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then put that behind cron every 30 minutes. Note &lt;code&gt;rclone sync&lt;/code&gt; makes the destination &lt;em&gt;match&lt;/em&gt; the source — it deletes objects at the destination that no longer exist at the source. If you want DR to survive an accidental mass-delete on the primary, use &lt;code&gt;rclone copy&lt;/code&gt; instead, or enable versioning and Object Lock on the DR bucket.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Document &amp;amp; Test the Restore Procedure
&lt;/h3&gt;

&lt;p&gt;Your DR plan is only useful if the person on call can execute it without calling you. I have been that 3 a.m. call; write the runbook for someone who has not seen it before:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DR Runbook: Primary Region Failure&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trigger conditions:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Primary region unreachable for &amp;gt; 15 min&lt;/li&gt;
&lt;li&gt;Major data corruption detected&lt;/li&gt;
&lt;li&gt;Executive decision to failover&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The whole procedure runs about 45–90 minutes. Each step is a separate command block you can paste as-is.&lt;/p&gt;

&lt;p&gt;1.&lt;strong&gt;Verify DR bucket integrity&lt;/strong&gt; (5 min)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   aws s3 &lt;span class="nb"&gt;ls &lt;/span&gt;s3://dr-backups-dr/ &lt;span class="nt"&gt;--recursive&lt;/span&gt; | &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt;
   &lt;span class="c"&gt;# Compare count to expected object count&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2.&lt;strong&gt;Promote DR database&lt;/strong&gt; (15-45 min)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="c"&gt;# Start the instance in the DR region, then stream the dump straight out of S3.&lt;/span&gt;
   &lt;span class="c"&gt;# NOTE: gunzip cannot read an s3:// URL — you must pipe through `aws s3 cp ... -`.&lt;/span&gt;
   &lt;span class="nv"&gt;LATEST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws s3 &lt;span class="nb"&gt;ls &lt;/span&gt;s3://dr-backups-dr/postgres/ | &lt;span class="nb"&gt;sort&lt;/span&gt; | &lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 1 | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $2}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
   aws s3 &lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="s2"&gt;"s3://dr-backups-dr/postgres/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LATEST&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;db.dump.gz"&lt;/span&gt; - &lt;span class="se"&gt;\&lt;/span&gt;
     | &lt;span class="nb"&gt;gunzip&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
     | pg_restore &lt;span class="nt"&gt;-d&lt;/span&gt; production_db
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3.&lt;strong&gt;Update DNS&lt;/strong&gt; (2-30 min, depends on TTL)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="c"&gt;# Route53: Change A record to DR instance IP&lt;/span&gt;
   &lt;span class="c"&gt;# Wait for DNS propagation (monitor with dig + health checks)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;4.&lt;strong&gt;Verify application health&lt;/strong&gt; (10 min)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   curl https://myapp.com/healthcheck
   &lt;span class="c"&gt;# Expect: {"status":"ok","region":"dr"}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;5.&lt;strong&gt;Communicate&lt;/strong&gt; (ongoing)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Status page update&lt;/li&gt;
&lt;li&gt;Internal Slack alert&lt;/li&gt;
&lt;li&gt;Customer notification (if SLA impacted)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Test this runbook quarterly.&lt;/strong&gt; An untested runbook is just a theory, and outages are bad at following theories.&lt;/p&gt;

&lt;h2&gt;
  
  
  What goes wrong with S3 DR?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Replication lag will surprise you
&lt;/h3&gt;

&lt;p&gt;CRR is asynchronous. An object written at T0 might not appear in the DR bucket until T0 + 15 minutes (or longer under load). &lt;strong&gt;Your latest backup might not yet be in DR when you need it.&lt;/strong&gt; This is the failure mode I check first in any DR drill.&lt;/p&gt;

&lt;p&gt;What helps: check replication lag metrics before declaring disaster, and keep a force-sync procedure for critical objects.&lt;/p&gt;

&lt;h3&gt;
  
  
  Encryption keys live in one region
&lt;/h3&gt;

&lt;p&gt;If your primary region uses AWS KMS-managed keys and that region is down... &lt;strong&gt;you can't decrypt backups in the DR region&lt;/strong&gt; unless you've planned for cross-region KMS access. I have seen a perfectly good DR bucket become useless because the key was still in the failed region.&lt;/p&gt;

&lt;p&gt;What helps: use client-side encryption (you hold the key) or ensure the KMS key is accessible from the DR region (multi-Region KMS key).&lt;/p&gt;

&lt;h3&gt;
  
  
  S3 is not a database DR mechanism
&lt;/h3&gt;

&lt;p&gt;S3 is great for DR &lt;em&gt;of things stored in S3&lt;/em&gt;. It is not a replacement for database replication. Your PostgreSQL primary still needs streaming replication, logical replication, or Patroni — S3 is the safety net, not the primary mechanism.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;S3 is probably already your biggest DR asset&lt;/strong&gt; — most critical data lands there eventually.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Three patterns:&lt;/strong&gt; Backup-to-S3 (simplest, RPO=hours), Log shipping (better, RPO=minutes), Active-Active (best, complex).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Start with Pattern 1&lt;/strong&gt; (automated backups + CRR) — it's a 1-day setup that covers 80% of DR scenarios.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test restores quarterly.&lt;/strong&gt; An untested DR plan is a false sense of security.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;S3 complements — doesn't replace — database replication.&lt;/strong&gt; Use both.&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;&lt;em&gt;Building a DR target on self-hosted S3? &lt;a href="https://rustfs.com" rel="noopener noreferrer"&gt;RustFS&lt;/a&gt; is Apache 2.0 licensed and its &lt;a href="https://github.com/rustfs/rustfs" rel="noopener noreferrer"&gt;README Feature &amp;amp; Status table&lt;/a&gt; lists **Bucket Replication&lt;/em&gt;&lt;em&gt;, **Versioning&lt;/em&gt;&lt;em&gt;, **Bitrot Protection&lt;/em&gt;* and &lt;strong&gt;Event Notifications&lt;/strong&gt; as ✅ Available — the four primitives a DR pipeline actually leans on. Being straight with you: &lt;strong&gt;Distributed Mode is still 🚧 Under Testing&lt;/strong&gt;, so validate your multi-node topology yourself before betting a production DR plan on it. &lt;a href="https://rustfs.com/download" rel="noopener noreferrer"&gt;Download here&lt;/a&gt;.*&lt;/p&gt;




&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Is S3 enough for disaster recovery, or do I need a database replica?
&lt;/h3&gt;

&lt;p&gt;You need both. S3 handles the non-transactional layer — files, logs, backups, artifacts. For PostgreSQL or MySQL you still need streaming or logical replication if you want RPO measured in seconds. I treat S3 DR as the safety net and database replication as the thing that actually keeps me asleep at night.&lt;/p&gt;

&lt;h3&gt;
  
  
  How long does it take to restore from S3 after a disaster?
&lt;/h3&gt;

&lt;p&gt;You will not know until you time it. Bandwidth to the S3 endpoint, I/O on the restore target, and whether you are doing a full restore or point-in-time recovery all dominate. Run a quarterly drill, record the real number in the runbook, and use that as your RTO. The planning-doc number is fiction.&lt;/p&gt;

&lt;h3&gt;
  
  
  Should I use AWS S3 or self-hosted S3 for DR?
&lt;/h3&gt;

&lt;p&gt;Both work. AWS S3 with Cross-Region Replication is simpler to configure but costs more (storage plus request and egress fees) and keeps your DR data inside the same vendor blast radius as production. Self-hosted S3 (RustFS, MinIO, Ceph RGW) gives lower ongoing cost, no inter-site egress billing, and vendor independence, at the price of operating the DR storage yourself. Many teams run a hybrid: self-hosted for the primary site, a cloud bucket as the off-site archive tier.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does S3 Cross-Region Replication protect me from accidental deletion?
&lt;/h3&gt;

&lt;p&gt;Not by itself. CRR faithfully replicates deletions when &lt;code&gt;DeleteMarkerReplication&lt;/code&gt; is enabled, so a mass-delete on the primary propagates to DR. Real protection comes from &lt;strong&gt;versioning&lt;/strong&gt; plus &lt;strong&gt;Object Lock&lt;/strong&gt; (WORM retention) on the destination bucket, and from keeping at least one backup copy outside the replication path. Replication is a redundancy mechanism, not a backup.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is the required IAM setup for &lt;code&gt;put-bucket-replication&lt;/code&gt;?
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;Role&lt;/code&gt; field is a required top-level element of the replication configuration — it is the IAM role S3 assumes on your behalf. That role needs read permissions plus &lt;code&gt;s3:GetReplicationConfiguration&lt;/code&gt; on the source bucket, and &lt;code&gt;s3:ReplicateObject&lt;/code&gt;, &lt;code&gt;s3:ReplicateDelete&lt;/code&gt; and &lt;code&gt;s3:ReplicateTags&lt;/code&gt; on the destination. Versioning must be enabled on both buckets before the rule will apply.&lt;/p&gt;




&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;p&gt;All figures and commands in this article were checked against primary sources on &lt;strong&gt;2026-08-06&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Uptime Institute, &lt;em&gt;2022 Outage Analysis&lt;/em&gt; — outage cost and human-error figures: &lt;a href="https://www.businesswire.com/news/home/20220608005265/en/" rel="noopener noreferrer"&gt;https://www.businesswire.com/news/home/20220608005265/en/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;AWS CLI reference, &lt;code&gt;put-bucket-replication&lt;/code&gt; (required &lt;code&gt;Role&lt;/code&gt; field, versioning prerequisite): &lt;a href="https://docs.aws.amazon.com/cli/latest/reference/s3api/put-bucket-replication.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/cli/latest/reference/s3api/put-bucket-replication.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;rclone S3 backend configuration: &lt;a href="https://rclone.org/s3/" rel="noopener noreferrer"&gt;https://rclone.org/s3/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;rclone &lt;code&gt;sync&lt;/code&gt; command semantics: &lt;a href="https://rclone.org/commands/rclone_sync/" rel="noopener noreferrer"&gt;https://rclone.org/commands/rclone_sync/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;RustFS README, Feature &amp;amp; Status table + Apache 2.0 license: &lt;a href="https://github.com/rustfs/rustfs" rel="noopener noreferrer"&gt;https://github.com/rustfs/rustfs&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Commands marked &lt;code&gt;NOT EXECUTED IN CI&lt;/code&gt; are reproduced verbatim from the linked official documentation but were not run in the environment used to write this article.&lt;/p&gt;

</description>
      <category>s3</category>
      <category>disasterrecovery</category>
      <category>backup</category>
      <category>rustfs</category>
    </item>
    <item>
      <title>S3 Lifecycle Policies: How to Cut Your Storage Bill 40% (With the Actual Arithmetic)</title>
      <dc:creator>Ethan Carter</dc:creator>
      <pubDate>Wed, 19 Aug 2026 13:14:00 +0000</pubDate>
      <link>https://dev.to/ethan-carter/s3-lifecycle-policies-how-to-cut-your-storage-bill-40-with-the-actual-arithmetic-4lnk</link>
      <guid>https://dev.to/ethan-carter/s3-lifecycle-policies-how-to-cut-your-storage-bill-40-with-the-actual-arithmetic-4lnk</guid>
      <description>&lt;p&gt;An S3 lifecycle policy moves objects between storage classes on a schedule, or deletes them. The '40%' people quote is real, but only for one specific bucket shape, and only on the storage line. On AWS us-east-1 list prices, pushing 30% of a 100 TB bucket to Standard-IA and 40% to Glacier Instant Retrieval drops the storage line from &lt;strong&gt;$2,304.00 to $1,254.40 — a 45.6% cut&lt;/strong&gt;. The arithmetic is the boring part. The saving leaks back out in the quarter after you ship the rule, in the line items nobody re-opens.&lt;/p&gt;

&lt;p&gt;Every number here is pulled from the &lt;strong&gt;AWS Price List Bulk API&lt;/strong&gt; (&lt;code&gt;us-east-1&lt;/code&gt; offer file, &lt;code&gt;publicationDate 2026-08-07&lt;/code&gt;) or worked out from those rates. If a rate isn't in the offer file, I say so. I'm not inventing a number to fill the gap.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Stats&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Figure&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;S3 Standard, first 50 TB/mo&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$0.023 / GB-mo&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;AWS Price List API, us-east-1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S3 Standard-IA&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;$0.0125 / GB-mo&lt;/strong&gt; + $0.01/GB retrieval&lt;/td&gt;
&lt;td&gt;AWS Price List API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S3 Glacier Instant Retrieval&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;$0.004 / GB-mo&lt;/strong&gt; + $0.03/GB retrieval&lt;/td&gt;
&lt;td&gt;AWS Price List API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S3 Glacier Flexible Retrieval&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$0.0036 / GB-mo&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;AWS Price List API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Minimum object size that transitions by default&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;128 KB&lt;/strong&gt; (since Sept 2024)&lt;/td&gt;
&lt;td&gt;AWS S3 User Guide&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Minimum storage duration&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;30 d&lt;/strong&gt; (IA) / &lt;strong&gt;90 d&lt;/strong&gt; (Glacier IR, Flexible) / &lt;strong&gt;180 d&lt;/strong&gt; (Deep Archive)&lt;/td&gt;
&lt;td&gt;AWS S3 User Guide&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lifecycle transition request, into Glacier Flexible&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$0.03 / 1,000 requests&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;AWS Price List API&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  What is an S3 lifecycle policy?
&lt;/h2&gt;

&lt;p&gt;An S3 lifecycle policy is a bucket-level ruleset: it transitions objects to a cheaper class, or expires them, once they hit an age you set. You attach JSON or XML; S3 runs it asynchronously, so there's no cron job to babysit.&lt;/p&gt;

&lt;p&gt;The User Guide lists five actions: &lt;code&gt;Transition&lt;/code&gt;, &lt;code&gt;Expiration&lt;/code&gt;, &lt;code&gt;NoncurrentVersionTransition&lt;/code&gt;, &lt;code&gt;NoncurrentVersionExpiration&lt;/code&gt;, &lt;code&gt;AbortIncompleteMultipartUpload&lt;/code&gt;, and a &lt;code&gt;Filter&lt;/code&gt; that takes &lt;code&gt;Prefix&lt;/code&gt;, &lt;code&gt;Tag&lt;/code&gt;, &lt;code&gt;ObjectSizeGreaterThan&lt;/code&gt;, &lt;code&gt;ObjectSizeLessThan&lt;/code&gt;, or &lt;code&gt;And&lt;/code&gt; to combine them.&lt;/p&gt;

&lt;p&gt;Two things actually cost you money. One: transitions only go down the ladder. The User Guide is blunt that the Deep Archive transition 'can go only one way.' Two: billing starts the instant the rule is satisfied, not when the data finishes moving. You're paying the destination rate from day one, even if AWS is still copying in the background.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where does the 40% actually come from?
&lt;/h2&gt;

&lt;p&gt;It comes down to the price gap between classes, times how much of your data is cold. That's the whole formula. For a &lt;strong&gt;100 TB bucket in us-east-1&lt;/strong&gt; (AWS bills in 1 TB = 1,024 GB, so that's 102,400 GB), baseline, everything in S3 Standard:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; 51,200 GB × $0.023  = $1,177.60   (first 50 TB band)
 51,200 GB × $0.022  = $1,126.40   (next 450 TB band)
 ------------------------------------------------
 TOTAL                 $2,304.00 / month
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Split that 100 TB three ways: hot in Standard, warm in Standard-IA, cold in Glacier IR:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Hot / Warm / Cold&lt;/th&gt;
&lt;th&gt;Monthly storage&lt;/th&gt;
&lt;th&gt;Saving&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Conservative&lt;/td&gt;
&lt;td&gt;50% / 30% / 20%&lt;/td&gt;
&lt;td&gt;$1,643.52&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;−28.7%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typical&lt;/td&gt;
&lt;td&gt;30% / 30% / 40%&lt;/td&gt;
&lt;td&gt;$1,254.40&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;−45.6%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Aggressive&lt;/td&gt;
&lt;td&gt;20% / 20% / 60%&lt;/td&gt;
&lt;td&gt;$972.80&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;−57.8%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That 45.6% assumes roughly two-thirds of your bytes are over 30 days old, which is the usual shape for logs, backups, and ML snapshots. But it's the storage line only. Requests and retrievals bill on top, and they're exactly where the saving disappears.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which storage class should each tier of data land in?
&lt;/h2&gt;

&lt;p&gt;Pick the class by how often you actually read the data, then check the minimum duration. The two numbers that drive cost are per-GB storage and per-GB retrieval. Here's the full ladder from the same offer file:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Storage class&lt;/th&gt;
&lt;th&gt;$/GB-mo&lt;/th&gt;
&lt;th&gt;Retrieval&lt;/th&gt;
&lt;th&gt;Min duration&lt;/th&gt;
&lt;th&gt;Min billable size&lt;/th&gt;
&lt;th&gt;Availability SLA&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;S3 Standard&lt;/td&gt;
&lt;td&gt;$0.023&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;99.99%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S3 Standard-IA&lt;/td&gt;
&lt;td&gt;$0.0125&lt;/td&gt;
&lt;td&gt;$0.01/GB&lt;/td&gt;
&lt;td&gt;30 days&lt;/td&gt;
&lt;td&gt;128 KB&lt;/td&gt;
&lt;td&gt;99.9%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S3 One Zone-IA&lt;/td&gt;
&lt;td&gt;$0.010&lt;/td&gt;
&lt;td&gt;$0.01/GB&lt;/td&gt;
&lt;td&gt;30 days&lt;/td&gt;
&lt;td&gt;128 KB&lt;/td&gt;
&lt;td&gt;99.5%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S3 Glacier Instant Retrieval&lt;/td&gt;
&lt;td&gt;$0.004&lt;/td&gt;
&lt;td&gt;$0.03/GB&lt;/td&gt;
&lt;td&gt;90 days&lt;/td&gt;
&lt;td&gt;128 KB&lt;/td&gt;
&lt;td&gt;99.9%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S3 Glacier Flexible Retrieval&lt;/td&gt;
&lt;td&gt;$0.0036&lt;/td&gt;
&lt;td&gt;$0.01/GB standard, $0.00/GB bulk&lt;/td&gt;
&lt;td&gt;90 days&lt;/td&gt;
&lt;td&gt;n/a*&lt;/td&gt;
&lt;td&gt;99.99% after restore&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S3 Glacier Deep Archive&lt;/td&gt;
&lt;td&gt;see note&lt;/td&gt;
&lt;td&gt;$0.02/GB standard, $0.0025/GB bulk&lt;/td&gt;
&lt;td&gt;180 days&lt;/td&gt;
&lt;td&gt;n/a*&lt;/td&gt;
&lt;td&gt;99.99% after restore&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;* Glacier Flexible and Deep Archive add &lt;strong&gt;40 KB of overhead per object&lt;/strong&gt;, of which 8 KB is billed at S3 Standard rates and 32 KB at the destination Glacier rate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note on Deep Archive:&lt;/strong&gt; the us-east-1 S3 offer file exposes Deep Archive's transition, retrieval, and checksum SKUs but &lt;strong&gt;no per-GB timed-storage SKU&lt;/strong&gt;. I won't quote a storage rate I can't pull from a first-party file, so get it from the pricing console before you budget on it. The closest verifiable neighbour in the same file is Intelligent-Tiering's Deep Archive Access tier at &lt;strong&gt;$0.00099/GB-mo&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;One Zone-IA is 20% cheaper and lives in a single AZ at 99.5% availability. Fine for regenerable stuff: thumbnails, transcodes, a second backup copy. Never the only copy you've got.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you write and apply the rule?
&lt;/h2&gt;

&lt;p&gt;With &lt;code&gt;aws s3api put-bucket-lifecycle-configuration&lt;/code&gt;. This is the official AWS CLI example, copied verbatim from the command reference:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws s3api put-bucket-lifecycle-configuration &lt;span class="nt"&gt;--bucket&lt;/span&gt; amzn-s3-demo-bucket &lt;span class="nt"&gt;--lifecycle-configuration&lt;/span&gt;  file://lifecycle.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Rules"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"ID"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Move rotated logs to Glacier"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Prefix"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"rotated/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Enabled"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Transitions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"Date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2015-11-10T00:00:00.000Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"StorageClass"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"GLACIER"&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Enabled"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Prefix"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"NoncurrentVersionTransitions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"NoncurrentDays"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"StorageClass"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"GLACIER"&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"ID"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Move old versions to Glacier"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;[Both blocks sourced verbatim from docs.aws.amazon.com/cli/latest/reference/s3api/put-bucket-lifecycle-configuration.html — NOT EXECUTED IN CI.]&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A workable rule, using only documented &lt;code&gt;LifecycleRule&lt;/code&gt; fields: it tiers logs down, skips objects too small to move, expires old versions, and reclaims abandoned multipart uploads:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Rules"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"ID"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"logs-tier-down"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Enabled"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Filter"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"And"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"Prefix"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"logs/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"ObjectSizeGreaterThan"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;131072&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Transitions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"Days"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;"StorageClass"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"STANDARD_IA"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"Days"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"StorageClass"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"GLACIER_IR"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"NoncurrentVersionExpiration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"NoncurrentDays"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"AbortIncompleteMultipartUpload"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"DaysAfterInitiation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;ObjectSizeGreaterThan: 131072&lt;/code&gt; is just 128 KB written out, and the next section explains why that number matters. The Glacier IR hop sits at day 120, not 90, because Standard-IA has a 30-day minimum and the User Guide says the second transition 'must occur after at least' the first one's minimum has passed. Tighten that gap and AWS rejects the rule outright.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why do small objects break lifecycle economics?
&lt;/h2&gt;

&lt;p&gt;You pay per transition request, not per byte. The first lifecycle bill that ever surprised me was a 200 KB-heavy bucket where transition cost ate the whole storage saving, which is the trap most people hit before they check object size. A 200 KB object costs the same request as a 200 MB one. AWS made that the default in September 2024: 'the default behavior prevents objects smaller than 128 KB from being transitioned to any storage class.'&lt;/p&gt;

&lt;p&gt;Payback (transition fee divided by the monthly saving) at list rates:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Object size&lt;/th&gt;
&lt;th&gt;→ Standard-IA&lt;/th&gt;
&lt;th&gt;→ Glacier IR&lt;/th&gt;
&lt;th&gt;→ Glacier Flexible&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;128 KB&lt;/td&gt;
&lt;td&gt;7.8 months&lt;/td&gt;
&lt;td&gt;8.6 months&lt;/td&gt;
&lt;td&gt;12.7 months&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;256 KB&lt;/td&gt;
&lt;td&gt;3.9 months&lt;/td&gt;
&lt;td&gt;4.3 months&lt;/td&gt;
&lt;td&gt;6.3 months&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1 MB&lt;/td&gt;
&lt;td&gt;1.0 month&lt;/td&gt;
&lt;td&gt;1.1 months&lt;/td&gt;
&lt;td&gt;1.6 months&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10 MB&lt;/td&gt;
&lt;td&gt;~0.1 month&lt;/td&gt;
&lt;td&gt;~0.1 month&lt;/td&gt;
&lt;td&gt;~0.2 months&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Scale that to a whole bucket. Same 100 TB, one transition each, at $0.01/1,000 into Standard-IA and $0.03/1,000 into Glacier Flexible:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Average object size&lt;/th&gt;
&lt;th&gt;Object count&lt;/th&gt;
&lt;th&gt;One-time bill → Standard-IA&lt;/th&gt;
&lt;th&gt;→ Glacier Flexible&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;64 MB&lt;/td&gt;
&lt;td&gt;1,638,400&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$16.38&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$49.15&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2 MB&lt;/td&gt;
&lt;td&gt;52,428,800&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$524.29&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$1,572.86&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;200 KB&lt;/td&gt;
&lt;td&gt;536,870,912&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$5,368.71&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$16,106.13&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Same 100 TB, but a 328x swing in transition cost driven purely by object size. AWS says it straight: 'for smaller objects, the transition costs can outweigh the storage savings.' Check your average object size in S3 Storage Lens first, and put &lt;code&gt;ObjectSizeGreaterThan&lt;/code&gt; on every transition.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does the policy cost when the data comes back?
&lt;/h2&gt;

&lt;p&gt;Retrieval fees are the second place the win leaks out. Standard-IA saves $0.0105/GB-mo versus Standard but charges $0.01/GB to read. Those two numbers give you a hard break-even:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Class&lt;/th&gt;
&lt;th&gt;Monthly saving vs Standard&lt;/th&gt;
&lt;th&gt;Retrieval&lt;/th&gt;
&lt;th&gt;Break-even&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Standard-IA&lt;/td&gt;
&lt;td&gt;$0.0105/GB&lt;/td&gt;
&lt;td&gt;$0.01/GB&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1.05 full reads/month&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;One Zone-IA&lt;/td&gt;
&lt;td&gt;$0.0130/GB&lt;/td&gt;
&lt;td&gt;$0.01/GB&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1.30 full reads/month&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Glacier Instant Retrieval&lt;/td&gt;
&lt;td&gt;$0.0190/GB&lt;/td&gt;
&lt;td&gt;$0.03/GB&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.63 full reads/month&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Read a Glacier IR object more than about twice a quarter and you'd have been cheaper in Standard. The '$0.004/GB' headline hides how tight that budget really is.&lt;/p&gt;

&lt;p&gt;Early deletion is the third trap. Delete or overwrite before the minimum and AWS bills the rest pro-rated: $0.0125/GB-mo for Standard-IA, $0.004 for Glacier IR, $0.0036 for Glacier. Point a 30-day retention rule at a 90-day-minimum class and you pay for 90 days of Glacier every month, indefinitely.&lt;/p&gt;

&lt;h2&gt;
  
  
  Intelligent-Tiering or hand-written rules?
&lt;/h2&gt;

&lt;p&gt;Intelligent-Tiering adds a &lt;strong&gt;monitoring fee of $0.0025 per 1,000 objects per month&lt;/strong&gt; and moves data between tiers for you, with no retrieval fee on the Frequent, Infrequent, or Archive Instant tiers. Hand-written rules are free to run but assume you already know your access pattern.&lt;/p&gt;

&lt;p&gt;What decides it is object count, not total data:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Object count&lt;/th&gt;
&lt;th&gt;Intelligent-Tiering monitoring&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1,000,000&lt;/td&gt;
&lt;td&gt;$2.50/month&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;50,000,000&lt;/td&gt;
&lt;td&gt;$125.00/month&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;536,870,912&lt;/td&gt;
&lt;td&gt;$1,342.18/month&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;I use Intelligent-Tiering for buckets with unpredictable reads and big objects, and explicit rules where the access pattern is already obvious. Logs and backups are the obvious case: nobody reads last quarter's access logs spontaneously, so a monitoring fee to find that out is pure waste. The other direction: half a billion thumbnails is $1,342/month in monitoring, and most of those objects sit under the 128 KB threshold anyway.&lt;/p&gt;

&lt;h2&gt;
  
  
  Does lifecycle work the same on self-hosted S3?
&lt;/h2&gt;

&lt;p&gt;Not quite. On AWS a transition shuffles bytes between AWS-run classes. On self-hosted S3 there's no Glacier, so a 'lifecycle rule' means expire, clean up old versions, or move to a remote tier you set up yourself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MinIO&lt;/strong&gt; implements this through &lt;code&gt;mc ilm&lt;/code&gt;. From the official command reference:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mc ilm rule add &lt;span class="nt"&gt;--expire-days&lt;/span&gt; 90 &lt;span class="nt"&gt;--noncurrent-expire-days&lt;/span&gt; 30  myaistor/mydata

mc ilm rule add &lt;span class="nt"&gt;--transition-days&lt;/span&gt; 30 &lt;span class="nt"&gt;--transition-tier&lt;/span&gt; &lt;span class="s2"&gt;"COLDTIER"&lt;/span&gt; myaistor/mydata
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;[Verbatim from min.io/docs — the &lt;code&gt;myaistor&lt;/code&gt; alias is MinIO's own example alias. NOT EXECUTED IN CI.]&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;RustFS&lt;/strong&gt; is straight about this one. Its README Feature &amp;amp; Status table shows &lt;strong&gt;Lifecycle Management as 🚧 Under Testing&lt;/strong&gt;, next to Distributed Mode and RustFS KMS. Versioning, Bucket Replication, Event Notifications, Bitrot Protection, and Single Node Mode are ✅ available; there's no Object Lock row and no tiering row. If tiered lifecycle is a hard requirement today, RustFS isn't the answer yet.&lt;/p&gt;

&lt;p&gt;What RustFS does give you is the structural argument: your own disks mean no per-GB retrieval fee and no per-request transition fee, so the break-even math above stops applying. The catch is you buy and run the hardware. If you're already running RustFS for other reasons, expire-and-cleanup lifecycle is free and worth turning on. If you'd stand it up purely to dodge egress, the hardware usually costs more than the bandwidth you save, so run the numbers first.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 9000:9000 &lt;span class="nt"&gt;-p&lt;/span&gt; 9001:9001 &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;/data:/data &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;/logs:/logs rustfs/rustfs:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;[Verbatim from github.com/rustfs/rustfs README — NOT EXECUTED IN CI.]&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;S3 API on &lt;code&gt;:9000&lt;/code&gt;, Console on &lt;code&gt;:9001&lt;/code&gt;. The README lists default credentials &lt;code&gt;rustfsadmin&lt;/code&gt; / &lt;code&gt;rustfsadmin&lt;/code&gt;, and docs.rustfs.com explicitly tells you not to keep them: "Do not use the well-known &lt;code&gt;rustfsadmin&lt;/code&gt; value for either credential."&lt;/p&gt;

&lt;h2&gt;
  
  
  The five mistakes that eat the savings
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Transitioning objects under 128 KB.&lt;/strong&gt; Payback is 8–13 months. Filter them out with &lt;code&gt;ObjectSizeGreaterThan&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chaining transitions inside a minimum duration.&lt;/strong&gt; Standard-IA → Glacier IR at day 60 is invalid; the IA minimum is 30 days and Glacier IR's own 90-day clock only starts on arrival.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Forgetting &lt;code&gt;NoncurrentVersionExpiration&lt;/code&gt;.&lt;/strong&gt; On a versioned bucket, every overwrite leaves a billable version. Storage grows forever while your object count looks flat.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skipping &lt;code&gt;AbortIncompleteMultipartUpload&lt;/code&gt;.&lt;/strong&gt; Failed uploads leave parts that are invisible in &lt;code&gt;ListObjects&lt;/code&gt; but fully billable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enabling Intelligent-Tiering on hundreds of millions of small objects.&lt;/strong&gt; Objects under 128 KB are never monitored for tiering, but you can still be paying monitoring fees at scale for the rest.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How much can S3 lifecycle policies actually save?
&lt;/h3&gt;

&lt;p&gt;On AWS us-east-1 list prices, a 100 TB bucket costs $2,304.00/month entirely in S3 Standard. Moving 30% to Standard-IA and 40% to Glacier Instant Retrieval brings it to $1,254.40/month — a 45.6% cut. A conservative 50/30/20 split still saves 28.7%. Savings scale linearly with the fraction of cold data, and apply to the storage line only.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is the minimum object size for an S3 lifecycle transition?
&lt;/h3&gt;

&lt;p&gt;128 KB. Since September 2024, AWS's default behavior prevents objects smaller than 128 KB from transitioning to any storage class. Configurations created before September 2024 keep the old behavior until you edit them. You can override with the &lt;code&gt;x-amz-transition-default-minimum-object-size&lt;/code&gt; header on &lt;code&gt;PutBucketLifecycleConfiguration&lt;/code&gt;, but you usually should not: a 128 KB object takes 7.8 months to pay back a single Standard-IA transition.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does S3 charge for lifecycle transitions?
&lt;/h3&gt;

&lt;p&gt;Yes, per request. In us-east-1: $0.01 per 1,000 transitions into Standard-IA, One Zone-IA, or Intelligent-Tiering; $0.02 per 1,000 into Glacier Instant Retrieval; $0.03 per 1,000 into Glacier Flexible Retrieval; $0.05 per 1,000 into Glacier Deep Archive. For 536 million small objects, that single move to Glacier Flexible is $16,106.&lt;/p&gt;

&lt;h3&gt;
  
  
  When is Standard-IA more expensive than S3 Standard?
&lt;/h3&gt;

&lt;p&gt;When you read the object more than 1.05 times per month. Standard-IA saves $0.0105/GB-month on storage but charges $0.01/GB on retrieval, so a little over one full read per month erases the discount. Glacier Instant Retrieval breaks even at 0.63 reads/month because its $0.03/GB retrieval fee is three times higher.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do self-hosted S3 servers support lifecycle policies?
&lt;/h3&gt;

&lt;p&gt;Partially, and support varies. MinIO implements lifecycle through &lt;code&gt;mc ilm rule add&lt;/code&gt; with expiration and remote-tier transition. RustFS lists Lifecycle Management as 🚧 Under Testing in its README Feature &amp;amp; Status table, so it is not production-ready today. The upside of self-hosting is that per-GB retrieval fees and per-request transition fees do not exist, which removes the break-even math entirely.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;p&gt;All figures checked &lt;strong&gt;2026-08-10&lt;/strong&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;th&gt;Used for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;AWS Price List Bulk API — &lt;code&gt;pricing.us-east-1.amazonaws.com/offers/v1.0/aws/AmazonS3/current/region_index.json&lt;/code&gt;, us-east-1 offer file, &lt;code&gt;publicationDate 2026-08-07&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;All per-GB storage, retrieval, request, and transition rates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/lifecycle-transition-general-considerations.html" rel="noopener noreferrer"&gt;Transitioning objects — general considerations&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;128 KB default, minimum durations, 40 KB Glacier overhead, one-way Deep Archive, early-billing rule&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/storage-class-intro.html" rel="noopener noreferrer"&gt;Understanding and managing Amazon S3 storage classes&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Minimum billable size, availability SLAs, AZ counts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/intro-lifecycle-rules.html" rel="noopener noreferrer"&gt;S3 Lifecycle configuration elements&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Action and Filter element names&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://docs.aws.amazon.com/cli/latest/reference/s3api/put-bucket-lifecycle-configuration.html" rel="noopener noreferrer"&gt;aws s3api put-bucket-lifecycle-configuration&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Verbatim CLI command and JSON example&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://min.io/docs/minio/linux/reference/minio-mc/mc-ilm-rule-add.html" rel="noopener noreferrer"&gt;mc ilm rule add&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Verbatim MinIO ILM commands&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/rustfs/rustfs" rel="noopener noreferrer"&gt;rustfs/rustfs README&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Verbatim docker run, default credentials, Feature &amp;amp; Status table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://docs.rustfs.com/en/installation/container/docker" rel="noopener noreferrer"&gt;docs.rustfs.com — Docker&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;API port 9000, Console port 9001, credential warning&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;p&gt;Before you trust any of this on your own bucket, pull object count and average size from S3 Storage Lens and drop them into the payback table to see whether a rule actually pays off. In my experience most don't, or only barely.&lt;/p&gt;

&lt;p&gt;If retrieval and transition fees, not the storage rate, are what's blowing up your bill, self-hosted S3-compatible storage deletes both line items. &lt;a href="https://rustfs.com" rel="noopener noreferrer"&gt;RustFS&lt;/a&gt; is Apache 2.0, S3-compatible, and runs single-node today: &lt;a href="https://github.com/rustfs/rustfs" rel="noopener noreferrer"&gt;github.com/rustfs/rustfs&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>s3</category>
      <category>devops</category>
      <category>storage</category>
    </item>
    <item>
      <title>Cross-Region S3 Replication Without the Gotchas (2026)</title>
      <dc:creator>Ethan Carter</dc:creator>
      <pubDate>Mon, 17 Aug 2026 13:07:00 +0000</pubDate>
      <link>https://dev.to/ethan-carter/cross-region-s3-replication-without-the-gotchas-2026-f3l</link>
      <guid>https://dev.to/ethan-carter/cross-region-s3-replication-without-the-gotchas-2026-f3l</guid>
      <description>&lt;h1&gt;
  
  
  Cross-Region S3 Replication Without the Gotchas (2026)
&lt;/h1&gt;

&lt;p&gt;Cross-region S3 replication copies objects from a source bucket to a destination in another region, continuously. AWS S3, MinIO, and RustFS all do it, but the failures almost never come from the copy itself. They come from versioning, IAM, and delete-marker defaults you forgot to set.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key facts before you start
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Check&lt;/th&gt;
&lt;th&gt;What the official docs say&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Versioning&lt;/td&gt;
&lt;td&gt;AWS requires versioning &lt;strong&gt;enabled on both&lt;/strong&gt; the source and destination buckets before replication works.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pre-existing objects&lt;/td&gt;
&lt;td&gt;AWS does &lt;strong&gt;not&lt;/strong&gt; replicate objects written before the replication configuration; use S3 Batch Replication to backfill.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IAM role&lt;/td&gt;
&lt;td&gt;AWS replication needs a role with 8 S3 actions (GetReplicationConfiguration, ListBucket, GetObjectVersionForReplication/Acl/Tagging, ReplicateObject/Delete/Tags).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MinIO&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;mc replicate add&lt;/code&gt; requires versioning on the source bucket; objects without a version ID are excluded.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RustFS&lt;/td&gt;
&lt;td&gt;Site replication (&lt;code&gt;rc admin replicate add&lt;/code&gt;) links whole deployments and needs versioning + TLS + root creds at every site.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What is cross-region S3 replication, in plain terms?
&lt;/h2&gt;

&lt;p&gt;CRR is a server-side rule that copies every new object from a source bucket to a destination in another region, asynchronously. 'Asynchronously' matters here: the source write returns success before the copy lands, so the two buckets are eventually consistent, not locked in step. Anything that speaks the &lt;code&gt;PUT Bucket Replication&lt;/code&gt; API does the same: AWS, MinIO, RustFS. The point is disaster recovery and reads closer to the user. But 'just copy my bucket to another region' hides a dozen small defaults. Versioning state, IAM trust, delete-marker handling, KMS keys: each one fails silently instead of throwing an error you'd notice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Versioning is the prerequisite nobody can skip
&lt;/h2&gt;

&lt;p&gt;This is the one I see missed most in postmortems. Versioning has to be on, at both ends, or replication doesn't work. I've spent multiple sessions debugging broken replication from skipping the 15-minute propagation wait after enabling versioning, so now I turn it on and walk away before writing anything. AWS tracks objects by version ID, so the destination needs it too. Enabling it is one CLI call (verbatim below). AWS flags a gotcha here that's worth taking seriously:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Enable versioning on BOTH the source and destination bucket&lt;/span&gt;
aws s3api put-bucket-versioning &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--bucket&lt;/span&gt; amzn-s3-demo-bucket &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--versioning-configuration&lt;/span&gt; &lt;span class="nv"&gt;Status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Enabled
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;AWS note: "When you enable versioning on a bucket for the first time, it might take a short amount of time for the change to be fully propagated... We recommend that you wait for 15 minutes after enabling versioning before issuing write operations."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you attach the rule to a bucket without versioning, AWS rejects the config outright. If only the destination is missing it, the source accepts writes but every replica write fails silently, and nothing shows up remotely. That's the failure I get pinged about more than any other: replication looks healthy, the destination is just empty.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you set up the AWS IAM role without a silent AccessDenied?
&lt;/h2&gt;

&lt;p&gt;S3 assumes an IAM role to do the copy. The role has two halves: a trust policy letting &lt;code&gt;s3.amazonaws.com&lt;/code&gt; assume it, and a permissions policy granting the exact replication actions. Both blocks below are verbatim from the AWS replication permissions docs. The trust policy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&lt;/span&gt;&lt;span class="p"&gt;:[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"Principal"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Service"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"s3.amazonaws.com"&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"sts:AssumeRole"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The permissions policy (source bucket = &lt;code&gt;amzn-s3-demo-source-bucket&lt;/code&gt;, destination = &lt;code&gt;amzn-s3-demo-destination-bucket&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"s3:GetReplicationConfiguration"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"s3:ListBucket"&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:s3:::amzn-s3-demo-source-bucket"&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"s3:GetObjectVersionForReplication"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"s3:GetObjectVersionAcl"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"s3:GetObjectVersionTagging"&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:s3:::amzn-s3-demo-source-bucket/*"&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"s3:ReplicateObject"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"s3:ReplicateDelete"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"s3:ReplicateTags"&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:s3:::amzn-s3-demo-destination-bucket/*"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where this goes wrong is quiet: drop the &lt;code&gt;s3.amazonaws.com&lt;/code&gt; principal from the trust policy, or leave &lt;code&gt;s3:ReplicateDelete&lt;/code&gt; out of the permissions, and replication just doesn't happen. No CloudTrail error, no SNS alert; the destination stays empty. Check the role ARN in the replication config matches what you attached, then test with one object.&lt;/p&gt;

&lt;h2&gt;
  
  
  The replication configuration file, field by field
&lt;/h2&gt;

&lt;p&gt;With versioning and the role set, you attach a replication config to the source bucket. The command is verbatim from the AWS CLI reference:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws s3api put-bucket-replication &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--bucket&lt;/span&gt; amzn-s3-demo-bucket1 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--replication-configuration&lt;/span&gt; file://replication.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The minimal &lt;code&gt;replication.json&lt;/code&gt; (verbatim from AWS docs) already bakes in two defaults that bite people:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:iam::123456789012:role/s3-replication-role"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Rules"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Enabled"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"DeleteMarkerReplication"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"Status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Disabled"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Filter"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"Prefix"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Destination"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"Bucket"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:s3:::amzn-s3-demo-bucket2"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An empty &lt;code&gt;Filter&lt;/code&gt; prefix means 'all objects'; &lt;code&gt;Priority&lt;/code&gt; decides which rule wins when two rules match the same object. The two fields worth adding for production are &lt;code&gt;Metrics&lt;/code&gt; / &lt;code&gt;ReplicationTime&lt;/code&gt;, which switch on lag tracking. Note they live inside the &lt;code&gt;Destination&lt;/code&gt; block, per the AWS schema:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:iam::123456789012:role/s3-replication-role"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Rules"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"ID"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"replicate-all-with-metrics"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Enabled"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Filter"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"Prefix"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"DeleteMarkerReplication"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"Status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Disabled"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Destination"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"Bucket"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:s3:::amzn-s3-demo-bucket2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"Metrics"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"Status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Enabled"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"EventThreshold"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"Minutes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"ReplicationTime"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"Status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Enabled"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"Time"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"Minutes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Metrics&lt;/code&gt; and &lt;code&gt;ReplicationTime&lt;/code&gt; are real fields in the AWS replication schema; enabling them is what makes replication lag visible instead of invisible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why are your old objects missing from the replica?
&lt;/h2&gt;

&lt;p&gt;This trips up almost everyone, and it's burned me on migrations that looked green until someone noticed six months of history missing from the replica. AWS says it plainly: 'Objects that existed before you set up replication aren't replicated automatically. In other words, Amazon S3 doesn't replicate objects retroactively.' A rule you add today only covers objects written or changed after it goes live. That 2 TB of history already in the source? It sits there until you backfill it explicitly.&lt;/p&gt;

&lt;p&gt;On AWS, the supported way to handle that is &lt;strong&gt;S3 Batch Replication&lt;/strong&gt;, a one-time job that copies existing objects on demand (the older per-rule &lt;code&gt;ExistingObjectReplication&lt;/code&gt; option is no longer supported — AWS removed it and points to Batch Replication). Note that backfilling is a large, billable operation for cross-region transfers. MinIO does the same through the &lt;code&gt;--replicate&lt;/code&gt; flag instead of a JSON field:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mc replicate add myaistor/mybucket &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nt"&gt;--remote-bucket&lt;/span&gt; https://user:secret@minio.mysite.tld/remotebucket &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nt"&gt;--replicate&lt;/span&gt; &lt;span class="s2"&gt;"delete,delete-marker,existing-objects"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;existing-objects&lt;/code&gt; value tells MinIO to replicate objects already there. MinIO needs versioning on first: objects written before versioning has no version ID and get excluded. Check the current MinIO docs for the exact &lt;code&gt;mc version&lt;/code&gt; subcommand before running that, since the flag name drifts between versions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Delete markers and KMS keys: the two settings that bite later
&lt;/h2&gt;

&lt;p&gt;Delete markers and KMS encryption both default to the safe-but-surprising option, and neither one errors when it's wrong. First, delete markers. With versioning on, deleting an object writes a delete marker instead of removing data. &lt;code&gt;DeleteMarkerReplication&lt;/code&gt; is &lt;code&gt;Disabled&lt;/code&gt; by default, so a delete in the source does &lt;strong&gt;not&lt;/strong&gt; delete in the destination, so your DR replica keeps serving the 'deleted' object. Set &lt;code&gt;"DeleteMarkerReplication": { "Status": "Enabled" }&lt;/code&gt; if you want deletes to follow, but in a bidirectional setup that can trigger delete storms, so decide on purpose. From production experience I rarely run bidirectional replication with delete-marker sync on; a delete storm across sites is painful and slow to unwind, so I keep delete propagation off and clean up the destination by hand when something is actually gone.&lt;/p&gt;

&lt;p&gt;Second, KMS-encrypted objects. If the source uses SSE-KMS, replication needs &lt;code&gt;SourceSelectionCriteria.SseKmsEncryptedObjects.Status = "Enabled"&lt;/code&gt; plus a &lt;code&gt;Destination.EncryptionConfiguration.ReplicaKmsKeyID&lt;/code&gt;, and the IAM role has to be allowed &lt;code&gt;kms:Decrypt&lt;/code&gt; on the source key and &lt;code&gt;kms:Encrypt&lt;/code&gt; on the destination key (&lt;code&gt;kms:GenerateDataKey&lt;/code&gt; is only needed when you replicate plaintext objects into a destination bucket that has default SSE-KMS/DSSE-KMS encryption). Miss any of that and encrypted objects never show up at the destination while unencrypted ones do. That's a partial, silent failure that's a pain to diagnose. Safest move until the KMS grants are confirmed: keep the same key, or SSE-S3, on both ends.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do self-hosted engines replicate across regions?
&lt;/h2&gt;

&lt;p&gt;If you run your own S3-compatible storage, the model holds: a rule or a site link copies objects between two endpoints, but the tooling is different. On MinIO, the &lt;code&gt;mc replicate add&lt;/code&gt; above is the whole flow (after versioning). On RustFS, cross-region is &lt;strong&gt;site replication&lt;/strong&gt;, which links two or more independent deployments and syncs buckets, object versions, and IAM across them. The commands are from the RustFS &lt;code&gt;rc&lt;/code&gt; client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Configure one alias per site (repeat for each deployment)&lt;/span&gt;
rc &lt;span class="nb"&gt;alias set &lt;/span&gt;site1 https://site1.example.com:9000 &lt;span class="se"&gt;\&lt;/span&gt;
    &amp;lt;your-access-key&amp;gt; &amp;lt;your-secret-key&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--region&lt;/span&gt; us-east-1 &lt;span class="nt"&gt;--bucket-lookup&lt;/span&gt; path

&lt;span class="c"&gt;# Confirm both sites are reachable&lt;/span&gt;
rc ready site1

&lt;span class="c"&gt;# Link the sites (first alias receives the admin request)&lt;/span&gt;
rc admin replicate add site1 site2

&lt;span class="c"&gt;# Inspect replication status from either site&lt;/span&gt;
rc admin replicate status site1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;RustFS is explicit about what this needs: two or more independent deployments, a stable S3 API endpoint per site, bidirectional connectivity on the S3 API port (normally &lt;code&gt;9000&lt;/code&gt;), trusted TLS certs, the &lt;code&gt;rc&lt;/code&gt; client on a secured host, root admin creds at every site, and bucket-versioning support at every site. Bring each deployment up with the verified Docker command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 9000:9000 &lt;span class="nt"&gt;-p&lt;/span&gt; 9001:9001 &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;/data:/data &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;/logs:/logs rustfs/rustfs:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Default console credentials are &lt;code&gt;rustfsadmin&lt;/code&gt; / &lt;code&gt;rustfsadmin&lt;/code&gt;, and the console listens on port &lt;code&gt;9001&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The RustFS docs are clear about the limits: site replication is &lt;strong&gt;asynchronous&lt;/strong&gt; (a successful write at one site doesn't mean the other already has it) and provides &lt;strong&gt;no DNS failover, no traffic routing, no application recovery orchestration&lt;/strong&gt;. Plan your own recovery. Distributed Mode is still 'Under Testing' in the Feature &amp;amp; Status table, so treat each linked site as single-node-ready today, not a multi-node cluster, and test the workflow on empty sites before you link production data. RustFS gives you cross-region sync, but not a turnkey active-active setup that resolves conflicts. The docs don't pretend otherwise. If replication is the only reason you'd stand up a second cluster, do the egress math first: the bandwidth you save is usually smaller than a second deployment's run cost, and self-hosting only wins when you already own the hardware or you have a data-residency requirement.&lt;/p&gt;

&lt;h2&gt;
  
  
  Monitoring replication lag and the cost of cross-region traffic
&lt;/h2&gt;

&lt;p&gt;You can't see replication lag until you turn on metrics. On AWS that means setting &lt;code&gt;Metrics&lt;/code&gt; and &lt;code&gt;ReplicationTime&lt;/code&gt; in the rule (above) and alarming on the CloudWatch metrics they produce, and without those blocks AWS emits no replication-latency metric at all. &lt;code&gt;ReplicationTime&lt;/code&gt; lets you request a target replication window in minutes; AWS also offers S3 Replication Time Control (S3 RTC) for workloads that need a contracted SLA (99.9% of objects replicate within 15 minutes). Alarm on sustained backlog, not a single sample. Async replication falls behind temporarily under bursty writes.&lt;/p&gt;

&lt;p&gt;On cost, cloud and self-hosted aren't a tweak apart, they're structurally different. AWS bills inter-region transfer for every replicated byte (rates vary by region pair), so a high-churn bucket racks up real egress. A self-hosted pair, RustFS via &lt;code&gt;rc admin replicate add&lt;/code&gt; or MinIO via &lt;code&gt;mc replicate add&lt;/code&gt;, moves bytes over your own network, so there's no per-GB cloud egress for the replication itself (you still pay your bandwidth provider). I'm not quoting a per-GB number here because AWS inter-region rates are region-pair-specific and move around. The point that holds: the engine choice decides who sends the egress bill, not whether bytes move.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Does S3 cross-region replication copy existing objects?
&lt;/h3&gt;

&lt;p&gt;No. AWS states that objects created before the replication configuration is attached are not replicated automatically. To move historical data, run an S3 Batch Replication job (the older per-rule &lt;code&gt;ExistingObjectReplication&lt;/code&gt; option is no longer supported). MinIO covers the same case with the &lt;code&gt;existing-objects&lt;/code&gt; value in &lt;code&gt;mc replicate add --replicate&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does cross-region replication replicate delete markers?
&lt;/h3&gt;

&lt;p&gt;Not by default, and I'd leave it off unless you have a reason. In the AWS replication schema, &lt;code&gt;DeleteMarkerReplication&lt;/code&gt; defaults to &lt;code&gt;Disabled&lt;/code&gt;, so deleting an object in the source does not delete its replica. Flip it to &lt;code&gt;"Status": "Enabled"&lt;/code&gt; only when the destination is a true mirror and you accept that a bad delete now spreads to both sides.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does the destination bucket need versioning for S3 replication?
&lt;/h3&gt;

&lt;p&gt;Yes, on both ends, no exceptions. Replication keys off version IDs, so the destination has to track them too. One gotcha: the change isn't instant. AWS says wait about 15 minutes after you flip versioning on before you start writing, or you'll replicate into a half-propagated bucket and wonder why objects are missing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can MinIO or RustFS do cross-region S3 replication?
&lt;/h3&gt;

&lt;p&gt;Yes. MinIO uses &lt;code&gt;mc replicate add&lt;/code&gt; with a &lt;code&gt;--replicate&lt;/code&gt; flag that controls delete, delete-marker, and existing-object replication. RustFS uses site replication via the &lt;code&gt;rc&lt;/code&gt; client: &lt;code&gt;rc admin replicate add site1 site2&lt;/code&gt; links two deployments and syncs buckets, object versions, and IAM. Both require versioning on the source and (for RustFS) bidirectional network access on the S3 API port.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I monitor S3 replication lag?
&lt;/h3&gt;

&lt;p&gt;On AWS, enable the &lt;code&gt;Metrics&lt;/code&gt; and &lt;code&gt;ReplicationTime&lt;/code&gt; blocks inside the replication rule; only then does AWS emit replication-latency CloudWatch metrics you can alarm on. Without those blocks, lag is not exposed by default. RustFS exposes per-site replication status through &lt;code&gt;rc admin replicate status site1 --metrics&lt;/code&gt;, which reports backlog and metrics from each deployment.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://blog.rustfs.com/s3-cross-region-replication/" rel="noopener noreferrer"&gt;RustFS blog&lt;/a&gt;. If you want S3-compatible replication without the per-GB cloud egress, &lt;a href="https://rustfs.com" rel="noopener noreferrer"&gt;RustFS&lt;/a&gt; is open source under Apache 2.0 and links regions through site replication. &lt;a href="https://docs.rustfs.com/en/operations/high-availability/site-replication" rel="noopener noreferrer"&gt;read the docs&lt;/a&gt; or &lt;a href="https://rustfs.com" rel="noopener noreferrer"&gt;download&lt;/a&gt; to try it.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>s3</category>
      <category>replication</category>
      <category>crossregion</category>
      <category>disasterrecovery</category>
    </item>
    <item>
      <title>Backblaze B2 vs Self-Hosted S3: Which Saves More Money?</title>
      <dc:creator>Ethan Carter</dc:creator>
      <pubDate>Sun, 16 Aug 2026 13:21:00 +0000</pubDate>
      <link>https://dev.to/ethan-carter/backblaze-b2-vs-self-hosted-s3-which-saves-more-money-4gp8</link>
      <guid>https://dev.to/ethan-carter/backblaze-b2-vs-self-hosted-s3-which-saves-more-money-4gp8</guid>
      <description>&lt;h1&gt;
  
  
  Backblaze B2 vs Self-Hosted S3: Which Actually Saves More Money in 2026?
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Backblaze B2's pricing is about as simple as cloud storage gets: $0.00695/GB/month (~$6.95/TB/mo) for storage, no charge for uploads, and free egress up to 3× your stored capacity (then $0.01/GB).&lt;/strong&gt; No storage tiers, no retrieval classes, no egress spreadsheet. For backups, archives, and media libraries that combination tends to be the practical choice.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Simple pricing and low total cost are not the same thing once your data passes a certain size. This article compares where B2 wins, where self-hosted S3 (&lt;a href="https://rustfs.com" rel="noopener noreferrer"&gt;RustFS&lt;/a&gt;, MinIO) wins, and the data volume where the two cross over.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Stats (Backblaze figures verified 2026-08-04)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Figure&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;B2 pay-as-you-go storage&lt;/td&gt;
&lt;td&gt;$6.95 / TB / month ($0.00695/GB)&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.backblaze.com/cloud-storage/pricing" rel="noopener noreferrer"&gt;Backblaze pricing&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;B2 egress&lt;/td&gt;
&lt;td&gt;Free up to 3× monthly storage, then $0.01/GB&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.backblaze.com/cloud-storage/pricing" rel="noopener noreferrer"&gt;Backblaze pricing&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Always-free tier&lt;/td&gt;
&lt;td&gt;First 10 GB of storage&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.backblaze.com/cloud-storage/pricing" rel="noopener noreferrer"&gt;Backblaze pricing&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Transactions&lt;/td&gt;
&lt;td&gt;Class A/B/C free; Class D $0.004 per 10,000 calls&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.backblaze.com/cloud-storage/pricing" rel="noopener noreferrer"&gt;Backblaze pricing&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;B2 regions&lt;/td&gt;
&lt;td&gt;4 — US West, US East, EU Central, CA East&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.backblaze.com/computer-backup/docs/data-centers-and-data-regions" rel="noopener noreferrer"&gt;Backblaze docs&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Self-hosted crossover&lt;/td&gt;
&lt;td&gt;~7 TB (our cost model, assumptions below)&lt;/td&gt;
&lt;td&gt;This article&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Backblaze B2: What You Get for $0.00695/GB
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Included?&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;S3-Compatible API&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;"S3 Compatible API" — mostly core operations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage&lt;/td&gt;
&lt;td&gt;$0.00695/GB/month (~$6.95/TB/mo)&lt;/td&gt;
&lt;td&gt;~70% cheaper than AWS S3 Standard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Uploads (Class A)&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;No ingress fee&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Downloads (Class B)&lt;/td&gt;
&lt;td&gt;Free up to 3× storage/mo, then $0.01/GB&lt;/td&gt;
&lt;td&gt;Generous egress, no per-GB surprise under 3×&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Minimum commitment&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Pay for what you use&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data durability&lt;/td&gt;
&lt;td&gt;11 nines claimed&lt;/td&gt;
&lt;td&gt;Standard for industry&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Presigned URLs&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;Downloads and uploads; browser POST uploads not supported&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Server-side encryption&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;SSE-B2 (Backblaze-managed) or SSE-C (customer-managed)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API transactions&lt;/td&gt;
&lt;td&gt;Free (Class A/B/C)&lt;/td&gt;
&lt;td&gt;Class D $0.004 per 10,000 calls; first 2,500/day free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multipart Upload&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lifecycle rules&lt;/td&gt;
&lt;td&gt;⚠️ Basic&lt;/td&gt;
&lt;td&gt;Hide/delete only, no tier transitions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Object Lock / WORM&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;Governance + compliance retention, legal hold, bucket default retention&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cross-region replication&lt;/td&gt;
&lt;td&gt;⚠️ Cloud Replication&lt;/td&gt;
&lt;td&gt;Within or between regions, but a cross-region target needs a second account; Native API/console only, 2 rules per bucket&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ACLs / IAM roles / object tagging&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;Officially listed as unsupported in the S3-Compatible API&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Where B2 shines:&lt;/strong&gt; Backups, archives, media asset libraries, and disaster-recovery targets. These are workloads where you write once, read occasionally, and want a bill you can predict from one line item.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where B2 struggles:&lt;/strong&gt; Heavy egress above 3× your stored capacity, where the $0.01/GB overage applies. Also any workload that needs lifecycle tier transitions, object tagging, ACLs, or IAM roles. Backblaze lists each of those as unsupported in its S3-Compatible API, and B2 lifecycle rules only hide, delete, or cancel unfinished large uploads.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Crossover Math: When Self-Hosted Wins
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Monthly Data Stored&lt;/th&gt;
&lt;th&gt;Backblaze B2&lt;/th&gt;
&lt;th&gt;Self-Hosted (RustFS)&lt;/th&gt;
&lt;th&gt;Winner&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1 TB&lt;/td&gt;
&lt;td&gt;$6.95&lt;/td&gt;
&lt;td&gt;~$38 (hardware amortized + ops)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;B2&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10 TB&lt;/td&gt;
&lt;td&gt;$69.50&lt;/td&gt;
&lt;td&gt;~$53&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;RustFS (-24%)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;50 TB&lt;/td&gt;
&lt;td&gt;$347.50&lt;/td&gt;
&lt;td&gt;~$142&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;RustFS (-59%)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;100 TB&lt;/td&gt;
&lt;td&gt;$695&lt;/td&gt;
&lt;td&gt;~$253&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;RustFS (-64%)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;500 TB&lt;/td&gt;
&lt;td&gt;$3,475&lt;/td&gt;
&lt;td&gt;~$917&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;RustFS (-74%)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Assumptions: B2 egress within the free 3×-storage tier (so B2 cost ≈ storage cost). Self-hosted hardware amortized over 36 months. Ops cost = 0.15 FTE allocation.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For a typical backup or archive workload the crossover lands near 7 TB, inside the 5–10 TB band. Below it, B2's convenience usually wins on price and on not having to run anything. Above it, self-hosted pulls clearly ahead on cost.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Hidden Factor: Download Costs
&lt;/h2&gt;

&lt;p&gt;Backblaze changed its pricing model: egress is now free up to 3× your average monthly storage, and free beyond that through CDN or compute partners. Only egress above 3× storage is billed, at $0.01/GB. For most backup and archive workloads that makes download cost effectively zero, a real change from the old per-GB download charges.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Monthly Egress Cost (B2)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1 TB stored, 10% egress/mo (100 GB)&lt;/td&gt;
&lt;td&gt;$0 (within 3× free tier)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10 TB stored, 25% egress/mo (2.5 TB)&lt;/td&gt;
&lt;td&gt;$0 (within 3× free tier)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;50 TB stored, 50% egress/mo (25 TB)&lt;/td&gt;
&lt;td&gt;$0 (within 3× free tier)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;100 TB stored, 400% egress/mo (400 TB)&lt;/td&gt;
&lt;td&gt;$1,000 (100 TB overage × $0.01)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Low-egress backup and archive workloads pay nothing for B2 downloads. Heavy egress (a CDN origin, ML training pulls, analytics above 3× storage per month) starts to cost. Self-hosted S3 stays at $0 either way, because the bandwidth is yours.&lt;/p&gt;

&lt;p&gt;Self-hosted S3: &lt;strong&gt;download cost = $0&lt;/strong&gt; (it's your network, your bandwidth).&lt;/p&gt;

&lt;h2&gt;
  
  
  Decision Framework
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Choose Backblaze B2 If:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;✅ You store &lt;strong&gt;&amp;lt; 10TB&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;✅ Primary workload is &lt;strong&gt;backup/archive&lt;/strong&gt; (write-once, read-rarely)&lt;/li&gt;
&lt;li&gt;✅ You want &lt;strong&gt;zero operational responsibility&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;✅ Download ratio is &lt;strong&gt;&amp;lt; 20%&lt;/strong&gt; of stored data monthly&lt;/li&gt;
&lt;li&gt;✅ You don't need lifecycle tier transitions, object tagging, ACLs, or IAM roles&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Choose Self-Hosted S3 (RustFS/MinIO) If:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;✅ You store &lt;strong&gt;&amp;gt; 10TB&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;✅ Workload is &lt;strong&gt;active&lt;/strong&gt; (frequent reads AND writes)&lt;/li&gt;
&lt;li&gt;✅ You want &lt;strong&gt;predictable $0 egress at any scale&lt;/strong&gt; (B2's free egress caps at 3× storage/mo)&lt;/li&gt;
&lt;li&gt;✅ You have &lt;strong&gt;infra capacity&lt;/strong&gt; or want to build it&lt;/li&gt;
&lt;li&gt;✅ You need &lt;strong&gt;S3 surface B2 doesn't expose&lt;/strong&gt; (object tagging, ACLs, IAM roles, lifecycle transitions)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Choose Both (Hybrid):
&lt;/h3&gt;

&lt;p&gt;A lot of teams keep B2 as their off-site cold target and run self-hosted S3 for hot, active data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Write → Self-hosted S3 (hot, fast, zero egress)
       │
       └── Lifecycle copy → B2 (cold archive, off-site)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The S3 API sits on both sides, so the same tooling moves data either way. The two tiers just carry different cost profiles.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;B2 has the simplest cloud storage pricing&lt;/strong&gt; ($0.00695/GB store, free egress up to 3× storage, then $0.01/GB). Strong fit for backups and archives.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Self-hosted S3 becomes cheaper around 7TB&lt;/strong&gt; in our model, depending on download activity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;B2 egress is now free up to 3× storage&lt;/strong&gt; — only heavy egress (&amp;gt;3×) costs; self-hosted is always $0.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;B2 does support Object Lock&lt;/strong&gt; (governance/compliance + legal hold) and bucket replication, but not object tagging, ACLs, IAM roles, or lifecycle tier transitions. Check your requirements against the list above.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hybrid is common:&lt;/strong&gt; self-hosted for hot data, B2 for cold archive. The shared S3 API makes the split workable.&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;&lt;em&gt;&lt;a href="https://rustfs.com" rel="noopener noreferrer"&gt;RustFS&lt;/a&gt; — Apache 2.0, S3-compatible object storage you run yourself, with bucket replication, versioning, and event notifications available today (lifecycle management is still marked under testing). &lt;a href="https://rustfs.com/download" rel="noopener noreferrer"&gt;Download here&lt;/a&gt; or read the code on &lt;a href="https://github.com/rustfs/rustfs" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Is Backblaze B2 really S3-compatible?
&lt;/h3&gt;

&lt;p&gt;Mostly. B2's S3-Compatible API covers the core operations (PutObject, GetObject, ListObjects, Multipart Upload, DeleteObject), so S3-aware tools like rclone, restic, and Cyberduck work against it unchanged. Presigned URLs work for both downloads and uploads, and server-side encryption is supported via SSE-B2 or SSE-C. Backblaze officially lists five gaps: ACLs, IAM roles, object tagging, website configuration, and browser-based POST uploads to presigned URLs. Cloud Replication is also absent from the S3 API. It runs through the Native API or the web console instead.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is Backblaze B2 cheaper than AWS S3?
&lt;/h3&gt;

&lt;p&gt;On storage, clearly: $0.00695/GB against $0.023/GB for S3 Standard, about 70% less. Egress widens it further. B2 is free up to 3× your monthly storage and $0.01/GB above that, while AWS charges $0.09/GB after its free allowance. The catch is reach and feature surface. B2 runs 4 regions (US West, US East, EU Central, CA East) against AWS's 30-plus, and it has no storage classes to transition between. For backup and archive workloads, B2 ends up far below AWS S3 once egress is in the picture. In our cost model that gap runs to roughly 60–80%.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does Backblaze B2 have a free tier?
&lt;/h3&gt;

&lt;p&gt;Yes, and it doesn't expire. The first 10 GB of storage is always free, egress is free up to 3× your monthly storage, and Class A, B, and C transactions are free on pay-as-you-go. Class D calls cost $0.004 per 10,000, with the first 2,500 per day free. That is enough room to run a personal backup, a small-business archive, or a proof of concept without ever generating a bill, and it is more generous than most cloud storage free tiers.&lt;/p&gt;

&lt;h3&gt;
  
  
  When does self-hosted S3 storage become cheaper than Backblaze B2?
&lt;/h3&gt;

&lt;p&gt;Around 7 TB in our model, which amortizes hardware over 36 months and charges 0.15 FTE for operations. At 1 TB, B2 wins outright ($6.95 versus roughly $38 self-hosted). At 10 TB, self-hosted runs about 24% less; at 100 TB, about 64% less; at 500 TB, about 74% less. Egress shifts the line. B2's free 3× allowance covers most backup patterns, but a CDN origin or an ML training set pulling more than 3× storage per month pays $0.01/GB, while self-hosted egress stays at $0.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does Backblaze B2 support Object Lock and cross-region replication?
&lt;/h3&gt;

&lt;p&gt;Both, with caveats. Object Lock is fully supported — governance and compliance retention modes, legal hold, and bucket-level default retention — and lifecycle rules will not delete a version that Object Lock protects. Replication runs through Backblaze Cloud Replication, which copies buckets within or between regions, but a cross-region target requires a second Backblaze account (an account is tied to one region), it allows only two rules per bucket, and it is driven by the Native API or web console rather than the S3 API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;p&gt;Every Backblaze figure and feature claim above was checked against Backblaze's own documentation on 2026-08-04:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Storage price, free egress, free tier, transaction classes — &lt;a href="https://www.backblaze.com/cloud-storage/pricing" rel="noopener noreferrer"&gt;Backblaze B2 pricing&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Object Lock (governance/compliance modes, legal hold, default retention) — &lt;a href="https://www.backblaze.com/docs/cloud-storage-enable-object-lock-with-the-native-api" rel="noopener noreferrer"&gt;Backblaze Object Lock docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Cloud Replication and the cross-region second-account requirement — &lt;a href="https://www.backblaze.com/docs/cloud-storage-cloud-replication" rel="noopener noreferrer"&gt;Backblaze Cloud Replication docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Lifecycle rule actions (hide, delete, cancel unfinished large files) — &lt;a href="https://www.backblaze.com/docs/cloud-storage-lifecycle-rules" rel="noopener noreferrer"&gt;Backblaze Lifecycle Rules docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;S3-Compatible API gaps (ACLs, IAM roles, object tagging, website config, POST uploads) — &lt;a href="https://www.backblaze.com/docs/cloud-storage-s3-compatible-api" rel="noopener noreferrer"&gt;Backblaze S3-Compatible API docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Region list — &lt;a href="https://www.backblaze.com/computer-backup/docs/data-centers-and-data-regions" rel="noopener noreferrer"&gt;Backblaze data centers and regions&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;RustFS license and feature status — &lt;a href="https://github.com/rustfs/rustfs" rel="noopener noreferrer"&gt;RustFS on GitHub&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AWS S3 list prices ($0.023/GB Standard, $0.09/GB egress) are AWS published pricing.&lt;/p&gt;

</description>
      <category>s3</category>
      <category>backblaze</category>
      <category>cost</category>
      <category>selfhosted</category>
    </item>
  </channel>
</rss>
