Skip to content
Add a cloud provider

Add a cloud provider

The platform runs on AWS and is designed so a second cloud is an addition rather than a rewrite. What makes that possible is a single rule, and the discipline to apply it consistently.

The rule

Platform-facing APIs stay cloud-shaped. Developer-facing APIs stay cloud-neutral.

The line is drawn by audience, not by layer. A platform engineer already knows which cloud they are configuring, so an API they consume should be honest about it. An application developer should not have to know, so an API they consume should not expose it.

ADR-0007 records the reasoning, including the option that was rejected: one neutral abstraction over both, whose most important field would inevitably have been a free-form cloud-specific blob.

What a new provider must implement

LayerWhat is neededWhy it is cloud-shaped
NetworkVPC-equivalent, subnets, private DNS zone, VPN reachabilityTopology and naming differ fundamentally
ClusterManaged Kubernetes with a CNI you controlThe bootstrap sequence is provider-specific
IdentityWorkload-to-cloud-API bindingTrust policy shapes are not interchangeable
Secrets storeSomewhere ESO can read fromProvider-specific auth
Node autoscalingKarpenter or the provider’s equivalentNot every cloud has a production-ready Karpenter

Each gets its own OpenTofu stack and its own section in the documentation. The GCP design is worked through in docs/superpowers/specs/2026-08-18-gcp-support-design.md, and three decision records already frame it: ADR-0005, ADR-0006, ADR-0007.

What gains a sibling, not a field

When an API is genuinely cloud-shaped, add a sibling XRD rather than a discriminated union.

EPI is the clearest case. Its central field is inline AWS IAM JSON. GCP’s equivalent is a list of predefined roles or a custom role’s permissions. There is no neutral form of that field, and an API carrying both shapes and choosing at render time is two APIs sharing a name — with worse error messages than either would have alone.

So: EPI for AWS, a sibling for GCP, both consumed internally by the developer-facing compositions.

What must not change

App and SQLInstance claims are developer-facing and stay neutral. The same claim should mean the same thing on either cloud.

That does not mean nothing underneath changes — the S3 bucket becomes a GCS bucket and the IAM role becomes a workload identity binding. It means the claim does not, so an application team’s manifests are not a per-cloud artifact.

Where a genuinely cloud-specific knob is unavoidable, it belongs in a clearly-marked optional sub-block, not spread through the API.

The test to apply

Before adding an abstraction, ask who reads it:

  • A platform engineer configuring one cloud? Keep it cloud-shaped and honest. Portability buys nothing here and costs indirection.
  • An application developer who should not care? Keep it neutral, and put the cloud-specific part in the composition where it belongs.

An API that looks neutral but is not produces worse failures than one that is visibly cloud-specific, because the error arrives later and further from its cause.

Reading on

  • Foundations — how the AWS stacks are structured, and where a sibling would slot in
  • Developer platform — the neutral APIs that must keep working unchanged