Kubernetes solved block and file storage a decade ago with the Container Storage Interface — write one driver, plug it into any cluster, get a PersistentVolumeClaim that works the same way on AWS EBS, Ceph, or a bare-metal LVM pool. In September 2022, SIG Storage announced the same fix for buckets: the Container Object Storage Interface, COSI, a BucketClaim you could point at any S3-compatible backend without rewriting a line of application code. It targeted alpha in Kubernetes 1.25 and beta in 1.32. Kubernetes 1.32 shipped in December 2024. As of mid-2026, COSI's v1alpha2 API is still pre-alpha on the project's own main branch, and the newest tagged release — v0.2.2, cut in December 2025 — still ships the older v1alpha1 API. The standard missed its own beta date by a year and a half and hasn't even finished alpha.
That's the number a self-hosted PaaS actually needs before deciding what to build for tenant object storage: don't put a bucket-provisioning primitive on top of COSI yet. Not because the idea is wrong — it's the right abstraction, and the KEP's motivation section still names a real gap — but because the project hasn't cleared the bar it set for itself, and platforms have already shipped working alternatives while it stalled. Here's the dated evidence for that verdict, what COSI actually gets right, and what to build instead in the meantime.
The Timeline COSI's Own Repo Documents
None of this is speculation — it's the project's own release history and its own KEP graduation criteria, both public on GitHub.
| Date | Milestone |
|---|---|
| 2020 | KEP-1979 ("Object Storage Support") filed with SIG Storage |
| Aug 2022 | Alpha targeted for Kubernetes 1.25; v0.1.0 shipped in the original (now-retired) COSI repos |
| Dec 2024 | Beta targeted for Kubernetes 1.32 — the date came and went with no beta release |
| Jan–Feb 2025 | Project consolidates into a single monorepo, restarts versioning at v0.2.0 — still v1alpha1 |
| Dec 8, 2025 | v0.2.2 released — still v1alpha1, the newest tag as of this writing |
| Mid-2026 | v1alpha2 sits pre-alpha on main; the project's own docs warn breaking changes are still expected before it gets a semver tag |
Four years from "we're announcing this" to "the newer API isn't even alpha yet" is a long runway for a spec whose entire pitch is portability you can build on. A self-hosted PaaS choosing its object-storage primitive today is choosing infrastructure it needs to trust for years — and COSI's own commit history is the strongest argument against betting on it right now.
What COSI Actually Gets Right
The stalled timeline doesn't mean the design is bad — it's worth understanding, because the mechanics are the part worth borrowing even before the project itself is ready. COSI defines five Kubernetes resources, split cleanly between the two people who touch object storage on any real platform: the operator who sets policy, and the tenant who just wants a bucket.
BucketClass(cluster-scoped, operator-owned) — like aStorageClass, it names a driver and backend-specific parameters (endpoint, region, retention defaults).BucketClaim(namespaced, tenant-owned) — a tenant asks for a bucket by referencing aBucketClass, the same way aPersistentVolumeClaimreferences aStorageClass.Bucket— the cluster-scoped object the driver creates in response to a claim, mirroring how aPersistentVolumebacks aPersistentVolumeClaim.BucketAccessClass/BucketAccess— the same claim/class split, but for credentials: requesting access mints a KubernetesSecretcontaining S3-compatible keys, endpoint, and bucket name as JSON.
A pod mounts that Secret as a volume or env vars and talks to the bucket with any S3 SDK — the driver underneath is invisible to the workload. That's the real win: an app doesn't know or care whether it's talking to Ceph RGW, MinIO, or Azure Blob.
The catch is driver coverage. Working COSI drivers exist for Ceph/Rook, Scality RING, and Azure Blob, with MinIO integration in progress — real vendors, real code. But the list of drivers that don't exist is more telling: no official driver from AWS for S3 itself, none from Cloudflare for R2, none from Backblaze, Wasabi, or Google Cloud Storage. A "vendor-neutral" standard that most of the S3-compatible object storage market hasn't implemented isn't neutral yet — it's a promising spec with partial coverage, which is a different thing to build a platform primitive on.
The Graduation Bar COSI Set for Itself
KEP-1979 doesn't leave "is this ready" to vibes — it states its own criteria, and the gap between that bar and what's shipped is the clearest evidence for the verdict.
Beta requires: full component implementation, basic end-to-end test coverage, and provisioning metrics. GA requires: stress testing, validated production deployments, and CSI-style conformance certification. As of mid-2026 the project is still shy of its own alpha exit criteria — the v1alpha2 API redesign (KEP PR #4599) hasn't stabilized enough to tag a release, let alone accumulate the e2e coverage beta demands.
Two non-goals make this more than a versioning technicality. First, bucket mutation isn't supported even in the in-progress v1alpha2 — you can create and delete a bucket through COSI, but changing its policy or lifecycle rules after the fact isn't part of the spec yet, so day-2 operations still need a vendor-specific escape hatch. Second, COSI explicitly declines to standardize the data plane — it hands you a Secret with S3-compatible credentials and stops there; every read/write/list call still goes through a normal S3 SDK, unstandardized by design. Both are reasonable scoping choices, but they mean COSI is solving less of the problem than "CSI for buckets" implies, and it's solving even that smaller problem slower than its own KEP predicted.
What Hosted PaaS Platforms Shipped While COSI Stalled
The gap didn't stay empty. Two platforms picked opposite ends of the build-vs-borrow spectrum, and the contrast is the most useful data point for a self-hosted PaaS deciding what to do right now.
Railway shipped a proprietary version of exactly what COSI promises. Its Storage Buckets feature is genuinely self-serve: click "Bucket" on the project canvas, pick a region, and Railway provisions an isolated, S3-compatible bucket per environment. Credentials show up automatically as env vars matched to common SDK conventions (ACCESS_KEY_ID, SECRET_ACCESS_KEY, BUCKET, ENDPOINT, REGION), so libraries like the AWS SDK, boto3, or Laravel's filesystem driver auto-detect them with zero manual wiring. Pricing undercuts AWS S3 at $0.015/GB-month with unlimited free API calls and bucket-to-bucket egress. It's a good product — and it's also a closed, Railway-only bucket API. A tenant's BucketClaim-equivalent only works on Railway; there's no portability story at all, because Railway never had to solve for it.
Fly.io did the opposite: nothing first-class, just plumbing. There's no bucket-provisioning UI or API — you bring your own S3-compatible bucket from any provider and wire it in with fly secrets set AWS_ACCESS_KEY_ID=... AWS_SECRET_ACCESS_KEY=... BUCKET_NAME=... AWS_ENDPOINT_URL_S3=.... No abstraction, no driver layer, no lock-in beyond whatever bucket provider you picked yourself.
| COSI | Railway Storage Buckets | Fly.io BYO-bucket | |
|---|---|---|---|
| Maturity | Pre-alpha (v1alpha2) | GA, production | GA, production |
| Portability | Designed for it, undelivered | None — Railway-only | Full — any S3-compatible provider |
| Self-serve provisioning | Yes, in spec | Yes, shipped | No — tenant brings the bucket |
| Openness | Open standard, open drivers | Closed, single-vendor | Open — just env vars |
| What a workload sees | S3-compatible Secret | S3-compatible env vars | S3-compatible env vars |
The mechanism a workload actually sees — an S3-compatible endpoint plus a key pair — is identical in all three rows. The difference is entirely in who provisions it and how portable that provisioning is, and on that axis Fly's minimal approach and COSI's stalled spec end up in the same place: no platform-owned bucket, no platform lock-in.
The Decision for a Self-Hosted PaaS Today
That convergence is the actual answer. A platform choosing its object-storage story has three real options, and the evidence above resolves the choice:
- Wait for COSI and build
BucketClaimsupport once it's ready. Correct destination, wrong timing — "ready" isn't a date on a roadmap, it's a stablev1alpha2tag plus the beta criteria the KEP itself defines (full component implementation, e2e coverage, provisioning metrics). Building against a pre-alpha CRD schema means absorbing breaking changes with zero notice, for a spec that still won't cover bucket mutation or the data plane once it lands. - Build a proprietary bucket primitive now, Railway-style. Fast to ship, good UX — and a managed data service with exactly the lock-in and operational surface a git-push-to-owned-machines platform is supposed to avoid. Provisioning and rotating buckets on tenants' behalf means owning backups, encryption-at-rest defaults, and a lifecycle-policy API surface that has nothing to do with running containers.
- BYO-bucket via secret injection — Fly's pattern, available today. A tenant provisions a bucket wherever they like (Hetzner Object Storage, R2, Backblaze, self-hosted MinIO/Garage/SeaweedFS) and injects the resulting S3-compatible credentials as secrets, same as every other environment variable. No new CRD, no driver to maintain, no data plane to operate — just the credential-injection path a platform already has.
Option 3 is the right one right now, and it's not a compromise so much as the same principle bex already applies to managed databases: the platform's job is to run the tenant's app on owned machines, not to become the operator of every stateful backing service that app happens to need. A bex.yml that lets a service declare envFrom: secretRef for S3-compatible credentials gets a tenant to a working bucket today, with zero lock-in, using infrastructure that already exists.
The revisit trigger is concrete, not a vague "check back later": once COSI ships a stable v1alpha2 tag and clears its own beta bar — full component implementation and real e2e coverage, not just the alpha-level API review it has today — a BucketClaim-shaped CRD becomes worth adding as a convenience layer on top of the same BYO-credential foundation, not a replacement for it. Until then, the standard that was supposed to make this decision easy is itself the reason to keep it simple.
Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own. Star the repo on GitHub or deploy your first app today.
Sources
- Introducing COSI: Object Storage Management using Kubernetes APIs
- kubernetes-sigs/container-object-storage-interface (GitHub)
- container-object-storage-interface releases
- KEP-1979: Object Storage Support
- Spotlight on SIG Storage (2026)
- ceph/ceph-cosi driver
- Scality COSI driver
- Railway Storage Buckets docs
- Fly.io: Add Object Storage



