Fleet Templating

Templating helps you customize deployments for different clusters without maintaining separate configuration files. This helps you:

  • Deploy environment-specific image tags.

  • Use cluster labels to define regional configurations.

  • Generate dynamic values for Helm charts.

  • Reuse computed values across multiple manifests.

Fleet preprocesses the following files:

  • fleet.yaml

  • Helm values files

  • Kubernetes manifests

  • HelmOps resources

When you install Helm charts using Fleet, it is important to distinguish between Fleet-managed files and internal Helm chart files:

  • Helm chart values.yaml (No templating):

    • The default values.yaml file bundled inside a Helm chart does not support Fleet templating. Any substitutions happen during the standard Helm chart rendering phase, before Fleet applies the chart. If you use ${var} in this file, Helm treats it as plain text.

  • Fleet valuesFiles and values (Templating supported):

    • Fleet allows you to reference additional values files through fleet.yaml using the valuesFiles or values keys. These Fleet-specific configurations fully support templating, allowing you to apply dynamic, environment-specific overrides.

How Fleet templating works

Before a Bundle is created, the Fleet controller scans specific files (like fleet.yaml and values.yaml). It searches for ${ } delimiters. These are used instead of standard Go-template {{ }} to avoid collisions with Helm’s own templating engine.

Templating Flow

For example, if you have a values.yaml file with:

values:
  region: ${ .ClusterLabels.region }

If the target cluster contains the label: region: us-west. Then Fleet generates:

values:
  region: us-west

Fleet versus Helm templating

Fleet preprocessing happens before Helm rendering.

Feature Fleet Templating Helm Templating

Delimiter

${ }

{{ }}

Timing

Pre-processing (Manager side)

Rendering (Cluster side)

Context

Cluster Labels, Annotations, Name

Values.yaml, Chart metadata

Processor

Fleet Controller

Helm Binary

For example, if you have a fleet.yaml with:

values:
  cluster: ${ .ClusterName }

config:
  message: "{{ .Values.message }}"

In this example:

  • Fleet resolves ${ .ClusterName }

  • Helm later resolves {{ .Values.message }}

Value precedence

When Fleet prepares the final values for a deployment, it merges configuration from multiple sources. If the same key exists in multiple places, the source with the highest precedence wins. Here are possible sources of values, listed in decreasing order of precedence:

  1. templateValues: Values generated here always overwrite other sources. Use this for logic-heavy variables.

  2. targetCustomizations: Overrides defined for specific cluster groups.

  3. helm.valuesFrom: Values loaded dynamically from ConfigMaps or Secrets on downstream clusters. These take precedence over files and inline values.

  4. helm.valuesFiles: External YAML files referenced in the helm block. These override inline helm.values.

  5. helm.values: Global inline values defined in your Fleet.yaml.

  6. Chart values.yaml: The default values bundled inside the Helm chart.

Value Precedence in templating

Accessing Cluster data

Fleet injects specific cluster information into the template context. You can access these using the following keys:

Variable Description Example

.ClusterName

The metadata name of the Cluster resource.

${ .ClusterName }

.ClusterLabels

Map of labels assigned to the Cluster.

${ .ClusterLabels.env }

.ClusterAnnotations

Map of annotations assigned to the Cluster.

${ .ClusterAnnotations.owner }

.ClusterValues

Custom values in the Cluster’s spec.templateValues.

${ .ClusterValues.apiEndpoint }

.ClusterNamespace

The namespace where the Cluster resource resides.

${ .ClusterNamespace }

Expressions and logic

You can also leverage Sprig functions in Fleet to use complex string manipulation and logic.

Avoid using Sprig functions that produce random output (for example, uuidv4). Randomly generated values change on every evaluation and will trigger endless, continuous redeployments. Always guard against null values when writing your templates to prevent rendering errors.

Default Values

Use default to ensure a deployment doesn’t fail if a label is missing.

values:
  region: ${ default "us-east-1" .ClusterLabels.region }

Conditional Logic

Change configuration based on the environment.

values:
  # Sets 'stable' for prod, 'latest' for others
  imageTag: ${ if eq .ClusterLabels.env "prod" }stable${ else }latest${ end }

String Manipulation

values:
  # Converts cluster name to uppercase
  upperName: ${ upper .ClusterName }

