Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Dynamic Patching

Dynamic patching is one of the most powerful features of this architecture. It allows per-cluster, per-component value overrides without modifying Git, the component catalog, or any template. Operators can change Helm values, replica counts, feature flags, and more — and Flux reconciles the change automatically.

How Patching Works

sequenceDiagram
    participant Op as Operator
    participant API as flux-resourceset API
    participant DB as Data Store
    participant Flux as Child Cluster (Flux)

    Op->>API: PATCH cluster "us-east-prod-01"<br/>patches.grafana.replicaCount = "3"
    API->>DB: Update cluster document
    API-->>Op: 200 OK

    Note over Flux: Next poll cycle

    Flux->>API: GET /clusters/{dns}/platform-components
    API->>DB: Read cluster + catalog
    API->>API: Merge: inject patches.grafana into grafana input
    API-->>Flux: {"inputs": [{..., "patches": {"replicaCount": "3"}}]}

    Flux->>Flux: ResourceSet renders ConfigMap with replicaCount=3
    Flux->>Flux: HelmRelease references ConfigMap via valuesFrom
    Flux->>Flux: Helm upgrade applies new replica count

The Patches Object

Patches are stored in the cluster document, keyed by component ID:

{
  "cluster_dns": "us-east-prod-01.k8s.example.com",
  "patches": {
    "grafana": {
      "replicaCount": "3",
      "persistence.storageClassName": "ssd"
    },
    "podinfo": {
      "replicaCount": "2",
      "ui.color": "#2f855a",
      "ui.message": "Hello from patches"
    },
    "traefik": {
      "deployment.replicas": "1",
      "service.type": "ClusterIP"
    }
  }
}

Each key in a component’s patches maps to a Helm value path. Dotted keys (like ui.color) map to nested Helm values.

How Patches Become Helm Values

The ResourceSet template renders patches into a ConfigMap, then references it from the HelmRelease via valuesFrom:

flowchart TD
    A["API Response<br/>patches: {replicaCount: '2', ui.color: '#2f855a'}"]
    B["ConfigMap<br/>values-podinfo-cluster"]
    C["HelmRelease<br/>platform-podinfo"]
    D["Helm Chart<br/>podinfo"]

    A -->|"ResourceSet renders"| B
    B -->|"valuesFrom with targetPath"| C
    C -->|"helm upgrade"| D

    B -.- B1["data:<br/>  replicaCount: '2'<br/>  ui.color: '#2f855a'"]
    C -.- C1["valuesFrom:<br/>  - kind: ConfigMap<br/>    valuesKey: replicaCount<br/>    targetPath: replicaCount<br/>  - kind: ConfigMap<br/>    valuesKey: ui.color<br/>    targetPath: ui.color"]

The targetPath in valuesFrom tells Helm where to inject the value in the chart’s values tree. This is a standard Flux HelmRelease feature — the innovation is that the values are computed from the API, not hardcoded in Git.

In the demo template, each generated values ConfigMap is labeled reconcile.fluxcd.io/watch: "Enabled" and each generated HelmRelease uses interval: 1m. This gives fast event-driven upgrades when values change, plus a short periodic poll interval.

Patching via CLI

The demo includes a CLI command to patch any component with dynamic key=value paths:

# Patch podinfo values on demo-cluster-01
./target/debug/flux-resourceset-cli demo patch-component demo-cluster-01 podinfo \
  --set replicaCount=3 \
  --set ui.message="Hello from CLI patch" \
  --set ui.color="#3b82f6"

This updates the cluster document’s patches.podinfo object in the data store.

Patching Use Cases

Use CasePatch ExampleEffect
Scale a component{"replicaCount": "3"}Component scales to 3 replicas
Change UI branding{"ui.color": "#ff0000", "ui.message": "Maintenance"}Application UI reflects new values
Environment-specific tuning{"resources.limits.memory": "512Mi"}Different resource limits per cluster
Feature flags{"feature.newDashboard": "true"}Enable features per cluster
Ingress configuration{"ingress.className": "internal"}Different ingress class per cluster

Patching vs. Other Override Mechanisms

MechanismScopeRequires Git PR?Use Case
Catalog defaultsAll clusters using the componentYes (schema change)Global default values
OCI tag overrideOne cluster, one componentNo (API call)Hotfix or canary version
Component path overrideOne cluster, one componentNo (API call)Component version upgrade
PatchesOne cluster, one componentNo (API call)Value tuning, feature flags, scaling
Template changesAll clusters (template is global)Yes (Git PR)Changing how resources are rendered

Patches are the most granular override — they change individual Helm values without affecting any other cluster or component.

Verifying Patches

After patching, verify the change propagated:

# Reconcile quickly
flux reconcile helmrelease platform-podinfo -n flux-system --with-source

# Check reconcile result
kubectl get hr -n flux-system platform-podinfo \
  -o jsonpath='ready={.status.conditions[?(@.type=="Ready")].status} reason={.status.conditions[?(@.type=="Ready")].reason} action={.status.lastAttemptedReleaseAction}{"\n"}'

# Check the actual deployment
kubectl get deploy -n podinfo podinfo \
  -o jsonpath='replicas={.spec.replicas} color={.spec.template.spec.containers[0].env[?(@.name=="PODINFO_UI_COLOR")].value} message={.spec.template.spec.containers[0].env[?(@.name=="PODINFO_UI_MESSAGE")].value}{"\n"}'

# Check rendered values
kubectl get configmap -n flux-system values-podinfo-demo-cluster-01 \
  -o jsonpath='replicas={.data.replicaCount} color={.data.ui\.color} message={.data.ui\.message}{"\n"}'