Est.

GCS vs S3 vs Azure Blob API Differences That Break Portability

Migration costs compound across authentication, tiering, and event models that don't translate.

Staff Writer · · 11 min read
Cover illustration for “GCS vs S3 vs Azure Blob API Differences That Break Portability”
Object Storage · September 30, 2026 · 11 min read · 2,484 words

S3, GCS, and Azure Blob all do the same basic job: you hand them a file, they store it, they promise not to lose it. Code written against one of them breaks on another in ways that aren't obvious until migration, because they diverge in authentication models, multipart upload mechanics, naming conventions, conditional operations, and metadata handling.

This isn't a rounding error kind of problem, either. The global cloud object storage market grew from $8.14 billion in 2024 to $9.49 billion in 2025, a 16% jump in a single year. At that scale, a bad portability bet doesn't just cost engineering time. It costs real money, and it costs it more than once.

Consider this a map, not a feature comparison. Every section below covers one specific way these three APIs diverge, and one specific way that divergence breaks code that assumed otherwise. A survey of 500+ infrastructure leaders found that the AWS S3 vs. Azure Blob choice intersects directly with multi-cloud portability concerns.

S3's status as the industry baseline

S3 showed up in 2006. It has had two decades to become the thing every other object store gets measured against. Two decades is a long time in software years. Long enough that the API stopped being "AWS's storage product" and started being the language object storage speaks by default.

Look at the ecosystem gravity here. TensorFlow, PyTorch, Apache Spark, Airflow, MLflow, Kubeflow, virtually every major AI and ML framework ships with native S3 support baked in. If a non-S3 store wants any of those tools to work out of the box, it has to speak S3's dialect.

That's where "S3-compatible" gets slippery as a label. Backblaze B2 and DigitalOcean Spaces, among others, each implement some slice of the S3 HTTP API and its signing behavior. Some slice is the key phrase. A 2026 portability analysis from oneuptime.com finds not identical semantics, not full feature coverage, not real operational interchangeability. So a client library will happily list a bucket and upload a file, look for all the world like it's working, and then fall over the moment it hits a lifecycle rule or a notification config the target store never implemented. Partial success is the trap. It's like a rental car with the same dashboard as your car at home, until you reach for the turn signal and hit the wipers instead.

Where GCS diverges from S3 at the API level

Google built an actual bridge here, an S3 interoperability mode through its XML API, with documentation spelling out where it departs from Amazon's behavior. The partial compatibility is a designed feature, not an accident of convergent evolution.

But bridges have gaps. Some lifecycle configurations simply have no GCS equivalent to map onto. And the fancier S3 features, S3 Select for querying data in place, S3 Object Lambda for transforming objects on read, S3 Batch Operations, none of them have a GCS counterpart anywhere in the compatibility layer. If your code depends on those, the interoperability mode simply doesn't have a shelf for them.

The storage class models don't line up structurally, either. GCS runs a unified API across five classes, Rapid, Standard, Nearline, Coldline, and Archive, while S3 splits things into eight classes with separate behaviors and separate retrieval paths. Code written to exploit S3's class-specific retrieval mechanics is going to behave differently the moment it lands on GCS, because the underlying model just isn't shaped the same way.

Autoclass is GCS's own invention, and it's a genuinely nice one: objects drift between storage tiers automatically based on how often they're actually accessed, no lifecycle rules required. S3 has nothing like it.

Then there's the sharpest gotcha in the cold-storage world. GCS Archive bills a 365-day minimum storage period; S3 Glacier Deep Archive bills 180 days. If an object is deleted or overwritten before that window closes, the meter doesn't care: you're paying for the full period regardless. That's a substantial discrepancy. That's twice as long a commitment, hiding in a setting most teams configure once and never revisit.

Event notifications split off in their own direction too. Any pipeline wiring bucket events straight into a Lambda handler needs a real rearchitecture to work on GCS.

And pricing moves under your feet. Lifecycle logic built around last year's cost curve is now optimizing for numbers that no longer exist. GCS standard storage runs $0.020/GB, and 2026 pricing shifts are notable: multi-region Nearline rose from $0.010 to $0.015/GB and multi-region Archive was cut from $0.004 to $0.0024/GB (datastorage.com, July 2026), relevant because lifecycle code that assumed old pricing tiers will now hit different cost curves.

Where Azure Blob diverges as the harder migration target

Azure runs its own REST API and its own SDK, full stop. There's no native S3 endpoint sitting underneath it. An S3-only client isn't some universal adapter that quietly works across all three clouds, and Azure is the clearest place that assumption falls apart.

Azure does have an S3 endpoint in the works, but as of mid-2026 it's still in preview. Preview means the semantics can shift under you at any point, so it's not something to build a production migration plan around. Migration tooling built for S3 generally needs real, structural rework to target Azure: a different signing scheme, a different request shape, different error codes coming back.

