AWS vs. Azure vs. GCP: A Platform Engineer's Cold-Blooded Comparison

Sources

Most cloud comparisons are written by sales teams or "cloud architects" who focus on feature checklists and pricing tiers. For a Platform Engineer, that's irrelevant. We don't care if a provider has 200+ services; we care about the API consistency, the K8s control plane experience, and the cognitive load of the IAM model. Since this guide first ran, we've kept getting the same feedback: the framing is right, but the numbers are missing. So this edition adds them — list prices checked against the official pricing pages (date-stamped as of Sep 2026), a worked monthly-cost example with the math shown, and the outage history that should inform your region strategy. All prices are on-demand list prices in us-east-1, eastus, and us-central1; no reservations, no committed-use discounts, no enterprise negotiation.

The Technical Comparison Matrix

When evaluating a cloud provider, you aren't choosing a set of tools—you're choosing the "OS" for your infrastructure. Here is how they actually compare on the vectors that matter to SREs.

Vector AWS Azure GCP
K8s Experience EKS: Powerful but manual. Historically high overhead (though Karpenter helps). AKS: Strong AD integration, but sometimes inconsistent API stability. GKE: The gold standard. Autopilot and native integration make it the lowest friction.
IAM Complexity High: Granular, powerful, but a nightmare to audit at scale. Medium: Centered around Entra ID (AD). Great for corporate, clunky for pure cloud. Low: Project-based hierarchy is intuitive and easier to reason about.
API Ergonomics Inconsistent. Legacy services feel like 2006; newer ones are modern. CLI is a beast. Heavy. The Azure Resource Manager (ARM) is powerful but often feels slow and verbose. Clean. GCP's APIs are generally the most consistent and developer-friendly.
Lock-in Surface High. Once you use DynamoDB and Lambda, moving is a total rewrite. Medium. Strong tie-in to the Microsoft ecosystem (O365, AD). Medium. BigQuery is a huge "gravity well" that makes leaving hard.
Egress Pricing (first paid tier, as of Sep 2026) $0.09/GB up to 10 TB/mo, first 100 GB free. $0.087/GB for the next 10 TB/mo, first 100 GB free, drops to $0.083 after. Premium Tier: $0.12/GiB up to 1 TiB, then $0.11; Standard Tier starts at $0.085/GiB.
K8s Control-Plane Fee (as of Sep 2026) EKS: $0.10/cluster-hour ($73/mo), plus per-node compute. AKS Standard: $73/mo, plus a per-vCPU management charge on VM node pools. GKE: $0.10/cluster-hour ($73/mo), one zonal or Autopilot cluster free per billing account.

The Cost Model Deep-Dive: Egress Is the Real Differentiator

Nobody picks a cloud on egress pricing, which is strange, because egress is one of the few line items that scales with your product's success rather than your engineering discipline. All three providers bill internet egress in declining tiers, and all three give you a token free allowance — 100 GB/month on AWS and Azure, 1 GiB on GCP Premium Tier (200 GiB on Standard Tier). Here are the list-price tiers (as of Sep 2026):

Monthly volume AWS (us-east-1) Azure (from North America/Europe) GCP Premium (us-central1 → North America) GCP Standard Tier
First paid tier Up to 10 TB at $0.09/GB Next 10 TB at $0.087/GB 1–1,024 GiB at $0.12/GiB 200–10,240 GiB at $0.085/GiB
Second tier Next 40 TB at $0.085/GB Next 40 TB at $0.083/GB 1,024–10,240 GiB at $0.11/GiB 10,240–153,600 GiB at $0.065/GiB
Third tier Next 100 TB at $0.07/GB Next 100 TB at $0.07/GB Above 10,240 GiB at $0.08/GiB Above 153,600 GiB at $0.045/GiB

