Foundations
Everything a Kubernetes API server needs to exist before Flux can reconcile anything — the network, the secrets and PKI layer, and the cluster itself — is provisioned with OpenTofu stacks orchestrated by Terramate. This page is the model: why these two tools, and why three stages rather than one. For the commands and what each stage actually creates, see Get Started and this section’s cloud-specific page.
Why OpenTofu
OpenTofu is the open-source fork of Terraform, created after HashiCorp moved
Terraform off an OSI-approved license. It is now a Linux Foundation project
with community governance, and it stayed compatible with existing Terraform
configuration and providers — so adopting it cost nothing beyond the tool
name in CI and mise.toml.
Why Terramate
A platform with five-plus independent lifecycles (network, secrets, cluster bootstrap split into two internal stages, an opt-in LLM platform) doesn’t fit one OpenTofu root module. Terramate turns the separate stacks into one graph:
- Ordering — each stack declares the stacks it runs
afterin itsstack.tm.hcl. Terramate topologically sorts the graph instead of a human remembering tocdinto five directories in the right sequence. - Drift detection —
terramate script run drift detectplans every stack and reports divergence without applying anything. - DRY configuration —
opentofu/config.tm.hclholds the globals every stack reads (region, cluster name, chart versions) instead of five copies of the same variable. - Cloud selection — one variable,
TM_CLOUD, decides which lanes an invocation may touch (awsby default, orgcp,aws,gcp,all). It is enforced inscripts/provision/tm-provisioner.sh, the wrapper every stack reaches OpenTofu through, so a single interception point covers the shared scripts and every per-stack override. The AWSllm-platformstack keeps its ownTM_LLM_PLATFORM_ENABLEDgate — that is a feature axis, not a cloud one.
The alternative — running tofu apply by hand in each stack directory —
still works; it just loses all four of the above and puts the ordering back
in a human’s head.
The three-stage model
Every cloud this platform targets provisions in the same three stages, because the dependency between them is real, not a convention:
- Network — VPC/subnets and whatever gives this machine private access to what gets built next. Nothing after this stage can reach anything built in the next two.
- Security — a secrets and PKI layer, running before Kubernetes exists
because Kubernetes bootstrap itself consumes it: the second half of cluster
bootstrap writes that cluster’s own JWT auth mount into OpenBao — it is the
only stack that knows the cluster’s OIDC issuer — so the Kubernetes stack’s
own
stack.tm.hcldeclares this stage in itsafterlist. - Kubernetes — the cluster, plus enough of a CNI to make nodes
Ready, with Flux installed and reconciling by the end of it. This is the handoff point: once Flux is running, GitOps owns everything that happens next.
This page stays cloud-neutral on purpose, not because there is one “true” shape underneath, but because the model — three stages, in this order, for this reason — really is the same on every cloud, while the stacks that implement it are not. Where a page can’t be honestly neutral, it isn’t: ADR-0007 makes the same call for the Crossplane compositions — an API that only looks cloud-neutral produces worse errors than one that is honestly cloud-shaped — so the cloud-specific detail lives on AWS and GCP, not blended in here. Where the two differ service by service — and where they deliberately meet — is on Cloud support.