Azure's storage classes are Hot, Cool, Cold, and Archive. Cool has been a moving target lately: a 128 KiB minimum billable object size was announced then paused in June 2026, and Azure Smart Tier went generally available in April 2026, now automating movement into and out of Cool. The class names, the minimum durations, and the retrieval mechanics differ enough that S3-centric lifecycle code cannot be ported unchanged.

But the path there looks nothing alike underneath. Azure has no S3-style storage class named in the API call. It's a set-tier operation instead, so getting an object into archive and getting it back out follows a structurally different sequence.

Teams that relied on automatic tiering in GCS Autoclass or S3 Intelligent-Tiering must rewrite that logic explicitly, since lifecycle management here requires more manual policy configuration. Event notifications add yet another architecture to track: Azure uses Event Grid, a third distinct model sitting apart from both SNS/SQS/EventBridge on one side and Pub/Sub on the other.

Redundancy is where Azure actually offers more than the other two, in a way that cuts both ways. Code that assumes redundancy comes bundled with class selection, S3-style, needs rethinking before it makes sense on Azure. The redundancy model is the most explicit of the three (LRS, ZRS, GRS, GZRS, RA-GRS, RA-GZRS), the widest menu, but the redundancy choice is a deployment-time configuration, not a storage-class selection as in S3, meaning code that assumes S3's redundancy-via-class approach will need rethinking.

Authentication and credential models across the three providers

Azure Blob runs its own REST API and SDK surface and does not natively expose an S3 endpoint, so an S3-only client is not a universal three-cloud abstraction. S3 uses Signature Version 4, HMAC-SHA256 signing applied to every single request, and it's the reference implementation everyone else gets measured against. GCS splits itself in two: its S3-compatible requests use Google HMAC keys, but native GCS calls run on OAuth 2.0 and service account tokens. That means one provider, two credential models, depending purely on which API surface you happen to be hitting.

Azure adds a third flavor on top: shared key (the account key), Shared Access Signatures, or workload identity through Azure Active Directory, each with its own token lifetime and its own scope rules. None of these three models talk to each other.

The failure mode here is blunt and immediate. An application hardcoded to SigV4 signing cannot authenticate against Azure's native REST API without a genuine rewrite. Same provider, wrong door, no entry.

Oneuptime.com's analysis finds the fix that actually holds up: pull short-lived credentials through each platform's standard credential chain, or route calls through a storage adapter that speaks each SDK's own native model. Never hardcode a credential format and assume it travels. Workload identity exists on all three clouds, technically, but the path to invoking it differs enough that the abstraction is more of an idea than a working shortcut. No credential library out there covers all three natively, so multi-cloud auth means either building a real portability layer or maintaining separate SDK branches per provider. There are three distinct auth surfaces.

Multipart upload mechanics and list operation behavior

S3 and GCS have the most optimized multipart upload paths for large files, handling parallelization and retry logic more gracefully. Azure's equivalent, block blob commit, works in a different order entirely: upload the blocks first, then commit a block list afterward. The API shape is different enough that S3 multipart client code is not something you drop in and expect to run. Part size minimums and maximums differ across providers, so code written around S3's boundaries can throw errors, or worse, silently truncate data, on a different provider.

ETags deserve their own warning label. An S3 ETag lines up with an MD5 digest for some combinations of upload method and encryption, but multipart uploads produce a different value entirely, and GCS and Azure each define their own version of an entity tag. Treating a generic ETag as a content checksum is a mistake waiting to happen. Any deduplication or resumable-upload logic leaning on ETag comparisons across providers will throw false mismatches, so the safer move is calculating and storing an application-level checksum whenever content identity actually matters.

Listing objects has its own quirks at scale. Data pipelines and sync jobs feel this first, since they lean on listing constantly. GCS and Azure also return different response shapes, different continuation token formats, and different maximum page sizes, so pagination code written for S3 needs real adaptation before it works elsewhere.

Conditional operations, metadata handling, and event notification gaps

Conditional writes look similar on paper and diverge in the details. S3 supports conditional PUT and DELETE outright. GCS supports precondition headers in its native API, but that support gets limited once you're going through the S3 interoperability layer instead. Azure supports ETags in conditional headers too, but the semantics differ in edge cases.

Metadata headers are a smaller trap, but a near-universal one. All three providers let you attach user-defined metadata as HTTP headers, but the prefix conventions differ: S3 wants x-amz-meta-, GCS wants x-goog-meta-, Azure wants x-ms-meta-. Any code that builds or parses metadata by matching a specific prefix breaks the instant it hits a different provider.