Three details the pricing pages bury. First, units differ: AWS and Azure bill per decimal GB, GCP per GiB — at 5 TB/month that's a ~7% unit difference before rates even enter the picture. Second, GCP's Premium Tier (Google's backbone, the default) is more expensive than AWS at low volumes: $0.12/GiB up to 1 TiB vs. AWS's flat $0.09/GB. GCP's Standard Tier — which routes over the public internet with a lower SLA — is the one that undercuts AWS. Third, both AWS and Azure eventually bottom out at $0.05/GB — AWS past 150 TB/month, Azure in its 350 TB tier — but those discounts start past the volume most platforms ever see; at the first paid tier, Azure's edge over AWS is 3.3%; most mid-size platforms never leave the first paid tier, where Azure's edge over AWS is 3.3% and GCP Premium is ~18% worse than AWS.

The other metered network services follow the same "nickel-and-dime the data path" pattern. NAT gateways, which you need for private subnets to reach the internet, are priced remarkably similarly — and add up remarkably fast:

Component AWS NAT Gateway Azure NAT Gateway GCP Cloud NAT
Hourly $0.045/hr per gateway (per AZ for regional NAT) $0.045/hr; StandardV2 SKU is zone-redundant at the same price $0.0014/hr per VM, capped at 32 VMs ($0.044/hr flat above that)
Data processing $0.045/GB $0.045/GB $0.045/GiB
Extra Public IPv4: $0.005/hr per IP (since Feb 2024) Standard public IP: $0.005/hr External IPs used by the gateway: $0.005/hr each; unused static IPs billed at a higher rate

Load balancers diverge more. AWS bills an hourly charge plus capacity units (ALB: $0.0225/hr + $0.008/LCU-hour). Azure's Standard Load Balancer is $0.025/hr for the first 5 rules plus $0.005/GB processed. GCP bundles your first 5 forwarding rules into a single $0.025/hr charge and adds $0.008/GiB processed. For a 2-LB platform moving 2 TB/month through the balancers, that's roughly $49 (AWS), $47 (Azure), and $33 (GCP) — GKE's external Application Load Balancer is genuinely cheaper here, partly because you're charged once for the bundle instead of per balancer.

A Worked Example: The Same Platform, Priced Three Ways

Assume a mid-size platform: 40 nodes of 4 vCPU/16 GB (m5.xlarge on AWS, D4s_v5 on Azure, n2-standard-4 on GCP), 5 TB/month of internet egress, 2 load balancers processing 2 TB/month, NAT processing 5.5 TB/month (outbound plus return traffic), a 3-AZ deployment, one production K8s cluster with an SLA-backed control plane, and a 730-hour month. List prices, as of Sep 2026:

Cost component (monthly) AWS Azure GCP
Compute (40 nodes, 4 vCPU/16 GB) $5,606.40 $5,606.40 $5,670.64
K8s control plane $73.00 (EKS) $1,201.00 (AKS Std $73 + 160 vCPU × $7.05) $73.00 (GKE)
Internet egress (5 TB) $441.00 $426.30 $522.35 Premium / $378.81 Standard Tier
NAT (5.5 TB processed, 3 AZs) $346.05 (3 gateways) $280.35 (1 zone-redundant V2) $269.92 (32-VM cap hit)
Load balancers (2, 2 TB processed) $48.85 (2 × ALB) $46.50 (2 × Standard LB) $33.15 (rule bundle)
Total $6,515.30 $7,560.55 $6,569.06 (Premium) / $6,425.53 (Standard Tier)

The math, so you can check us: compute is 40 × $0.192 × 730 on AWS/Azure (m5.xlarge and D4s_v5 both list at $0.192/hr) and 40 × $0.1942 × 730 on GCP. Egress: AWS is (5,000 − 100) × $0.09; Azure is (5,000 − 100) × $0.087; GCP Premium is 1,023 GiB × $0.12 + 3,632.6 GiB × $0.11 (5 TB = 4,656.61 GiB), and Standard Tier is (4,656.61 − 200) × $0.085. NAT: AWS runs one gateway per AZ for zone redundancy — 3 × $0.045 × 730 = $98.55 hourly plus 5,500 GB × $0.045 = $247.50 processing; Azure's StandardV2 is zone-redundant in a single resource, so $32.85 + $247.50; GCP bills 40 VMs at the 32-VM cap ($0.044 × 730 = $32.12), 2 external IPs ($7.30), and 5,122.27 GiB × $0.045 processing. Load balancers: AWS 2 × $0.0225 × 730 + ~2.74 GB/hr processed × $0.008 × 730; Azure 2 × $0.025 × 730 + 2,000 GB × $0.005; GCP $0.025 × 730 (both rules inside the first-5 bundle) + 1,862.65 GiB × $0.008.

Read the AKS row again, because it's the story here. Azure's pricing page (as of Sep 2026) layers a per-vCPU management charge on top of the VM price for AKS-managed nodes — $7.05/month per General Purpose vCPU. Forty 4-vCPU nodes is 160 vCPUs, or $1,128/month, before you've paid for a single virtual machine. That single line item turns Azure from "roughly at parity" into 16% more expensive than AWS for this shape of platform, and it scales with node count, not with cluster count. If you run wide, flat clusters on AKS, re-price your fleet before your next renewal. Caveats, because we'd rather be precise than dramatic: this excludes storage, databases, reservations/savings plans, CDN offsets (CloudFront egress is materially cheaper than direct egress), and the fact that most enterprises negotiate. A 1-2 NAT-gateway AWS design saves ~$65-98/month but concentrates failure. GCP's Standard Tier saves ~$144/month but rides the public internet. The point isn't the absolute number — it's that identical architectures produce materially different bills, and the differences live in boring network line items.

The Idle-Cost Traps

Every provider has charges that accrue while you sleep. The ones that actually hurt platform teams:

Network Topology: Three Mental Models

You can't design a landing zone until you internalize how each provider models "a network" — because they genuinely disagree.

AWS: accounts are the wall, TGW is the plumbing
  acct-prod           acct-staging
   VPC (regional)      VPC (regional)
        \                 /
         [ Transit Gateway ]--- egress VPC --- internet
         $0.05/hr per attachment + $0.02/GB

Azure: hub-and-spoke is the canonical shape
  mgmt subscription
        |
   hub VNet (firewall, DNS, VPN)
   /       |       \
  spoke   spoke    spoke
  (peering: non-transitive, $0.01/GB per direction)

GCP: the network is global, the project is the wall
  organization
   +-- folder: platform
        +-- project: prod   -- one VPC, subnets in every region
        +-- project: staging
        +-- project: ci

Private access to managed services is where the models fork hardest. AWS PrivateLink gives you an elastic network interface in your VPC per endpoint — $0.01/hr per AZ plus $0.01/GB (tiered down to $0.004/GB past 1 PB); gateway endpoints for S3 and DynamoDB are free. Azure Private Endpoints are per-resource NICs inside your subnet — $0.01/hr plus $0.01/GB processed — and they're the standard answer for storage, databases, and anything else you don't want touching the internet. GCP Private Service Connect endpoints are forwarding rules: $0.01/hr, data charges per the service. Functionally all three converge on the same idea; the difference that matters operationally is that Azure's private endpoints consume subnet address space (plan your CIDRs or you'll re-IP), while AWS's interface endpoints do too but gateway endpoints for S3 don't. For hybrid connectivity, the honest summary: all three sell you the same physical product (MPLS-adjacent private circuits into their regions) with different marketing. AWS Direct Connect, Azure ExpressRoute, and GCP Cloud Interconnect (Dedicated or Partner) all bill port-hours plus per-GB egress through the circuit. Budget-wise, GCP's Partner Interconnect is the cheapest entry point for sub-Gbps needs ($0.054–$0.174/hr by port); AWS and Azure want you to commit to full ports. The topology decision matters more than the vendor: if your on-prem footprint is small, terminate VPNs in the hub and skip circuits entirely until throughput forces the issue.

