The same primitive, three providers
A mapping you can use to read a design written for another cloud — and, more importantly, a record of where that mapping misleads. Every row carries what differs, because the services in a row are near-equivalents and never exact ones.
Reading a row across is the fast way to orient yourself and the fast way to be wrong. The differences below are not trivia: they change how you size a connection pool, whether in-memory state is safe, what a restart does, and how a service behaves the first time it scales. Read the third block of every row you use.
Host-maintenance behaviour is not the same: Compute Engine can live-migrate a running instance to another host, while EC2 signals a retirement event and expects you to stop/start or rebuild, which changes the instance store and often the address. A shutdown and re-registration path that is exercised weekly on one provider may almost never run on another.
The concurrency model differs, and it changes your code. Cloud Run dispatches multiple concurrent requests into one container instance and scales on that concurrency setting; a Fargate task is an always-running unit and you scale by task count. Per-instance in-memory state, connection-pool sizing and "requests per instance" arithmetic all come out differently on each.
The API is portable; the surrounding wiring is not. GKE Autopilot manages nodes and rejects pod specs that violate its constraints, while EKS leaves you to configure the CNI and the pod-to-IAM identity mapping yourself. Ingress, load-balancer annotations and storage classes are provider-specific, so the same manifests need per-provider changes.
Whether one instance handles one invocation at a time is a per-provider answer. A Lambda execution environment processes a single event at a time, so module-level state is effectively single-threaded; Azure Functions hosts can process multiple invocations in one worker process, where the same module-level variable is shared mutable state. Timeout ceilings, payload limits and how each triggers from a queue differ as well.
The namespace and the access model are shaped differently: Azure inserts a storage account above the container, so identity, network rules and throughput limits attach at that account level with no S3 equivalent. Signed-URL construction, permitted expiry windows, versioning and lifecycle-rule semantics are provider-specific, so presigned-upload code does not port unchanged.
Aurora and AlloyDB replace the storage layer beneath Postgres rather than replicating a standard one, so failover behaviour, replica visibility and write amplification are not the same as streaming replication from a stock primary — and the same application can see different replica-lag behaviour on each. All three restrict superuser and differ in which extensions are permitted, which decides whether a migration that works locally works there.
Durability and topology are tier-dependent, not primitive-dependent: whether persistence exists, whether failover is automatic, and whether cluster mode is enabled all vary by the tier you picked. Cluster mode is the one that reaches your code — multi-key operations and transactions cannot span slots, so a working single-node client can fail on a clustered deployment of the "same" service.
Ordering and redelivery semantics are genuinely different products. SQS standard queues are unordered and FIFO is a separate queue type with its own constraints; Pub/Sub is at-least-once with optional ordering keys and both push and pull delivery; Service Bus adds sessions, transactions and native dead-lettering. The redelivery knob is a visibility timeout, an ack deadline and a lock duration respectively — same idea, different expiry behaviour and different consequences when a consumer stalls.