Use templateValues for cluster-specific data

You can define cluster-specific data mappings directly on the downstream cluster using the templateValues field in the Cluster custom resource. These values are then passed into the Fleet templating engine and can be consumed in your fleet.yaml (or HelmOp) files using the .ClusterValues variable.

Variable Reuse

You can define a value once in templateValues and reference it multiple times in the values block using the .TemplateValues prefix.

helm:
  templateValues:
    # 1. Define once
    templateValues:
      environment: production
      region: eu-west
      enableMetrics: "true"
  values:
    values:
      app:
        env: "${ .ClusterValues.environment }"
        region: "${ .ClusterValues.region }"
      metrics:
        enabled: "${ .ClusterValues.enableMetrics }"
        # Example of using ClusterLabels alongside ClusterValues
        siteIdentifier: "${ index .ClusterLabels \"site-id\" }"

Structural Generation (Advanced)

Standard YAML blocks must be valid YAML before processing. This prevents you from using range or if to generate lists or maps. Because templateValues is a map of strings, it bypasses this restriction. Fleet evaluates the string first and then parses the result as YAML.

The following example deploys different image tags depending on the cluster environment.

helm:
  templateValues:
    imageTag: ${ if eq .ClusterLabels.env "prod" }stable${ else }latest${ end }

  values:
    image:
      repository: nginx
      tag: ${ .TemplateValues.imageTag }

This results in

  • If the cluster label is env=prod, then the rendered image tag is stable.

  • If the cluster label is env=dev, then the rendered image tag is latest.

Disable preprocessing

If you use other templating languages in your configurations and want to avoid conflicts with Fleet’s templating engine, you can turn off Fleet’s templating entirely in fleet.yaml.

helm:
  disablePreProcess: true

This helps you template syntax conflicts with Fleet preprocessing, if you want to preserve raw template expressions.

This flowchart below provides a clear path for file processing.

Disable Preprocessing Flow

Escaping expressions

To escape a single expression while keeping the engine active, use a double dollar sign ($$).

For example, value: $${ .ClusterName }, results in value: ${ .ClusterName }.

Test template expressions locally

You can test your values templating and preview how your expressions render using the Fleet command-line interface (CLI). The fleet target command evaluates a local bundle file against a live cluster and outputs the exact resources the Fleet controller would create.

Prerequisites: Unlike the fleet apply command, which can run entirely locally without a cluster, fleet target requires access to a running Kubernetes cluster where Fleet is installed.

  1. Create a local bundle: First, use the fleet apply command to convert your existing local directory into a bundle file. Run fleet apply -o bundle.yaml.

  2. Evaluate the targets: Run the fleet target command and pass the generated bundle file as an argument. You can specify a namespace as an argument so the CLI knows where to look for your target Cluster resources. Run fleet target -f bundle.yaml

  3. Inspect the output: The command evaluates your targeting rules and prints the BundleDeployment and Content resources that would be created. Inspect this output to verify that your template expressions evaluated correctly against the specific cluster labels, names, and values.

Best practices

  • Whitespace Control: Use ${- and -} to strip leading/trailing whitespace, especially in templateValues where YAML indentation is sensitive.

  • Centralize Logic: Use templateValues for complex calculations to keep the main values block clean.

  • Defensive Coding: Always provide a default value for cluster labels to avoid empty string injections.

Troubleshooting templating

Template value is empty

  • Verify that the cluster label or annotation actually exists on the Cluster resource in the management plane.

  • Check that the template expression is valid Go syntax.

  • Ensure preprocessing is enabled for the specific file.

Template expression is not evaluated

  • Verify that the file type supports preprocessing (e.g., fleet.yaml, values.yaml).

  • Ensure disablePreProcess is not enabled in your configuration.

  • Confirm the syntax strictly uses the ${ } delimiter.

Helm template conflicts

  • Remember that Fleet and Helm use different template syntaxes.

  • Use ${ } for Fleet pre-processing and {{ }} for Helm templating.

  • Avoid mixing both in the same expression.

YAML Parse Errors

  • If using templateValues for complex blocks, use whitespace control (${- and -}) to ensure the generated YAML remains structurally valid. Incorrect indentation is the primary cause of deployment failures in logic-heavy templates.