Deep Dive: The Managed K8s Battle (EKS vs AKS vs GKE)

For platform engineers, the managed Kubernetes service is the "center of gravity." This is where the real operational difference lies.

GKE (The Gold Standard)

GCP basically invented the modern K8s experience. GKE Autopilot is the first real attempt to make K8s "serverless" by managing the nodes for you. The integration with Google's network is seamless, and the upgrade process is the most reliable in the industry. The part nobody mentions: Autopilot bills by pod request — you pay for what your workloads ask for, not for the nodes Google sizes around them, and it enforces minimums (50m CPU / 0.5 GiB per container) so you can't request your way to a $0 cluster.

EKS (The Industrial Powerhouse)

EKS is the most flexible, but it forces you to do more of the heavy lifting. For years, managing node groups was a pain until Karpenter (developed by AWS) changed the game. Karpenter allows for just-in-time node provisioning based on pod requirements, making EKS competitive with GKE's efficiency. EKS Auto Mode (GA December 2024) now bundles Karpenter, storage, and load-balancer management into the control plane — you pay the EC2 price plus a management charge per instance (in AWS's own worked example, $0.172/hr of Auto Mode charges on $1.434/hr of EC2 — a ~12% tax for never touching a node again).

AKS (The Enterprise Bridge)

AKS is the best choice for organizations already deep in the Microsoft ecosystem. The integration with Azure Active Directory (Entra ID) is a massive win for security teams. However, AKS has historically struggled with "strange" outages and API inconsistencies that can break CI/CD pipelines. AKS Automatic is Microsoft's Autopilot-analog — production-ready defaults, managed node pools — and it's pinned to the Standard tier, i.e., the per-vCPU billing model that makes wide clusters expensive.

The K8s Cost Reality: Control Planes, Serverless, and Upgrade Windows

The "$73/month control plane" is folklore at this point, and it's mostly still true — with meaningful exceptions. List prices as of Sep 2026:

