S3-compatible, actually
“S3-compatible” is printed on every object storage page on the internet. It tells you almost nothing. The interesting question isn’t whether you can PutObject — it’s whether you can ever get all your objects back out and walk away.
That’s the part nobody puts on the marketing page. So let’s talk about it.
The S3 API is the standard, and that’s fine
There’s no ISO committee for object storage. What happened instead is that Amazon shipped an HTTP API in 2006, everyone wrote clients against it, and the API became the de-facto standard by sheer gravity. aws s3, boto3, s3cmd, rclone, mc, the Go and Rust and JS SDKs — they all speak the same dialect. Point them at a different endpoint and they mostly just work.
That’s a genuinely good outcome. A stable wire protocol that a dozen independent implementations target is the thing portability is built on. When someone says “S3-compatible,” they’re promising to honor that contract.
The trouble is the contract is large, and “compatible” is doing a lot of quiet work.
What real compatibility actually means
Anyone can implement GET, PUT, and DELETE on a bucket. That’s table stakes, not compatibility. The features that decide whether you can actually move are the boring ones:
- Clients and signatures. Does SigV4 auth work with the standard SDKs, unmodified, just by swapping the endpoint URL? Or do you need a vendor’s special fork?
- Signed URLs. Pre-signed
GET/PUTURLs are how half the web uploads files. If they’re not implemented the same way, every upload form you wrote breaks on migration. - Versioning. If your app relies on object versions and the new place doesn’t expose them, you don’t just move data — you lose history.
- Lifecycle rules. Expiry, transitions, abort-incomplete-multipart. These encode real business logic. If they don’t transfer, you’re rewriting policy by hand.
- Multipart uploads. Large objects depend on it. Subtle differences in part-size limits or ETag behavior surface as corrupted files at 3 a.m.
Compatibility is the whole surface your code already touches — not the three verbs in the demo.
How portability quietly becomes a hostage situation
Here’s the move. A provider speaks S3 well enough to get you in the door. Then they bolt on proprietary “value-add” features — a bespoke query layer, a special event pipeline, an SDK extension, a console-only lifecycle gizmo — and encourage you to build on them. Each one is genuinely convenient. Each one is also a thread stitching you to that vendor.
You don’t notice until you try to leave. Your buckets are still “S3-compatible,” sure. But your app now depends on three things that only exist there.
And then there’s the real trap: egress fees. Your data went in cheap. Reading it back out to anywhere else costs money per gigabyte. The bigger your dataset grew, the more expensive the exit becomes — which is exactly backwards from how a fair deal should work. Portability you can’t afford to exercise isn’t portability. It’s a polite hostage situation.
Portability you can test, not just trust
We built Kaligon object storage to be boring in the way that matters. It speaks the S3 API. Point any S3 client at the endpoint — aws-cli, rclone, boto3, whatever you already have — and it works. Versioning, lifecycle rules, and signed URLs behave the way the standard says they should, because the standard is the product, not a marketing checkbox.
The part that makes portability real: no egress fees between Kaligon resources in the same region, and a single flat rate for outbound internet egress. Moving data between your own buckets and instances costs nothing. Leaving doesn’t get more expensive as your data grows. Pricing is one flat price per resource — no committed-use tiers quietly raising the cost of changing your mind. You can see it on the pricing page.
So don’t trust us — test us. Run rclone copy against a Kaligon bucket and a competitor’s, both directions, and watch the meter. The whole point of a standard is that you shouldn’t have to take anyone’s word for it.