Foundations
opentofu/scaleway builds an empty
Kapsule cluster and steps away: the catalog arrives through the bootstrap and
Flux.
What is decided for you
Section titled “What is decided for you”| Position | How |
|---|---|
| Kapsule; Kosmos refused | validation |
Dedicated 4 control plane in prod, mutualized elsewhere | from environment; prod on mutualized fails the plan |
| Kapsule’s Cilium | enforced |
One pool per zone, fr-par-1 and fr-par-2, COMPUTE3-X8C-16G, 2 to 5 nodes | default; shared-vCPU and dev types refused |
least_waste expander, a placement group per zone | default; price refused |
| An explicit version, patches in a window | both required |
| No public IP on nodes; a Public Gateway per zone | enforced |
API server allow-list, 0.0.0.0/0 refused | required |
| A /22 Private Network, a security group per cluster and zone | default |
| One Project per environment | project_id required |
| One Project-scoped Crossplane key, bound to the gateways’ addresses | crossplane_permission_sets required, AllProductsFullAccess refused |
| Nothing written to Cockpit; a query-only token | default |
owner, environment, socle-version, cluster tags on every resource | enforced |
Under the hood
- Refused node types: BASIC, DEV1, PLAY2, STARDUST, and zones without the
chosen generation. The placement groups are
max_availability. additional_tagscannot override the four socle tags.delete_additional_resources = false: deleting the cluster leaves the Load Balancers and Block volumes Kubernetes created. Client data survives a teardown; someone has to remove what is billed.oidc_issuer_urlandworkload_identity_poolare always null: Kapsule has no OIDC issuer, Scaleway no workload identity federation. They keep one output surface across the four clouds.
What is deliberately absent
Section titled “What is deliberately absent”- Kosmos: another CNI, no Private Network, no migration path.
- A CNI variable: Kapsule does not support
none; Calico is worse on every count. - Controlled isolation: dev and staging would use another egress path than production.
- The
priceexpander: a silent no-op outside GCE and AWS. - Shared-vCPU and development node types: a 99% SLO or less.
feature_gates,admission_plugins,apiserver_cert_sans,open_id_connect_config: the socle needs none.- A Public Gateway bastion: an inbound surface the allow-list does not cover.
- Cockpit alerting, contacts, dashboards, exports: catalog concerns.
- Load Balancers, DNS records, certificates: the cloud controller manager and the catalog own them.
- A backend block and a state bucket: yours (prerequisites).
Measured
Section titled “Measured”- 2026-09-14, instance API: Zen 5 ranges (COMPUTE3, STANDARD3, BASIC3) exist in
fr-par-1,fr-par-2,nl-ams-1,nl-ams-2; POP2 and PRO2 infr-par-3,nl-ams-2,nl-ams-3and the threepl-wawzones. Scaleway’s documentation disagrees; the validations follow the API. - 2026-10-05,
fr-par-1:COMPUTE3-X8C-16G(8 vCPU, 16 GiB) €0.2341 an hour;POP2-HC-8C-16G, a generation older, €0.2128.
Reference
Section titled “Reference”Requirements
Section titled “Requirements”| Name | Version |
|---|---|
| terraform | >= 1.10 |
| scaleway | >= 2.82, < 3.0 |
Modules
Section titled “Modules”No modules.
Resources
Section titled “Resources”| Name | Type |
|---|---|
| scaleway_cockpit_token.observability | resource |
| scaleway_iam_api_key.crossplane | resource |
| scaleway_iam_application.crossplane | resource |
| scaleway_iam_policy.crossplane | resource |
| scaleway_instance_placement_group.socle | resource |
| scaleway_instance_security_group.socle | resource |
| scaleway_ipam_ip.gateway | resource |
| scaleway_k8s_acl.socle | resource |
| scaleway_k8s_cluster.socle | resource |
| scaleway_k8s_pool.socle | resource |
| scaleway_vpc.socle | resource |
| scaleway_vpc_gateway_network.socle | resource |
| scaleway_vpc_private_network.socle | resource |
| scaleway_vpc_public_gateway.socle | resource |
| scaleway_vpc_public_gateway_ip.socle | resource |
Inputs
Section titled “Inputs”| Name | Description | Type | Default | Required |
|---|---|---|---|---|
| cluster_endpoint_public_access_cidrs | CIDRs allowed to reach the Kubernetes API server. Required with no default: the control plane cannot be made private on Kapsule, so this list is the only boundary there is, and Kapsule ships 0.0.0.0/0. | list(string) | n/a | yes |
| cluster_name | Name of the Kapsule cluster. Also names the network resources the module creates. | string | n/a | yes |
| crossplane_permission_sets | Permission sets granted to the in-cluster Crossplane identity, scoped to this Project. Required with no default: what Crossplane may provision is a per-client decision, and a default would either be uselessly narrow or dangerously wide. | list(string) | n/a | yes |
| environment | Environment this cluster serves. Stamped as a tag, decides the default control plane offer, and is the axis the upgrade ring order follows. | string | n/a | yes |
| kubernetes_version | Kubernetes minor or patch version. Required with no default: Scaleway has no release channel, auto-upgrade covers patches only, and a floating version would let the pipeline lose track of which minor is deployed. | string | n/a | yes |
| maintenance_window | When patch auto-upgrades may run. Required with no default: a silent default means nobody decided when production gets upgraded, and the window is what orders the upgrade rings across environments. | object({ | n/a | yes |
| owner | Team accountable for the cluster. Stamped as a tag on every resource that takes tags. | string | n/a | yes |
| project_id | Scaleway Project that holds the cluster and its network. One Project per environment: it is the only boundary Scaleway offers for both IAM and cost attribution. | string | n/a | yes |
| additional_tags | Extra tags merged onto the standard set, rendered as key=value. Cannot override owner, environment, cluster or socle-version. | map(string) | {} | no |
| autoscaler_expander | How the cluster-autoscaler picks which pool to grow. least_waste strands the least capacity; Scaleway's own default is random, which is a coin flip once there is more than one pool. | string | "least_waste" | no |
| availability_zones | Zones the node pools span, one pool per zone. Two is the default because the current instance generation exists in only two zones of fr-par and nl-ams; only pl-waw offers three. | list(string) | [ | no |
| cockpit_token_enabled | Create a query-only Cockpit token so the central observability cluster can federate Scaleway's own metrics and logs. Reading is what the supervision plane does; pushing into Cockpit is billed per sample and is refused. | bool | true | no |
| control_plane_type | Control plane offer. Null derives it from environment — dedicated 4 in production for the SLA and the audit log, mutualized elsewhere. Kosmos offers are absent by decision. | string | null | no |
| create_vpc | Create the VPC instead of attaching to an existing one. Routing is VPC-wide: by default each cluster gets its own VPC; pass an existing one to share it. | bool | true | no |
| crossplane_allowed_cidrs | Source addresses the Crossplane key may be used from, as an IAM policy condition. Empty derives it from the Public Gateways' egress addresses, which is the only stable source a fully isolated node presents. | list(string) | [] | no |
| crossplane_key_expires_at | RFC 3339 expiry for the Crossplane API key. Null means no expiry. Scaleway has no workload identity federation, so this key is the credential — an expiry is what forces the rotation the factory owns. | string | null | no |
| delete_additional_resources | On cluster deletion, also delete the Load Balancers and Block volumes Kubernetes created. False keeps client data when a cluster is torn down, at the price of orphaned billable resources someone has to clean up. | bool | false | no |
| node_type | Commercial type of the pool nodes. COMPUTE3-X is the current generation: dedicated vCPU at 1 vCPU per 2 GiB. BASIC3-X is refused for nodes — shared vCPU and a 99% SLO. | string | "COMPUTE3-X8C-16G" | no |
| pool_max_size | Maximum nodes per pool, and therefore per zone. | number | 5 | no |
| pool_min_size | Minimum nodes per pool, and therefore per zone. Billed whether anything schedules on them or not, because the autoscaler never consolidates. | number | 2 | no |
| private_network_cidr | IPv4 subnet of the cluster's Private Network. Kapsule consumes a /22 per cluster, so anything larger is wasted and anything smaller is refused. | string | "10.10.0.0/22" | no |
| public_gateway_type | Offer of the Public Gateways that carry node egress. VPC-GW-S handles 100 Mbps, which is ample for image pulls and API calls. | string | "VPC-GW-S" | no |
| region | Scaleway region for the cluster, its network and its pools. | string | "fr-par" | no |
| root_volume_size_in_gb | System volume of each node. Scaleway's own guidance is 20 GB minimum and 100 GB to hold images and system logs comfortably. | number | 100 | no |
| scale_down_unneeded_time | How long a node must sit below the utilisation threshold before the autoscaler removes it. | string | "10m" | no |
| scale_down_utilization_threshold | Requested resources over allocatable capacity, below which a node becomes a scale-down candidate. | number | 0.5 | no |
| vpc_id | ID of an existing VPC to attach the cluster's Private Network to. Mutually exclusive with create_vpc. | string | null | no |
Outputs
Section titled “Outputs”| Name | Description |
|---|---|
| cluster_ca_certificate | Base64-encoded cluster CA certificate, for building a kubeconfig. |
| cluster_endpoint | URL of the Kubernetes API server. Always public on Kapsule — a fully private control plane does not exist — and reachable only from cluster_endpoint_public_access_cidrs. |
| cluster_id | ID of the Kapsule cluster, in region/uuid form. |
| cluster_name | Name of the Kapsule cluster. |
| cluster_region | Region of the cluster. A Kapsule cluster lives in exactly one. |
| cluster_type | The control plane offer in force. Derived from environment unless control_plane_type overrides it: production gets a dedicated offer for the SLA and the audit log. |
| cockpit_token_secret | Query-only Cockpit token for the central observability cluster to federate Scaleway's metrics and logs. Null when cockpit_token_enabled is false. |
| crossplane_access_key | Access key of the Crossplane API key. Not secret on its own, but pair it with crossplane_secret_key. |
| crossplane_application_id | ID of the IAM application the in-cluster Crossplane provider authenticates as. |
| crossplane_secret_key | Secret key for the in-cluster Crossplane provider. This is a long-lived credential because Scaleway offers no alternative; it is bound to the source addresses in crossplane_allowed_cidrs and expires at crossplane_key_expires_at. |
| gateway_egress_cidrs | Public addresses the cluster's nodes egress from, one per zone, as /32 CIDRs. This is the estate's stable source address: allow-list it wherever a client needs to admit the cluster. |
| helm_kubernetes | Drop-in value for the helm provider's kubernetes attribute, so a root configures it in one line. Kapsule has no exec credential plugin and accepts the IAM secret key as a bearer token, so the exec emits an ExecCredential from SCW_SECRET_KEY — the variable the scaleway provider already reads — at call time. No token is stored in state. |
| kubeconfig | Raw kubeconfig for the cluster. Sensitive: it carries a bearer token. |
| oidc_issuer_url | Always null. Kapsule exposes no OIDC issuer for workload identity, so nothing can federate against this cluster's ServiceAccount tokens. |
| private_network_id | ID of the cluster's Private Network. |
| security_group_ids | The cluster's own node security groups, keyed by zone. Not the shared Kapsule default, which is common to every cluster in the Project. One per zone because an Instance security group is a zoned resource. |
| tags | The standard tag set applied to every resource this module creates that accepts tags. |
| vpc_id | ID of the VPC the cluster's Private Network sits in, whether the module created it or not. |
| wildcard_dns | DNS wildcard resolving to every ready node. Useful for reaching a NodePort before an ingress path exists. |
| workload_identity_pool | Always null. Scaleway has no workload identity federation — the in-cluster provider authenticates with the API key below instead. |