In raw, unmanaged Cluster API, creating a single workload cluster requires writing and maintaining at least six to eight interdependent Custom Resource manifests: Cluster, InfrastructureCluster (e.g., AWSCluster), KubeadmControlPlane, control-plane AWSMachineTemplate, multiple worker MachineDeployments, worker AWSMachineTemplates, and KubeadmConfigTemplates.

Managing this sprawling CRD graph for one cluster is educational. Managing it across fifty or five hundred clusters in production creates a combinatorial explosion of boilerplate YAML.

Traditionally, platform engineering teams attempted to tame this complexity by layering external, client-side templating tools (such as Helm, Kustomize, or Jinja scripts) on top of Cluster API. However, client-side templating introduces critical architectural flaws: it lacks server-side schema validation, provides no continuous drift reconciliation, and forces application teams to understand low-level cloud provider infrastructure schemas.

Cluster API resolves this through ClusterClass and Managed Topologies.

ClusterClass elevates cluster definition into an object-oriented class-and-instance pattern. A platform team defines a parameterized, reusable cluster blueprint once as a ClusterClass. Cluster consumers then instantiate clusters through a lightweight, high-level spec.topology block on the Cluster resource.


1. The Problem: The Unmanaged CRD Graph at Scale

To understand why ClusterClass is necessary, contrast how clusters are defined with and without managed topologies:

DimensionRaw Cluster API (Unmanaged CRD Graph)Managed Topologies (ClusterClass)
Control PointFragmented across 6+ disparate CRs (Cluster, KCP, MD, templates)Single control point: Cluster.spec.topology
Boilerplate OverheadHundreds of lines of repetitive YAML copied per clusterMinimal manifest: ~30 lines specifying version, counts, and variables
Drift ReconciliationNone: changing a template requires manual rotation across every clusterContinuous: updating the ClusterClass cascades rollouts fleet-wide
Validation LayerClient-side YAML linter or generic Kubernetes API validationServer-side OpenAPI v3 schema validation on cluster variables
Consumer ExperienceHigh cognitive load: developers must master provider-specific VM/VPC CRDsLow cognitive load: developers select worker classes and pass parameters
Upgrade ExperienceMulti-phase manual patching of KCP and individual MachineDeploymentsSingle-knob operation: updating Cluster.spec.topology.version

2. The Architectural Mental Model: Class vs. Instance

The relationship between ClusterClass and a managed Cluster mirrors class-and-object architecture in software engineering:

flowchart TD
    subgraph PlatformEngineering["Platform Engineering Team (Blueprint Definition)"]
        CC["ClusterClass<br/>(Reusable Architecture)"]
        T_INFRA["InfrastructureCluster Template"]
        T_CP["ControlPlane Template"]
        T_WORKERS["Worker Classes (General, High-Mem, GPU)"]
        VARS["OpenAPI v3 Variable Schema"]
        PATCHES["JSON 6902 / Go Template Patches"]

        CC --> T_INFRA
        CC --> T_CP
        CC --> T_WORKERS
        CC --> VARS
        CC --> PATCHES
    end

    subgraph ClusterConsumers["Cluster Consumers (Concrete Deployments)"]
        C1["Cluster: Production EU<br/>(topology.version: v1.31.0, workers: 20)"]
        C2["Cluster: Staging US<br/>(topology.version: v1.31.0, workers: 3)"]
        C3["Cluster: Machine Learning<br/>(topology.version: v1.30.0, gpu-workers: 8)"]
    end

    CC -.->|"Instantiated by spec.topology.classRef"| C1
    CC -.->|"Instantiated by spec.topology.classRef"| C2
    CC -.->|"Instantiated by spec.topology.classRef"| C3

    subgraph TopologyEngine["CAPI Topology Controller (Server-Side Compiler)"]
        COMPILER["Evaluates Variables & Patches<br/>Reconciles Child CRD Graph"]
    end

    C1 --> COMPILER
    C2 --> COMPILER
    C3 --> COMPILER

    subgraph GeneratedResources["Generated Runtime Resources"]
        G_KCP["KubeadmControlPlane"]
        G_MD["MachineDeployments"]
        G_INFRA["AWSCluster / AzureCluster"]
    end

    COMPILER --> G_KCP
    COMPILER --> G_MD
    COMPILER --> G_INFRA

The Two Roles

  1. ClusterClass (The Blueprint): Authored by platform architects. It defines how a cluster of a particular tier or environment is structured: the base VPC topology, OS image templates, control plane settings, available worker classes, and variable validation rules.
  2. Cluster.spec.topology (The Instance): Authored by cluster consumers or automated GitOps pipelines. It defines what specific parameters this instance requires: target Kubernetes version, control plane replica count, worker pool sizes, and variable assignments.

