Quickstart: GCP
One tofu apply of the GCP root builds the VPC, its Cloud NAT and the GKE
Autopilot cluster, installs Flux; Flux then converges the catalog. GKE runs
the network (Dataplane V2), the cluster DNS and the nodes: there is nothing
to size.
Before you start
Section titled “Before you start”From Prerequisites:
- a billed project with its APIs enabled, and one socle cluster per project: every Workload Identity principal names the project, never the cluster;
- a GCS state bucket;
- a principal with the
apply roles,
logged in for both the provider (Application Default Credentials) and
gke-gcloud-auth-plugin; - quotas raised: a fresh project’s CPU and SSD quotas are not enough;
- OpenTofu 1.13 or later,
gcloud,gke-gcloud-auth-plugin,kubectl.
Write your main.tf
Section titled “Write your main.tf”One file in an empty directory: the state backend, then the socle’s GCP root called as a module. Change the names, the region and the window, keep the rest:
terraform { backend "gcs" { bucket = "acme-prod-tofu-state" prefix = "socle/gcp/acme-prod" }}
locals { socle_version = "0.1.0" # x-release-please-version}
module "socle" { source = "oci://ghcr.io/do-now-io/socle/opentofu-modules//opentofu/clusters/gcp?tag=${local.socle_version}"
socle_version = local.socle_version
gcp = { project_id = "acme-prod" region = "europe-west1" cluster_name = "acme-prod" owner = "platform" environment = "prod"
# When GKE may upgrade the cluster, at least four hours: here Saturday # 02:00-14:00 UTC. maintenance_window = { start_time = "2026-01-03T02:00:00Z" end_time = "2026-01-03T14:00:00Z" recurrence = "FREQ=WEEKLY;BYDAY=SA" }
# Create the VPC, its subnetwork, NAT and proxy-only subnet. Without it, # network_name names a VPC you already own. create_network = true }}
output "cluster_name" { value = module.socle.cluster_name}socle_versionis the only line an upgrade touches: it moves the module source and the Flux artifact together.maintenance_windowhas no default: GKE upgrades the cluster on its own, you choose when. The day orders the rings, for example dev on Tuesday, staging on Wednesday, prod on Saturday.- No Kubernetes version: the
REGULARrelease channel decides (release_channel,kubernetes_min_versionto raise the floor).
To serve routes on your domain and let modules have their own Google Cloud
access, add under gcp:
# The Google-managed certificates of the two shared Gateways, authorised # by DNS in the managed zone acme-example of this project. Without it no # Gateway is created. gateway_certificate = { dns_zone = "acme-example", domains = ["acme.example", "*.acme.example"] }
# Crossplane's grants; allowed_roles is the ceiling of every role a # catalog module gets. Empty still grants external-dns its DNS records role. crossplane = { allowed_roles = [] }With both and crossplane = { enabled = true } under kube, external-dns
turns on for those domains. The role each module needs in allowed_roles:
Crossplane’s allowed roles.
Every other key: Foundations and
Configure.
tofu inittofu applyThe apply ends once Flux’s objects are in the cluster; Flux converges the catalog after that.
Check it converged
Section titled “Check it converged”gcloud container clusters get-credentials acme-prod \ --region europe-west1 --project acme-prod --dns-endpointkubectl -n flux-system get resourcesetkubectl -n flux-system get ocirepository soclesocle-root at READY True means every module’s ResourceSet is applied
and healthy; the OCIRepository shows the pulled digest and
SourceVerified. If socle-root stays not Ready, the module’s ResourceSet
names what is waiting: see Troubleshooting.
Next steps
Section titled “Next steps”- Enable a module and Configure it.
- Upgrade: one line,
socle_version. - Foundations and Limits on GCP.
- Uninstall.