Vector EKS AKS GKE
Standard control plane $0.10/cluster-hour (~$73/mo) $73/cluster/month (Standard tier, SLA-backed) $0.10/cluster-hour (~$73/mo)
Free option None. Every cluster bills from hour zero. Free tier: $0, but no SLA and a 1,000-node cap. $74.40/month credit = one free zonal or Autopilot cluster per billing account.
Staying on an old version 14 months of standard support, then extended support bills $0.60/cluster-hour — a 6× penalty. ~12-month GA window (community-aligned); Premium tier at $438/cluster/month buys ~2 years. 14 months of support; the Extended release channel adds +$0.50/cluster-hour during the extended period, up to 24 months total.
Auto-upgrade behavior Never forced — AWS will happily bill you $0.60/hr for being late. AKS auto-upgrades clusters on unsupported versions. GKE upgrades all clusters over time, including ones not enrolled in a release channel.

The philosophical split is in the last row. GKE treats upgrades as a provider responsibility and will move your cluster whether you enrolled in a channel or not. AKS escalates: past end-of-support, Microsoft auto-upgrades you. EKS is the laissez-faire one — your cluster just gets more expensive. If your platform can't absorb an unrequested minor-version bump, you need maintenance windows and PDBs regardless of provider, but AWS at least lets you schedule the disruption on your calendar instead of theirs.

The "serverless K8s" pricing models are genuinely different animals, and the marketing hides it:

IAM in Practice: The CI Pipeline Test

IAM complexity is best measured not in feature lists but in what it takes to do the single most common platform task: give a CI pipeline write access to exactly one object-storage bucket and nothing else. Here's the runnable version on each provider.

AWS: two JSON documents — a trust policy federating your CI's OIDC provider (no long-lived secrets), and a permission policy pinned to one prefix:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
        }
      }
    }
  ]
}
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["s3:PutObject", "s3:GetObject"],
      "Resource": "arn:aws:s3:::platform-monkey-artifacts/ci/*"
    }
  ]
}

Azure: there is no resource-level policy language — you assign a built-in RBAC role ("Storage Blob Data Contributor") scoped down to the container, to an Entra ID service principal. One command, if you already know your ARM resource ID syntax:

az role assignment create \
  --assignee "$SP_OBJECT_ID" \
  --role "Storage Blob Data Contributor" \
  --scope "/subscriptions/$SUB_ID/resourceGroups/rg-platform/providers/Microsoft.Storage/storageAccounts/stplatformmonkey/blobServices/default/containers/artifacts"

GCP: an IAM binding on the bucket, with a CEL-based condition if you want prefix-level restrictions — the binding is a mutation of the bucket's policy document, not a standalone object:

gcloud storage buckets add-iam-policy-binding gs://platform-monkey-artifacts \
  --member="serviceAccount:ci-pusher@platform-monkey.iam.googleapis-alias.example.iam.gserviceaccount.com" \
  --role="roles/storage.objectAdmin" \
  --condition='expression=resource.name.startsWith("projects/_/buckets/platform-monkey-artifacts/objects/ci/"),title=ci-prefix-only'

Now the honest comparison. AWS is the most expressive — the JSON is auditable, diffable, and precise to a single prefix — and the OIDC federation pattern is the cleanest in the industry. The cost is that expressiveness: policies compose through a subtle interaction of identity policies, resource policies, SCPs, and permission boundaries, and "who can touch this bucket" becomes a multi-document archaeology project at scale. Azure is a one-liner, but the role is coarse (contributor on the container, not write-to-one-prefix), role assignments take minutes to propagate, and the real policy boundary is the scope string — a typo in a resource ID fails open more often than you'd like. GCP splits the difference: the project hierarchy makes "who is in scope" obvious, conditions give prefix-level control, but the default project roles (editor, legacy owner grants) hand out far more than CI needs, and deleting bindings requires editing a policy document that other automation may be mutating concurrently.

The "Exit Strategy" Analysis

The most important question for a platform lead is: \"How hard is it to leave?\"

