Skip to main content

Content Templating

Content templating lets the agent resolve Go template variables inside file contents as files are staged — without modifying source files in git. This solves the most common multi-site problem: each gateway needs subtly different configuration (system name, remote gateway URLs, historian settings) but maintaining one file per gateway in git doesn't scale.

How it works

Add template: true to any mapping. The agent copies the file from the repo into staging, then rewrites it in-place by executing it as a Go template before writing to /ignition-data.

spec:
sync:
profiles:
default:
vars:
systemName: "my-gateway"
mappings:
- source: "config/system-properties"
destination: "config/resources/local/ignition/system-properties"
type: dir
template: true # <-- enable content templating

Any file in config/system-properties/ that contains {{...}} syntax is rendered before being written to the gateway. Files without template syntax are copied as-is (fast path).

Available template variables

VariableExampleDescription
{{.GatewayName}}ignition-site1Gateway identity from annotation or app.kubernetes.io/name label
{{.PodName}}ignition-0Kubernetes pod name (useful for StatefulSet replicas)
{{.PodOrdinal}}0StatefulSet replica index (from apps.kubernetes.io/pod-index label with pod-name fallback; always 0 for non-StatefulSet pods)
{{.Namespace}}productionKubernetes namespace of the gateway pod
{{.CRName}}site1-syncName of the GatewaySync CR
{{.Ref}}refs/heads/mainGit ref being synced
{{.Commit}}abc1234Full git commit SHA
{{.Labels.key}}factory-northAny pod label
{{.Vars.key}}productionProfile-level or default-level variable

The systemName use case

Each Ignition gateway must have a unique systemName in config/resources/local/ignition/system-properties/config.json. Without content templating you'd need one file per gateway in git:

config/
system-properties/
site1/config.json # {"systemName": "site1-gw1"}
site2/config.json # {"systemName": "site2-gw1"}
... # 20 near-identical files

With content templating, one file in git handles all gateways:

config/system-properties/config.json
{
"systemName": "{{.GatewayName}}",
"httpPort": 8088,
"useSSL": false
}

Configure the mapping to template this directory:

spec:
sync:
profiles:
default:
mappings:
- source: "config/system-properties"
destination: "config/resources/local/ignition/system-properties"
type: dir
template: true

Each gateway receives its own rendered version with its unique system name, resolved at sync time.

Using vars for environment-specific values

Combine template: true with vars to inject deployment-mode or region-specific values.

vars in defaults (shared across all profiles)

spec:
sync:
defaults:
vars:
region: "us-east-1"
environment: "production"
profiles:
frontend:
vars:
deploymentMode: "frontend" # overrides nothing from defaults
mappings:
- source: "config/base"
destination: "config/resources/core"
type: dir
template: true
backend:
vars:
deploymentMode: "backend"
mappings:
- source: "config/base"
destination: "config/resources/core"
type: dir
template: true

A file in config/base/config.json can now reference both profile-level and default-level vars:

{
"systemName": "{{.GatewayName}}",
"region": "{{.Vars.region}}",
"deploymentMode": "{{.Vars.deploymentMode}}",
"environment": "{{.Vars.environment}}"
}

Merge semantics: Profile vars override default vars on a per-key basis. Keys in defaults but not in the profile are inherited; keys in the profile override the default value.

StatefulSet replica identity with {{.PodName}}

For StatefulSets with multiple replicas, each pod needs a unique system name. Use {{.PodName}} which resolves to the pod's Kubernetes name (e.g., ignition-0, ignition-1):

config/system-properties/config.json
{
"systemName": "{{.PodName}}",
"httpPort": 8088
}

For StatefulSets, {{.PodName}} is the ordinal-suffixed name from the downward API. This gives each replica a deterministic, unique system name without any manual configuration.

Deployment mode overlay

Use vars to select a configuration overlay directory at sync time:

spec:
sync:
profiles:
frontend:
vars:
deploymentMode: "frontend"
mappings:
- source: "config/base"
destination: "config/resources/core"
type: dir
- source: "config/overlays/{{.Vars.deploymentMode}}"
destination: "config/resources/{{.Vars.deploymentMode}}"
type: dir

The {{.Vars.deploymentMode}} in the source path selects config/overlays/frontend/ from git — a directory that only exists for this profile. The destination path separates overlays from core config so Ignition reads from both directories at runtime.

Alternative: JSON patches for targeted field updates

If you only need to update a few specific fields in JSON files — rather than authoring full {{...}} syntax in the source — consider JSON Patches instead. Patches are more surgical: you specify an sjson dot-notation path and the value to set, and the agent applies the change after staging without touching the rest of the file.

ApproachWhen to use
template: trueSource files are authored with {{...}} syntax; works on any text format
patchesYou want to override specific JSON field values without modifying source files

Both can be used on the same mapping — template: true runs first, then patches are applied.

Binary file protection

Files containing null bytes (\x00) are rejected with an error when template: true is set. This prevents accidental corruption of images, compiled modules, or other binary files.

If a mapping includes both text and binary files, either:

  1. Use separate mappings — one with template: true for text, one without for binary files
  2. Move binary assets to a path excluded by excludePatterns

Error messages

ErrorCauseFix
template=true on binary file is not supported: <path>File contains null bytesUse a separate mapping for binary files
executing template "<tmpl>": map has no entry for key "<key>"{{.Vars.key}} references a key not in varsAdd the missing key to vars in the profile or defaults
parsing template "<tmpl>": ...Invalid Go template syntax in fileFix the {{...}} syntax in the source file

Full example

apiVersion: stoker.io/v1alpha1
kind: GatewaySync
metadata:
name: multi-site-sync
namespace: production
spec:
git:
repo: "https://github.com/org/ignition-config.git"
ref: "main"
auth:
githubApp:
appId: 123456
installationId: 789012
privateKeySecretRef:
name: github-app-key
key: privateKey
gateway:
port: 8088
api:
secretName: gw-api-key
sync:
defaults:
vars:
environment: "production"
historyProvider: "db-historian"
excludePatterns:
- "**/.git/"
- "**/.gitkeep"
- "**/.resources/**"
profiles:
site:
vars:
deploymentMode: "standard"
mappings:
- source: "config/shared"
destination: "config/resources/core"
type: dir
- source: "config/system-properties"
destination: "config/resources/local/ignition/system-properties"
type: dir
template: true # resolves {{.GatewayName}} in system name
- source: "projects"
destination: "projects"
type: dir
required: true