The Topology Controller acts as a server-side compiler. It takes the blueprint and consumer parameters, evaluates variable substitutions and JSON patches, and generates and continuously reconciles the underlying concrete CAPI resources.


3. Anatomy of a ClusterClass

A production ClusterClass is organized into five structural sections:

apiVersion: cluster.x-k8s.io/v1beta1
kind: ClusterClass
metadata:
  name: standard-aws-cluster
  namespace: default
spec:
  # 1. Shared Infrastructure Template
  infrastructure:
    ref:
      apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
      kind: AWSClusterTemplate
      name: standard-aws-cluster-infra

  # 2. Control Plane Templates
  controlPlane:
    ref:
      apiVersion: controlplane.cluster.x-k8s.io/v1beta1
      kind: KubeadmControlPlaneTemplate
      name: standard-aws-kcp
    machineInfrastructure:
      ref:
        apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
        kind: AWSMachineTemplate
        name: standard-aws-cp-machine

  # 3. Reusable Worker Classes
  workers:
    machineDeployments:
      - class: "general-purpose"
        template:
          bootstrap:
            ref:
              apiVersion: bootstrap.cluster.x-k8s.io/v1beta1
              kind: KubeadmConfigTemplate
              name: general-worker-bootstrap
          infrastructure:
            ref:
              apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
              kind: AWSMachineTemplate
              name: general-worker-infra
      - class: "gpu-worker"
        template:
          bootstrap:
            ref:
              apiVersion: bootstrap.cluster.x-k8s.io/v1beta1
              kind: KubeadmConfigTemplate
              name: gpu-worker-bootstrap
          infrastructure:
            ref:
              apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
              kind: AWSMachineTemplate
              name: gpu-worker-infra

  # 4. Strongly-Typed OpenAPI v3 Variable Definitions
  variables:
    - name: region
      required: true
      schema:
        openAPIV3Schema:
          type: string
          enum: ["us-east-1", "us-west-2", "eu-central-1"]
    - name: workerInstanceType
      required: false
      schema:
        openAPIV3Schema:
          type: string
          default: "m6i.xlarge"

  # 5. Server-Side Patches
  patches:
    - name: injectRegion
      enabledIf: "{{ .region }}"
      definitions:
        - selector:
            apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
            kind: AWSClusterTemplate
            matchResources:
              infrastructureCluster: true
          jsonPatches:
            - op: add
              path: "/spec/region"
              valueFrom:
                variable: region

The Power of OpenAPI v3 Variables and Patches

Variables are not mere dumb string replacements. Cluster API validates variable inputs against the embedded openAPIV3Schema before any resources are stamped out. If a consumer provides an invalid region or unsupported instance type, the admission webhook rejects the kubectl apply synchronously.

Once validated, the patch engine injects values into the generated infrastructure templates using JSON 6902 patches or Go templates.


4. The Consumer Interface: Cluster.spec.topology

From the application or cluster consumer’s perspective, the entire infrastructure footprint collapses into a clean, declarative specification:

apiVersion: cluster.x-k8s.io/v1beta1
kind: Cluster
metadata:
  name: prod-analytics-cluster
  namespace: default
spec:
  topology:
    class: standard-aws-cluster
    version: v1.31.0

    controlPlane:
      replicas: 3

    workers:
      machineDeployments:
        - class: "general-purpose"
          name: "app-pool"
          replicas: 10
        - class: "gpu-worker"
          name: "training-pool"
          replicas: 4

    variables:
      - name: region
        value: "us-west-2"
      - name: workerInstanceType
        value: "c6i.4xlarge"

Consumers do not configure VPC route tables, security group ingress rules, or kubeadm bootstrap tokens. They interact purely with the platform API exposed by the ClusterClass.


5. Lifecycle Orchestration Under Managed Topologies

When operating a managed cluster, lifecycle operations transition from imperative manual patching to single-point topology mutations:

sequenceDiagram
    autonumber
    actor Consumer
    participant API as Kubernetes API
    participant TC as Topology Controller
    participant KCP as KubeadmControlPlane
    participant MD as MachineDeployments

    Note over Consumer,MD: Scenario: Fleet Upgrade from v1.30 to v1.31
    Consumer->>API: Patch Cluster.spec.topology.version = v1.31.0
    API-->>TC: Triggers Topology Reconciliation
    TC->>TC: Validates version & renders updated templates
    TC->>KCP: Patches KCP.spec.version = v1.31.0
    Note over TC,MD: Holds worker MachineDeployments at v1.30 (Version Skew Gate)
    KCP->>KCP: Executes rolling upgrade of control-plane machines
    KCP-->>TC: Reports status.ready = true & version = v1.31.0
    TC->>MD: Cascades version = v1.31.0 to worker MachineDeployments
    MD->>MD: Generates new MachineSets & rolls worker fleet
    TC-->>API: Sets Cluster condition TopologyReconciled = True

