How to Build or Modify a k0rdent Template#
One of the most important benefits of k0rdent is the ability to create your own ServiceTemplate
, ClusterTemplate
, and ProviderTemplate
objects. The concepts are largely the same for all three, so we'll demonstrate the process with a ClusterTemplate
.
Anatomy of a ClusterTemplate#
A ClusterTemplate
in k0rdent is a Kubernetes custom resource that points to a Helm chart. The chart itself contains all the CAPI objects required to define a cluster. A typical chart structure might look like this:
├── Chart.yaml
├── templates
│ ├── awscluster.yaml
│ ├── awsmachinetemplate-controlplane.yaml
│ ├── awsmachinetemplate-worker.yaml
│ ├── cluster.yaml
│ ├── k0scontrolplane.yaml
│ ├── k0sworkerconfigtemplate.yaml
│ └── machinedeployment.yaml
├── values.schema.json
└── values.yaml
Chart.yaml
contains metadata: chart name, version, and description.values.yaml
contains the default configuration parameters, such as cluster size, networking, and AMI IDs.values.schema.json
can restrict or validate the parameters that users supply.- The
templates/
directory contains Kubernetes manifests for CAPI objects, such asCluster
,AWSCluster
, machine templates, and control plane definitions.
By changing the values in values.yaml
, you can produce many different clusters without modifying the underlying templates.
Default values.yaml Example#
The following snippet shows the default values.yaml
for the AWS standalone control plane ClusterTemplate
. This file is the main entry point for customization.
controlPlaneNumber: 3
workersNumber: 2
clusterNetwork:
pods:
cidrBlocks:
- "10.244.0.0/16"
services:
cidrBlocks:
- "10.96.0.0/12"
clusterLabels: {}
clusterAnnotations: {}
region: ""
sshKeyName: ""
publicIP: false
bastion:
enabled: false
disableIngressRules: false
allowedCIDRBlocks: []
instanceType: t2.micro
ami: ""
clusterIdentity:
name: ""
kind: "AWSClusterStaticIdentity"
controlPlane:
amiID: ""
iamInstanceProfile: control-plane.cluster-api-provider-aws.sigs.k8s.io
instanceType: ""
rootVolumeSize: 8
imageLookup:
format: "amzn2-ami-hvm*-gp2"
org: "137112412989"
baseOS: ""
uncompressedUserData: false
nonRootVolumes: []
worker:
amiID: ""
iamInstanceProfile: control-plane.cluster-api-provider-aws.sigs.k8s.io
instanceType: ""
rootVolumeSize: 8
imageLookup:
format: "amzn2-ami-hvm*-gp2"
org: "137112412989"
baseOS: ""
uncompressedUserData: false
nonRootVolumes: []
k0s:
version: v1.32.6+k0s.0
arch: amd64
cpArgs: []
workerArgs: []
api:
extraArgs: {}
files: []
Building a ClusterTemplate: Step by Step#
Follow these steps to create a custom ClusterTemplate
.
1. Obtain or create a Helm chart#
Most users start from an existing template. Mirantis maintains a set of baseline ClusterTemplates
in the k0rdent GitHub repository. You can download these as tarballs from an OCI registry or clone them directly from GitHub. Alternatively, you can create your own chart with helm create
.
2. Inspect the chart#
Unpack the chart and look through the templates/
directory. Each file maps to a CAPI object. Look at how values are referenced, usually with syntax like .Values.controlPlaneNumber
. This tells you which parameters you can customize in values.yaml
.
3. Modify configuration#
Edit values.yaml
to set the cluster size, networking ranges, AMIs, or k0s configuration. If you are creating a custom template, update Chart.yaml
with your own name and version to distinguish it from the default. You may also extend values.schema.json
to validate new parameters.
For example, to adjust Calico settings you might add:
k0s:
version: v1.32.6+k0s.0
cpArgs:
- "--enable-worker"
- "--enable-calico"
workerArgs:
- "--labels=network=calico"
4. Package and upload#
Once modified, package the chart:
helm package ./my-custom-template
Then push it to your OCI registry:
helm push my-custom-template-0.1.0.tgz oci://registry.example.com/templates
You may also need to make the chart public.
Connecting the Chart to k0rdent#
k0rdent does not fetch charts directly. Instead, it relies on FluxCD Source objects. You must define a HelmRepository
, GitRepository
, or Bucket
that points to your chart location, and you must label it so that k0rdent will recognize it.
For example:
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
name: custom-repo
namespace: project-ottowa
labels:
k0rdent.mirantis.com/managed: "true"
spec:
url: oci://registry.example.com/templates
type: oci
interval: 10m
Make sure the repository object is in the same namespace in which you will be creating the ClusterTemplate
and ClusterDeployment
.
Creating the ClusterTemplate Resource#
With the source in place, you can now define a ClusterTemplate
that references your Helm chart:
apiVersion: k0rdent.mirantis.com/v1beta1
kind: ClusterTemplate
metadata:
name: custom-aws-standalone
namespace: project-ottowa
spec:
helm:
chartSpec:
chart: my-custom-template
version: 0.1.0
interval: 10m
sourceRef:
kind: HelmRepository
name: custom-repo
At this point, the k0rdent controller validates the chart, reads the default values, and checks any annotations in Chart.yaml
that declare required providers. For example, an AWS template might require infrastructure-aws
, control-plane-k0sproject-k0smotron
, and bootstrap-k0sproject-k0smotron
. The template will only be marked "ready" if those providers are present.
Deploying with a ClusterTemplate#
Creating a ClusterTemplate
does not deploy a cluster. Actual clusters are instantiated through ClusterDeployment
objects, which reference ClusterTemplates
and may override their default values. For example:
apiVersion: k0rdent.mirantis.com/v1beta1
kind: ClusterDeployment
metadata:
name: my-cluster
namespace: project-ottowa
spec:
template: custom-aws-standalone
credential: aws-cluster-identity-cred
config:
clusterLabels: {}
region: us-west-2
controlPlane:
instanceType: t3.small
rootVolumeSize: 32
worker:
instanceType: t3.small
rootVolumeSize: 32
When this object is applied, Flux installs the chart, CAPI providers reconcile their objects, and controllers like k0smotron configure k0s. Eventually, the ClusterDeployment
is marked ready.
Customizing Templates#
Customization is usually as simple as editing values.yaml
. The key is to understand which variables are exposed. You can determine this by inspecting the templates/
directory and seeing where .Values
are used. For example, if a manifest contains:
metadata:
name: {{ .Values.clusterName }}
Then you know that adding clusterName: "my-new-cluster"
to your values file will set the name.
Because Helm supports hierarchies and conditionals, values files can become quite expressive. Over time, organizations often create their own libraries of values files tailored for different environments — dev, staging, production — while reusing the same underlying templates.
Troubleshooting and Validation#
The most common issues when building ClusterTemplates
involve:
- Missing providers: If the template references providers not installed in your management cluster, it will fail validation.
- Schema violations: If you supply a value of the wrong type, Helm will reject it if a schema is defined.
- Flux sync errors: If Flux cannot reach your repository or chart, the template will not resolve.
Debugging usually involves checking Flux logs (kubectl logs -n flux-system deployment/helm-controller
), verifying that sources are labeled correctly, and ensuring that provider CRDs are installed.
Next Steps#
Building a ClusterTemplate
is often the first step toward customizing k0rdent for your environment. Once you understand how charts, values, and templates work together, you can extend the same model to ServiceTemplates for application add-ons and ProviderTemplates for new infrastructure backends.
By embracing templating as the core abstraction, k0rdent gives you a powerful system: clusters, services, and providers all managed through the same consistent pattern, with strong validation, automation, and reuse built in.