The Runtime Theory
Cloud & Infrastructure

Vendor Lock-In Is a Cost Model

Egress fees, control-plane APIs, and data gravity — how cloud providers price switching costs, and how to compute the real cost of a migration.

The Runtime Theory Team4 min read#cloud#vendor-lock-in#egress#pricing#migration
On this page

Vendor lock-in is not a moral failing of cloud providers or a conspiracy. It is a cost model: the providers have engineered a pricing structure where the cheapest way to use a service is to use more of the same provider, and the most expensive operation in the catalog is the one that takes data elsewhere. Once you read the invoice as a switching-cost schedule, "lock-in" becomes a concrete, computable number — and you can decide how much of it you are willing to pay.

Egress fees: the tariff on leaving

The centerpiece of the lock-in cost model is data egress — the charge for bytes leaving the provider's network:

text
S3 -> internet (us-east-1)          $0.09 per GB, first 10 TB
S3 -> same region EC2               $0.00
S3 -> other AWS region              $0.02 per GB
EBS snapshots -> different region   $0.09 per GB (plus transfer)
CloudFront (CDN egress)             ~$0.085 per GB, first 10 TB
NAT gateway data processing         $0.045 per GB

Notice the structure: ingress is free, same-provider traffic is near-free, cross-provider is metered, and egress to the internet is the premium tier. That is a tariff whose entire purpose is to make the marginal cost of using the same provider lower than the cost of leaving. The numbers are not subtle about it:

bash
# the migration invoice nobody prints before starting
size_tb=100
egress_rate=0.09
echo "egress of ${size_tb} TB from S3: \$$(python3 -c \
  "print(f'{(100*1024*$egress_rate)/1:,.0f}')")"

100 TB out of S3 is ~$9,200 in egress alone — before you pay for the transfer machines, the destination's ingress, or any downtime. Scale that to a petabyte data lake and the egress fee alone becomes a seven-figure line item. That number is the lock-in, expressed as a price.

The control-plane API is the real lock

Egress is the visible tariff; the control-plane APIs are the invisible one. You cannot migrate a workload by copying data — you must also reimplement the contract your application was written against:

text
auth        AWS IAM (policies, roles, STS)          vs. GCP IAM / OIDC
secrets     AWS KMS, Secrets Manager                vs. provider equivalents
queues      SQS vs. SNS vs. EventBridge             vs. RabbitMQ/Kafka
registry    ECR vs. ECR Public                       vs. Docker Hub/GHCR
observability CloudWatch, X-Ray                     vs. Datadog/Prometheus

Every one of these is a different API with different SDKs, different IAM-shaped permission models, and different operational semantics (SQS's at-least-once with visibility timeouts has no direct analog in RabbitMQ's ack model; EventBridge schema validation is not the same as Kafka's). The application code — your actual business logic — is usually the easy part to move. The control-plane plumbing is the part that gets rewritten, retested, and re-certified.

The provider-specific services (DynamoDB, S3, Lambda, Aurora) are where the switching cost concentrates: DynamoDB's single-digit-millisecond consistency model and its GSI semantics don't map 1:1 onto Postgres or Cassandra, so "port the workload" becomes "re-architect the data access pattern." This is data gravity in its purest form: data and control plane accumulate in the same place, and every byte and every API call makes the next decision to stay cheaper than the decision to leave.

The migration math

A migration is a project with a real budget, so price it like one. The honest estimate has five components:

text
1. egress and transfer          the tariff above, per TB
2. engineering time             API rewrites: weeks per service, not days
3. parallel-run infrastructure  both platforms running during cutover
4. retesting                    your CI/CD, load tests, and compliance runs
5. residual risk                migration incidents during cutover

The interesting property: component one (egress) is usually the smallest of the five. A 10 TB database is ~$1,000 in egress but months of engineering. That is why "lock-in" arguments that focus on egress fees miss the point — the control plane is the expensive part, and it is expensive in time, not bytes. The egress tariff is a rounding error on the real switching cost; it exists to make the decision feel metered rather than free.

What is actually portable

Portability is a spectrum, and the spectrum is predictable:

text
highly portable:    containers + Kubernetes manifests, Terraform (mostly),
                    stateless services, standard protocols (HTTP, gRPC, SQL,
                    S3-compatible object stores like MinIO)
moderately portable: managed databases (schema is portable, ops is not),
                    queuing (semantics differ), CI/CD pipelines
deeply locked:      serverless event models, proprietary DBs (DynamoDB),
                    control-plane auth (IAM), managed ML/AI services

Two notes on the "portable" column. First, S3-compatibility is a de facto standard now — most object stores (MinIO, Cloudflare R2, GCP) speak the S3 API, which is why R2's killer feature was zero egress fees: they removed the tariff on a protocol everyone already speaks. Second, Kubernetes is the great portability blur: your deployment manifests move, but the managed control plane (EKS vs. GKE vs. AKS), the IAM integration, and the CNI/CSI plugins stay behind.

The rational strategy

Lock-in is not all-or-nothing; it is a portfolio decision with a cost per service:

  1. Price the exit before you enter. Before adopting any service, write the migration cost for it — egress + API rewrite time. If the answer is "re-architecture," you have a strategic dependency, and you should treat it as one: minimize the surface area it touches, isolate it behind an interface of your own (a repository abstraction over DynamoDB costs a day to write and makes the exit a swap, not a rewrite).
  2. Keep the crown jewels portable. Data is what you can't re-create — use object storage with standard APIs (S3-compatible, versioned, with replication you control) so the data layer is never the hostage.
  3. Accept lock-in where the value exceeds the exit cost. Provisioned services (managed Postgres, serverless compute) genuinely remove operational work; paying a switching cost for less toil is a legitimate trade. The mistake is lock-in you didn't notice, not lock-in you priced.
  4. Multi-cloud is not the answer; the interface is. Running the same workload in three clouds triples the control-plane surface and the egress bill. What defeats lock-in is a thin, tested abstraction — not redundancy of vendors.

The runtime view

  • Egress fees are a tariff schedule: ingress free, same-provider cheap, leaving metered at the premium tier.
  • The control plane — IAM, queues, observability, proprietary databases — is where the real switching cost lives, measured in engineering months, not bytes.
  • Migration cost = egress + API rewrites + parallel-run + retesting + risk; the egress line is usually the smallest of the five.
  • Lock-in is a portfolio decision: price the exit per service, isolate what you can't afford to re-architect, and accept dependency only where the value is priced, not discovered.