1. Scaling Capacity

To scale a worker fleet, update spec.topology.workers.machineDeployments[name].replicas. The topology controller updates the underlying MachineDeployment, which cascades replica drift down to the MachineSet.

2. Upgrading Kubernetes Versions

To upgrade an entire cluster, change spec.topology.version. The topology controller manages the rolling replacement: it patches the control plane first, enforces the Kubernetes version skew policy by holding worker versions, verifies control plane stability, and automatically cascades the upgrade to all worker pools.

3. Modifying Infrastructure Flavors

To change instance types, storage classes, or IAM roles, consumers adjust variable values in spec.topology.variables. The patch engine compiles new immutable templates and triggers rolling updates.


6. The Golden Rule of Managed Topologies

The most common operational mistake when adopting ClusterClass is treating generated child resources as independent objects:

ANTI-PATTERN: Directly editing child objects in a managed cluster:
kubectl edit kubeadmcontrolplane ...
kubectl scale machinedeployment ...
flowchart LR
    OPERATOR["Operator runs kubectl edit<br/>on generated MachineDeployment"] --> MUTATE["Mutates child object directly in etcd"]
    MUTATE --> TRIGGER["Topology Controller detects drift<br/>relative to Cluster.spec.topology"]
    TRIGGER --> REVERT["Topology Controller overwrites child object<br/>back to declared topology state"]

The Golden Rule: If a cluster defines spec.topology, the Cluster resource is the single source of truth. Any manual edits made directly to child resources (KCP, MachineDeployment, or generated templates) will be automatically detected as drift and reverted by the Topology Controller.

Always direct mutations to Cluster.spec.topology.


7. Diagnostic Workflows & Decision Trees

When topology reconciliation encounters invalid schemas, failing patches, or blocked child rollouts, use a structured diagnostic sequence:

flowchart TD
    START["Topology Reconciliation Stalled"] --> CHECK{"Inspect Cluster Conditions"}
    CHECK -->|"TopologyReconciled = False"| T_FAIL{"Where is Topology Failing?"}
    T_FAIL -->|"Variable Validation Error"| V1["Check Cluster.spec.topology.variables against<br/>ClusterClass openAPIV3Schema rules"]
    T_FAIL -->|"Patch Engine Failure"| V2["Inspect ClusterClass patches: invalid JSON path<br/>or missing template selector match"]
    T_FAIL -->|"Template Reference Missing"| V3["Verify all referenced templates in ClusterClass<br/>exist in the target namespace"]

    CHECK -->|"TopologyReconciled = True"| CH_FAIL{"Child Controller Blocked?"}
    CH_FAIL -->|"KCP Rolling Upgrade Stuck"| C1["Check control-plane etcd quorum & cloud quotas<br/>(See Part 10 on Upgrades)"]
    CH_FAIL -->|"MachineDeployment Drain Stuck"| C2["Check PDB violations & volume unmount locks<br/>(See Part 7 on Worker Internals)"]

Essential Inspection Commands

1. Inspect Topology Condition on the Cluster

kubectl describe cluster <cluster-name>

Look for the TopologyReconciled condition:

  • Status: "True": The desired topology was successfully parsed, patched, and applied to all child resources.
  • Status: "False": Indicates a patch execution error or schema validation failure. The Message field specifies the exact JSON path or variable that failed.

2. Inspect the Blueprint Definition

kubectl describe clusterclass <class-name>

Inspect the defined worker classes, variable schemas, and patch selectors.

3. View the Complete Generated Resource Hierarchy

clusterctl describe cluster <cluster-name> --show-conditions

Confirms whether generated control plane and worker deployments are progressing toward the declared topology state.


8. Key Takeaways

  • The Class-Instance Abstraction: ClusterClass defines the reusable, parameterized architecture of a cluster; Cluster.spec.topology instantiates and customizes a specific cluster.
  • Server-Side Validation & Compilation: Variables are strongly validated against OpenAPI v3 schemas before being injected into templates via server-side JSON patches.
  • The Cluster is the Single Control Point: Scaling, upgrades, and configuration adjustments are executed by mutating Cluster.spec.topology. Never edit generated child objects directly.
  • Clean Organizational Boundaries: Platform engineering teams author and govern ClusterClass definitions; application and infrastructure operators consume self-service clusters via concise topology manifests.
  • Underlying CAPI Mechanics Remain Intact: ClusterClass does not replace core providers or controllers. It acts as an orchestrator sitting on top of standard MachineDeployment, KubeadmControlPlane, and Machine resources.

Next in the series

Now that we understand how ClusterClass and managed topologies standardize multi-cluster lifecycle management, explore Part 12: Cluster Autoscaler and Cluster API to examine how the Kubernetes Cluster Autoscaler integrates with declarative MachineDeployments to scale worker fleets dynamically based on workload scheduling pressure.


References & Further Reading