Event notifications split cleanly into two camps. S3 and GCS (along with compatible stores) share roughly comparable territory here, while Azure's Event Grid model exposes a different event schema and different routing primitives entirely. Anything using bucket events to kick off downstream processing needs a genuine rearchitecture for Azure.

Permissions follow the same pattern of superficial similarity, real structural difference. S3 runs on bucket policies plus IAM, GCS runs its own IAM with a different policy structure, and Azure runs RBAC roles alongside ACLs. Permission logic written as S3 bucket policy JSON has no direct equivalent on either GCS or Azure. Versioning shows the same story in miniature: all three support it, but the calls to turn it on, list versions, and restore a specific one all differ, so backup and audit tooling built around versioning has to branch per provider. S3-specific features with no cross-provider equivalent include S3 Select (query in place), S3 Object Lambda (transform on read), and S3 Batch Operations, and code that depends on these has no migration path without replacing them with separate compute steps.

Egress fees as a structural portability tax

Diagram: The Real Cost of Leaving: Egress Fees Across Three Clouds. Visualizes: Show the financial friction of migrating 100 TB out of each provider before any engineering work begins.

Migration has a cost before a single line of code changes, and it's not small. Moving 100 TB out of AWS S3 runs about $9,000; out of GCS, about $12,000; out of Azure, about $8,700. That's the friction sitting in front of the technical work, not behind it.

Per-gigabyte rates tell a similar story. S3 charges $0.09 per GB to the internet, GCS charges $0.12, Azure charges $0.087. Azure comes out marginally cheapest to leave, GCS the priciest.

Egress numbers end up steering decisions well past the migration itself, including where compute gets placed and the financial case for running multi-cloud. The AI tooling on each cloud tightens that grip further: SageMaker gets its best throughput paired with S3, Vertex AI integrates most tightly with GCS, Azure ML pairs natively with Azure Blob. Moving a large training dataset across clouds stacks the egress bill directly on top of whatever the API rewrite already costs.

Zero-egress storage does exist as an alternative path, and it can cut costs 50 to 80% for heavy, constant egress workloads, like serving media to a global audience. The tradeoff appears in feature depth: that kind of storage typically ships with fewer storage classes, no deep archive tier, and its own take on WORM-style retention rather than anything branded like S3 Object Lock. Cheaper egress, thinner feature set. Pick your tradeoff. Storage rates at the standard tier are S3 Standard $0.023/GB (stepping to $0.022 then $0.021 at volume), Azure Blob Hot $0.018/GB, and GCS Standard $0.020/GB (per datastorage.com).

Writing portable storage code: the practical surface to contain

Diagram: Seven Surfaces Where Provider-Specific Code Leaks In. Visualizes: Visualize the seven discrete points in a storage integration where provider-specific logic escapes into application code: authentication, multipart upload, list pagination…

Seven places, that's where provider-specific code actually leaks in: authentication, multipart upload, list pagination, metadata key prefixes, event notifications, conditional operations, and storage-class lifecycle logic. Contain those seven and the rest of the application barely notices which cloud it's talking to.

The strategy that holds up in practice is an abstraction layer. Wrap every provider SDK call behind an internal storage interface that speaks in plain application terms, put, get, list, delete, copy, and keep each provider's actual SDK tucked inside its own adapter. That confines all the provider-specific branching to one place instead of scattering it through the whole codebase.

Credentials follow the same logic. Never hardcode SigV4 signing or any other provider-specific credential format directly into application code; pull short-lived tokens through each provider's own standard credential chain instead, which is the posture oneuptime.com's 2026 analysis recommends. Checksums need the same discipline: calculate and store an application-owned SHA-256 or MD5 at write time, and never lean on ETag as a stand-in for content identity across providers. Strip provider-specific metadata prefixes at the adapter boundary too, so the rest of the system only ever sees one canonical format regardless of which cloud the bytes actually live on.

Azure Blob runs its own REST API and SDK surface and does not natively expose an S3 endpoint, so an S3-only client is not a universal three-cloud abstraction. They were never going to be. But an application that boxes in these seven surfaces stops caring which cloud is underneath it, and that's the only version of portability that actually survives contact with a real migration. For lifecycle and tiering, treat automatic tiering, such as S3 Intelligent-Tiering and GCS Auto.

Sources

  1. S3 vs Azure Blob vs Google Cloud Storage (2026)
  2. AWS vs Azure vs Google Cloud Object Storage: The 2026 Pricing Comparison - Data Storage
  3. Object Storage Comparison: AWS S3 vs Azure Blob vs GCP vs Cloudflare R2 (2026) — blobstreaming
  4. Best S3-Compatible Object Storage Providers in 2026 - Tested & Ranked | Mixpeek
  5. Amazon S3 vs Google Cloud Storage vs Azure Blob: Which is Best?
  6. What S3 Compatibility Does Not Guarantee
Filed underObject Storage

More in Object Storage