The Best MinIO Hosting and S3 Alternatives in 2026
Almost every team arrives at object storage the same way. Someone needs a place to put user avatars, or build artifacts, or last night's database dump, and the answer is an S3 bucket. Then the egress line gets noticed, or a compliance questionnaire arrives, or a developer tires of needing AWS credentials to run the test suite, and the question becomes: could we just run MinIO? That question has a good answer and a very bad answer, and which one you get depends on what the bucket is for.
I'm Ajay, I build PandaStack, and I should declare the conflict of interest by admitting we do not have one — we do not sell managed object storage. We run managed PostgreSQL 16 and we host apps, sandboxes and serverless functions. So I can say the thing vendors here generally will not: object storage is the canonical example of a workload you should almost never self-host for production durability. A MinIO node is a stateful, disk-bound service where the failure mode is losing customer data permanently, not restarting a process. What we host is the other half — the thing that reads and writes the bucket. More on that at the end.
What you are actually buying
Strip the marketing away and object storage is three things: a flat key-value namespace where values can be very large, an HTTP API with a specific auth scheme, and a durability promise underwritten by somebody replicating your bytes across failure domains. The first two are easy. The third is the entire product, and it is the one you cannot fake with a single machine. The S3 API became a de facto standard the way SQL did, so the question is never which API — it is who holds the disks, and what happens when one dies at 3am.
The two honest reasons to self-host MinIO
The first is local development and CI fixtures: you want the S3 API without an AWS account, without credentials in a test runner, without a shared staging bucket four engineers keep clobbering. MinIO in a container beside your test suite is a real S3 endpoint that starts in a second and tears down with the job. This is what most people who love MinIO are actually loving — the data is disposable by construction, so durability never comes up. The second is on-premises, air-gapped or hard data-residency requirements: if the bytes may not leave a jurisdiction, no managed service is an option, and the move is to budget for MinIO as the multi-node storage system it is.
Then the bad reason, which is the common one: you self-hosted because S3 felt expensive, you ran a single node with a single disk, and four years later that node holds every file your customers have ever uploaded. It has never been restored from. It may never have been backed up. It is simultaneously the most valuable and least protected object in your infrastructure. I have seen this in production more than once, and the owners are always slightly surprised to hear it described out loud.
# DEV AND CI ONLY. Single node, single drive, no erasure coding: any disk
# fault here is unrecoverable data loss. Nothing you would miss goes behind
# this command.
docker run --rm -p 9000:9000 -p 9001:9001 \
-e MINIO_ROOT_USER=minioadmin \
-e MINIO_ROOT_PASSWORD=minioadmin \
-v /tmp/minio-dev-data:/data \
quay.io/minio/minio server /data --console-address ":9001"
# Point the AWS CLI at it. The region value is arbitrary but the SDKs insist
# on having one, so everybody types us-east-1 and moves on.
export AWS_ACCESS_KEY_ID=minioadmin
export AWS_SECRET_ACCESS_KEY=minioadmin
export AWS_DEFAULT_REGION=us-east-1
aws --endpoint-url http://localhost:9000 s3 mb s3://uploads
aws --endpoint-url http://localhost:9000 s3 cp ./fixture.zip s3://uploads/
# Tear it down with the job. The whole point of this configuration is that
# losing the data is a feature.Erasure coding, and why one node is not a deployment
MinIO's durability mechanism is erasure coding, and it is good: objects are split into data and parity shards across drives, so a set can lose some number of drives and still reconstruct every object. You only get that when you deploy the thing as designed — a multi-node, multi-drive server pool, drives presented to MinIO directly rather than through a RAID controller, ideally in one low-latency segment because writes are synchronous across the set. The parity level decides how many drives can fail before the set goes read-only, and it costs usable capacity. Adding capacity later means another pool, not another disk. What almost everybody actually runs is minio server /data on one VM with one filesystem: no erasure coding, no parity, no healing, just a Go process writing files into a directory.
Egress is why most migrations happen
The reason a competitive market exists downstream of S3 is not storage price, which is broadly comparable across serious providers. Egress is — what you pay when bytes leave the provider's network — and it scales with your success rather than with your usage of the thing you are paying for. R2 and B2 compete primarily on that axis from different directions; Wasabi answers it with a flat rate and usage policies attached. I am deliberately not quoting figures: they change, and your effective rate depends on request patterns, storage class and retention minimums, so verify current rates against each provider's docs. A bucket storing ten terabytes and serving two hundred gigabytes a month is a storage problem; the reverse is an egress problem. Measure that ratio before you shop.
"S3-compatible" is a spectrum, not a boolean
Every provider here claims S3 compatibility and each means something different by it. The core — PUT, GET, DELETE, LIST, SigV4 — is universal and boring. Beyond that it fragments, and the fragments are exactly the features you discover you needed after you have already migrated.
- Multipart upload — universal in name, variable in the details: part-size minimums, part counts, copying parts from an existing object, and how abandoned uploads get cleaned up. Orphaned parts are a classic invisible line on a storage bill.
- Presigned URLs — nearly always there for GET and PUT, less consistently for POST policy uploads. The signature covers the host, so your SDK's endpoint style must match the server's.
- Conditional writes — If-Match and If-None-Match on PUT, the difference between a safe distributed lock and a hopeful one. Support arrived late and unevenly.
- Versioning and object lock — the mechanism behind ransomware-resistant backups. Availability differs sharply, and object lock is often the single feature that decides your provider.
- Lifecycle rules and storage classes — expiring objects, colder tiers, aborting stale multipart uploads. Semantics vary enough that a rule copied from the AWS docs may silently do nothing.
- Server-side copy, CORS, tagging, ACL semantics and per-request checksums — the long tail where some tool you did not write quietly assumes AWS behaviour.
The SDKs deserve their own warning, because they generate the most confused bug reports. AWS's SDKs are written against AWS, and periodically they start sending new headers or default integrity checksums that third-party stores have not implemented yet: uploads that worked last week fail opaquely, nothing in your code changed, and the fix is a config flag or an SDK pin until the store catches up. Most SDKs also default to virtual-host-style addressing, while self-hosted MinIO on a bare host needs path-style forced on.
The MinIO licensing and direction question
This is genuinely a buying factor, and I want to describe it carefully rather than confidently. MinIO's open-source server has been distributed under the AGPL v3, whose network-use clause makes some legal departments nervous about embedding it in a product you ship or offer as a service. What moved more recently is the boundary between the community edition and the commercial product: management functionality has been progressively concentrated in the paid offering, and the community console was reduced substantially, with admin operations pushed toward the mc command-line client. Several teams I know reacted by pinning an older release, and pinning a storage server is not a strategy anyone should be happy with.
The options
1. Self-hosted MinIO
The most popular S3-compatible server: a single Go binary, straightforward to stand up, with genuinely good erasure coding as a multi-node pool. The mc client is pleasant and the compatibility surface is one of the broadest outside AWS. The costs are the ones any storage system carries — capacity planning, drive replacement, upgrades, monitoring, the backup of the backup — plus the licensing question above. Choose it for dev, CI, on-prem and residency; be deliberate before making it the durable home of irreplaceable production data.
2. Amazon S3
The original, and still the most complete. Every feature in the section above exists here first and behaves canonically: versioning, object lock, replication, lifecycle transitions across many storage classes, event notifications, strong read-after-write consistency, and IAM instead of static key pairs. If your compute is already in AWS the integration story is unmatched. People leave over egress pricing for public content and a request-priced model that is hard to predict — but if your access pattern is mostly internal, that argument is largely theoretical.
3. Cloudflare R2
S3-compatible storage whose defining commercial choice is not charging for egress bandwidth. That single decision reshapes the economics of anything serving bytes to the public: media hosting, download distribution, public datasets, registries. It also sits alongside Workers, so putting logic in front of objects at the edge is a short path rather than an architecture. Check the feature surface against your own list rather than assuming parity with S3, and verify the current operation pricing — a provider that does not bill egress earns on something else.
4. Backblaze B2
A storage-first company with an S3-compatible layer over its own native API and a long public track record of operating drives at scale. The pitch is low storage cost with generous egress, particularly free or discounted through CDN and compute partnerships — a common pairing for cheap bulk storage behind a delivery network. Strong for backups and archives, and it supports object lock, which matters enormously for the ransomware argument below. The trade is a smaller feature surface and fewer regions.
5. Wasabi
Hot cloud storage sold on a deliberately simple model: one storage rate, without the separate charges that make other bills unpredictable. For teams whose pain is forecasting rather than absolute cost, that legibility is the product. The homework is the fine print, because a flat model has to be defended by policy — minimum retention periods and fair-use constraints on egress relative to stored volume are the usual mechanisms. A workload that writes and deletes constantly behaves very differently under a retention minimum than one that writes once and keeps.
6. Garage and SeaweedFS (the open-source alternatives to MinIO)
If you have decided to self-host, MinIO is not the only answer. Garage is a lightweight S3-compatible store built for geo-distributed deployments on unreliable, heterogeneous, high-latency nodes — it replicates whole objects rather than erasure-coding shards, trading storage efficiency for tolerance of the conditions that make clustered storage unhappy. SeaweedFS comes from the other end: an architecture for storing enormous numbers of small files, with an S3 gateway and a filer on top, so fifty million thumbnails suit it better than fifty thousand videos. Ceph's RADOS Gateway is the heavyweight fourth name — right if you already run Ceph, wrong if you would adopt Ceph to get a bucket.
7. PandaStack
Being direct, since this is my company: we do not offer managed object storage. There is no PandaStack bucket, no S3 endpoint, nothing that competes with anything above on storing your bytes. If that is what you came for, one of the six options above is your answer.
What we run is the compute that reads and writes the bucket. A PandaStack app or sandbox is a full Ubuntu userspace in a Firecracker microVM with its own guest kernel, created by restoring a snapshot with a p50 around 179ms. That suits the jobs hanging off object storage that are awkward elsewhere: the thumbnailer shelling out to ffmpeg, the archive extractor that must never see another tenant's files, the untrusted-file parser where a memory-safety bug should cost a microVM rather than a cluster. We use this split ourselves — baked microVM memory snapshots live in object storage and are paged into a booting guest on demand over HTTP range requests, and managed Postgres backups are written there on a schedule. The durable thing is somebody else's storage service; the disposable thing is ours.
# The pattern: a per-job microVM that processes one object and then dies.
# The bucket is durable; this VM is emphatically not, and that is the point.
from pandastack import Sandbox
sbx = Sandbox.create(template="base", ttl_seconds=600)
r = sbx.exec("python process_upload.py --key uploads/abc.zip")
print(r.exit_code, r.stdout)
sbx.destroy()Side by side
- Self-hosted MinIO — Model: S3-compatible server you operate, erasure-coded across a multi-node multi-drive pool. Ops: yours, including drives, upgrades and backups. Best for: local dev, CI fixtures, on-prem and air-gapped, data residency. Check the AGPL and edition boundaries first.
- Amazon S3 — Model: the reference implementation, IAM-native, deepest feature surface — versioning, object lock, replication, lifecycle tiers. Ops: none. Best for: workloads already inside AWS and anything needing the long tail of S3 features. Egress is the line people leave over.
- Cloudflare R2 — Model: S3-compatible storage that does not bill egress bandwidth, with native Workers integration. Ops: none. Best for: serving bytes to the public internet — media, downloads, registries, datasets. Verify operation pricing and API coverage.
- Backblaze B2 — Model: storage-first provider with an S3-compatible layer, low storage cost, generous egress via partner networks, object lock. Ops: none. Best for: backups, archives and bulk storage behind a CDN. Smaller feature surface than the hyperscalers.
- Wasabi — Model: flat hot-storage rate with a deliberately simple bill, defended by policy rather than metering. Ops: none. Best for: teams whose real pain is forecasting rather than absolute cost. Read the retention minimums and egress fair-use terms against your churn.
- Garage / SeaweedFS — Model: open-source S3-compatible stores with distinct designs — Garage replicates whole objects for geo-distributed unreliable nodes, SeaweedFS optimises for enormous counts of small files. Ops: yours. Best for: self-hosters whose shape does not match MinIO's assumptions.
- PandaStack — Model: not object storage; Firecracker microVMs running the jobs that read and write your bucket, plus managed Postgres 16. Ops: yours for the code, ours for the machine. Best for: upload processing, media pipelines and untrusted-file handling beside whichever store you picked.
Presigned uploads, so bytes never touch your app
Whichever provider you pick, do not proxy uploads through your application. Every megabyte a user sends should go from their browser straight to the store, authorised by a short-lived signed URL your backend mints. That removes a whole class of problems: no request-body limits tuned for video, no connection held open for a slow mobile upload, no untrusted bytes buffered in your web process, no scaling instance count against upload concurrency. The mechanics are identical across S3, R2, B2, Wasabi and MinIO — the compatibility promise paying off — and what trips people up on non-AWS endpoints is addressing style and CORS.
import boto3
from botocore.config import Config
# One client shape works against S3, R2, B2, Wasabi or MinIO. Only the
# endpoint and the addressing style change.
s3 = boto3.client(
"s3",
endpoint_url="https://s3.example-provider.com", # omit for real AWS
region_name="us-east-1",
config=Config(
signature_version="s3v4",
# Self-hosted MinIO on a bare host needs path-style. Most managed
# providers prefer virtual-host style. Getting this wrong produces
# SignatureDoesNotMatch and absolutely no useful hint.
s3={"addressing_style": "path"},
),
)
def upload_ticket(key: str, content_type: str) -> dict:
"""Mint a short-lived PUT URL. The browser uploads directly to storage;
these bytes never enter our application process."""
url = s3.generate_presigned_url(
"put_object",
Params={"Bucket": "uploads", "Key": key, "ContentType": content_type},
ExpiresIn=900, # 15 minutes is plenty; hours are a liability
)
return {"url": url, "method": "PUT", "headers": {"Content-Type": content_type}}
# Validation happens after the object lands, not during the upload: read the
# key, check the real content type, scan it, transcode it. Do that in a
# disposable sandbox, not in your web process.A bucket is not a backup of itself
Managed providers replicate your bytes across failure domains, which protects you from hardware. It does not protect you from the two things that actually delete production data: a bug in your own code, and an attacker with your credentials. Replication is extremely good at faithfully replicating a DELETE.
Two layers defend against different things. Versioning plus object lock in the same bucket is the ransomware and fat-finger defence: in compliance mode, for the retention window, nobody — including a key with full administrative rights — can delete or overwrite the object. That is uncomfortable by design, and if your own credentials can undo it then it is not protecting you from those credentials being stolen. The second layer is a copy at a different provider, defending against account suspension or a region-wide event. For self-hosted MinIO that layer is the whole plan — a nightly mirror to a managed provider with versioning on turns a single-node MinIO from a liability into something defensible. Then restore-test it once, with a stopwatch. An untested restore is a belief, not a backup.
How to choose in ten minutes
- Write down stored volume and monthly egress volume as two separate numbers. The ratio decides whether you are shopping on storage price or bandwidth policy, and almost nobody does it before opening pricing pages.
- Decide whether the data is replaceable. Build caches and CI artifacts are; user uploads and financial documents are not. Only the second category earns a durability conversation, and it should never live on one self-hosted disk.
- List the API features you actually depend on — object lock, versioning, lifecycle expiry, conditional writes, POST policy uploads — and check each against the candidate's docs. The phrase S3-compatible does not answer this.
- If self-hosting is on the table, ask which reason applies: dev and CI, residency and air-gap, or S3 looked expensive. The first two are good; the third is how single-node MinIO ends up holding four years of uploads.
- Pick the store, then pick where the jobs that read it run, and keep those separate. Durable storage plus disposable compute is the split that makes everything else easier to change later.
The short version
Run MinIO locally and in CI without a second thought — it is the best S3 endpoint you can start in a second and throw away. Run it on-premises when residency or an air gap leaves no alternative, and deploy the multi-node erasure-coded thing rather than the single-directory thing. For production durability of data customers cannot re-upload, use a managed provider: S3 for the full feature surface, R2 if you serve a lot of bytes to the public internet, B2 for cheap bulk storage with object lock, Wasabi if a predictable flat bill is worth its policy fine print, Garage or SeaweedFS if you are self-hosting anyway but MinIO's shape does not fit.
Then move uploads onto presigned URLs, turn on versioning with object lock, keep a copy at a second provider, and restore-test it once so you know it is real. Put the processing jobs somewhere disposable — that part is ours, and I would tell you the same thing if it were not. The line I would leave you with: "S3-compatible" is a load-bearing adjective doing an enormous amount of quiet work. It tells you the API will fit. It tells you nothing about who is awake when the disk dies.
Frequently asked questions
Is it safe to run MinIO in production?
It depends entirely on how you deploy it. MinIO's erasure coding is real and effective, but you only get it in a multi-node, multi-drive server pool where objects are split into data and parity shards across independent physical drives. That configuration, properly operated with monitoring and a tested restore, is defensible in production. What most teams actually run is a single node with one filesystem, which has no erasure coding, no parity and no healing — a single drive fault loses objects permanently. That is fine for development and CI, where the data is disposable by design, and dangerous as the home of irreplaceable customer uploads that nobody can re-create.
When should I self-host object storage instead of using S3 or R2?
There are two good reasons. The first is local development and CI, where you want a real S3 API without an account, credentials in a test runner, or a shared staging bucket that engineers keep clobbering — MinIO in a container is excellent here and the data is disposable by construction. The second is on-premises, air-gapped or hard data-residency requirements, where a managed service is simply not an option and you must budget for a proper multi-node deployment. The bad reason is cost: if you are self-hosting because egress pricing looked expensive, you are trading a predictable bill for an unbounded durability risk you will not notice until a disk fails.
What does S3-compatible actually mean?
It means the core operations work — PUT, GET, DELETE, LIST and SigV4 authentication — and nothing more specific than that. Beyond the core, support fragments across exactly the features you tend to need later: multipart upload details like part-size minimums and abandoned-upload cleanup, POST policy uploads, conditional writes with If-Match and If-None-Match, versioning, object lock, lifecycle rules and storage-class transitions, server-side copy, CORS and tagging. Addressing style matters too, since most SDKs default to virtual-host-style URLs while self-hosted servers on a bare host need path-style forced on. List the specific calls your tooling makes and check each one against the provider's documentation.
Why do teams migrate off Amazon S3?
Almost always egress, not storage price. Per-gigabyte-per-month storage is broadly comparable across serious providers, but what you pay for bytes leaving the network scales with your success rather than your data volume, and for anyone serving user media, public downloads or customer data exports it can become the majority of the bill. Cloudflare R2 and Backblaze B2 both compete primarily on that axis from different directions, and Wasabi offers a flat model defended by usage policies. Rates change, so verify current pricing against each provider's own documentation. Before shopping, measure stored volume and monthly egress separately — the ratio tells you which number you are actually optimising.
How do I back up an object storage bucket?
Two layers, defending against different failures. First, versioning plus object lock inside the bucket: in compliance mode nobody can delete or overwrite an object during the retention window, including a stolen administrative credential, which is the ransomware and accidental-DELETE defence. Replication alone does not help here, because it faithfully replicates a delete. Second, a copy at a different provider, which defends against account suspension, billing disputes and provider-wide events. For self-hosted MinIO that second layer is not optional — a scheduled mirror to a managed provider with versioning enabled is the difference between a liability and a plan. Then restore-test it once, deliberately, with a stopwatch.
Keep reading
- How to handle file uploads in a deployed app — Presigned URLs end to end, without proxying bytes.
- Processing untrusted uploads in a sandbox — What to do after the object lands in the bucket.
- Per-tenant object storage isolation — Keeping one tenant's credentials away from another's keys.
- Postgres backup, RPO and RTO explained — The same restore-test discipline, applied to a database.
- Serverless functions on PandaStack — Run the job that reads the bucket, then disappear.
49ms p50 cold start. Fork, snapshot, and scale to zero.