The lock-in isn't the VM; it's the Proprietary Data Plane. If you use AWS S3, you can move to any S3-compatible storage. If you use AWS DynamoDB, you are locked into a proprietary API. The same applies to GCP's BigQuery and Azure's CosmosDB. The Platform Monkey Strategy: To maintain sovereignty, use a "Cloud-Agnostic" abstraction layer. Use Crossplane to define your resources and Kubernetes as your universal control plane. By defining a XDatabase resource rather than an RDSInstance, you make the cloud provider an implementation detail rather than a permanent anchor. We've covered the full mechanics — including where Crossplane's Upjet layer lags Terraform providers — in Crossplane vs. Terraform: state files vs. control planes. One caveat we've grown into after watching teams actually attempt exits: egress pricing is the exit tax. Moving 500 TB of S3 data to another cloud at $0.05-0.09/GB costs $25,000-45,000 in egress fees alone — which is precisely why AWS introduced free egress for customers moving off the platform. Any exit strategy that ignores the data-movement bill is theater.

Multi-Region Reality: Outage History and Blast Radius

All three providers sell multi-region architectures, and all three have had the kind of incidents that tell you where their real failure domains live. Dates verified against the providers' own post-incident reports:

Incident What broke Platform lesson
AWS — Nov 25, 2020 (Kinesis post-event summary, us-east-1) A Kinesis front-end server overload cascaded into CloudWatch, Lambda, and anything downstream of them, for hours. One "regional" service with hidden fan-out can take half the region with it.
AWS — Dec 7, 2021 (service event summary, us-east-1 network device overload) A routine scaling activity overloaded network devices; EC2 APIs, the console, STS, Route 53 APIs, and CloudWatch degraded. Even AWS's own status tooling failed. us-east-1 is the largest region and the most common single point of failure on the internet. Diversify or accept the blast radius.
Azure — Jul 21, 2022 (M365/Teams global) A faulty Enterprise Configuration Service deployment cascaded for ~5 hours across global Microsoft services. Azure's control-plane dependencies are shared with M365 — you inherit consumer-cloud failure modes.
Azure — Jan 25, 2023 (global WAN post-incident review) Wide-area networking issues caused cross-region connectivity failures from 07:08 to 12:43 UTC. "Multi-region" on one provider's backbone is one shared network fabric. Region pairs share fates more than the docs imply.
Azure — Mar 23, 2023 (ARM, West Europe, PIR RNQ2-NC8) A code change took down Azure Resource Manager for ~4.5 hours: existing resources ran, but nothing could be deployed or scaled. Control plane ≠ data plane. A control-plane outage freezes your GitOps pipeline mid-rollout.
GCP — Jun 2, 2019 (Cloud Networking incident #19009) US-east network congestion caused up to 33% packet loss in us-east1 for ~4 hours. GCP's backbone is excellent, and still a single fabric with its own bad days.
GCP — Nov 11, 2019 (internal KMS failure incident) A key-management-system failure hit us-east1, us-east4, and southamerica-east1 simultaneously. Cryptographic dependencies are multi-region by design — they fail multi-region too.
GCP — Dec 14, 2020 (User ID Service quota incident) A quota system change set the auth service's usage to 0, then a mitigation triggered the failure it was meant to prevent: global auth outage, ~50 minutes, everything from Gmail to the Cloud Console. Google's most global services fail globally. Identity is a region-independent SPOF on every cloud.
GCP — Nov 16, 2021 (Cloud Networking incident report, load balancing) A configuration race condition served 404s from Google's front-end load balancers for ~30 minutes. Edge infrastructure is shared across customers, regions, and even Google's own products.
GCP — Jun 12, 2025 (Service Control incident report) A change in the automated quota system left the central policy layer rejecting external API calls with 503s — Google Cloud, Workspace, and Security Operations went dark, with collateral outages across the consumer internet. The "policy plane" is now a tier-zero dependency. Quota tooling can take down the cloud.

Two structural conclusions fall out of this history. First, every provider has a global layer that outranks your regional architecture: AWS's is concentrated in us-east-1 (where more services' control planes live than anywhere else), Azure's is ARM plus the shared Microsoft identity/config fabric, GCP's is its identity and policy planes. Designing "multi-region" within one provider mitigates some failure modes (the Nov 2020 Kinesis class) and is helpless against others (the Dec 2020 and Jun 2025 classes). Second, blast radius is governed by your account/subscription/project topology, which is where landing zones come in. The three landing-zone models, and what they actually isolate:

On data residency: all three now sell sovereignty tiers for customers whose regulators require them — the AWS European Sovereign Cloud (GA, Brandenburg region) with its own isolated control plane, Microsoft Cloud for Sovereignty as a governance layer over standard Azure regions, and Google's Sovereign Cloud portfolio (Data Boundary, Dedicated, Air-Gapped). Note what they admit by existing: in the standard commercial clouds, your data's location and the control plane's location are different claims.

Who Should Skip Each Cloud

Every cloud comparison ends with "it depends." We refuse. Here's when each provider is the wrong answer:

Final Verdict