Patching
| Enterprise | ||||
|---|---|---|---|---|
| Available in these plans | Free | Dev | Prod | Scale |
| Sync Patches | ||||
Use patches when a resource needs different field values on each side of the sync boundary. For example, you can rewrite image references to a private registry, or rewrite fields that contain the name of another synced object that vCluster translates. Without a patch, vCluster applies its built-in translations and otherwise leaves synced fields unchanged.
Patches are translation rules. When a resource changes, vCluster rewrites the fields that match your patch configuration and writes the result to the target cluster.
Patch lists live under the resource's sync direction. Use sync.toHost.<resource>.patches for resources synced from the tenant cluster to the control plane cluster, and sync.fromHost.<resource>.patches for resources synced from the control plane cluster into the tenant cluster.
Path syntaxβ
A path identifies the field in the Kubernetes object the patch targets. Patch paths use a dot notation.
Bracket notationβ
Use bracket notation for map keys that contain ., [, or ].
patches:
- path: metadata.annotations["example.com/key"]
expression: '"patched-" + value'
All entriesβ
Use [*] to match all elements of an array or all keys of a map. * does not match patterns or globs. Rather, it selects every entry at that position in the path. vCluster runs the patch once for each.
You can use [*] multiple times in a single path. spec.containers[*].volumeMounts[*].mountPath applies the patch to every mountPath across every container.
patches:
- path: spec.containers[*].env[*].value
expression: 'value.replace("internal.svc", "internal.svc.cluster.local")'
Patch modesβ
Choose the mode based on what the field contains:
| When you need to transform ... | Use |
|---|---|
| A field value | expression / reverseExpression |
| A field referencing another Kubernetes object | reference |
| Labels on a custom resource | labels |
You can use expression and reverseExpression together in one entry.
Validation rulesβ
- Each patch requires a
path. - Each patch uses exactly one mode.
expressionandreverseExpressioncan coexist in one entry, but neither can be combined withreferenceorlabels. - A resource's patch list cannot contain duplicate paths.
metadata.nameandmetadata.namespaceare not valid patch paths.- Reference patches require a vCluster mapper for the referenced resource type.
Expression patchesβ
Expression patches run JavaScript against each matched value. The expression receives value as the current field value and its return value replaces it.
For sync.toHost, expression transforms changes from the tenant cluster to the control plane cluster. reverseExpression transforms changes from the control plane cluster back to the tenant cluster.
If you omit expression, vCluster drops that field from the patch when syncing to the control plane cluster. If you omit reverseExpression, it drops the field when syncing back to the tenant cluster. Write only one to create a one-way transform.
For sync.fromHost, the transform only happens in one direction, from the control plane cluster to the tenant cluster. For most fromHost resources, expression transforms values as they arrive from the control plane cluster. ConfigMaps, Secrets, and custom resources are exceptions that use reverseExpression for this direction. See the Supported resources table for which keyword each resource requires. If you configure the wrong keyword for a resource, vCluster silently skips the expression and syncs the field unchanged.
sync:
toHost:
pods:
enabled: true
patches:
- path: spec.containers[*].image
expression: 'value.replace("docker.io/", "registry.internal/")'
In this example, any container image from docker.io in the tenant cluster is rewritten when the Pod syncs to the control plane cluster. A Pod with image: docker.io/nginx:latest runs as image: registry.internal/nginx:latest on the control plane cluster.
sync:
toHost:
services:
enabled: true
patches:
- path: spec.ports[*].name
expression: '"tenant-" + value'
reverseExpression: 'value.replace(/^tenant-/, "")'
In this example, a port named http in the tenant cluster becomes tenant-http on the control plane cluster. When a change flows back from the control plane cluster, the prefix is stripped and the port is http again in the tenant cluster.
sync:
fromHost:
configMaps:
enabled: true
mappings:
byName:
"default/my-cm": "tenant/my-cm"
patches:
- path: metadata.annotations[*]
reverseExpression: 'value.startsWith("www.") ? value.slice(4) : value'
In this example, the ConfigMap my-cm from the default namespace on the control plane cluster is synced into the tenant namespace in the tenant cluster. For every annotation value, if the value starts with www., that prefix is stripped in the tenant cluster. An annotation value of www.example.com on the control plane cluster appears as example.com in the tenant cluster. The control plane cluster object is unchanged.
JavaScript runtimeβ
Expressions can use these variables:
| Variable | Description |
|---|---|
value | The matched value. |
valueExists | true when the matched path exists in the patch. |
context.vcluster.name | Tenant cluster name. |
context.vcluster.namespace | Tenant cluster namespace in the control plane cluster. |
context.vcluster.config | Effective vcluster.yaml configuration. |
context.hostObject | Control plane cluster object, or null when unavailable. |
context.virtualObject | Tenant cluster object, or null when unavailable. |
context.path | The concrete matched path, useful with wildcards. |
Expressions can use these helper functions:
| Function | Description |
|---|---|
btoa(value) | Base64-encode a string. |
atob(value) | Base64-decode a string. |
virtualToHostDNS(value) | Rewrite a tenant service DNS name to the control plane cluster service DNS name. |
virtualToHostDNS supports service DNS names in the form <name>.<namespace>.svc or <name>.<namespace>.svc.<cluster-domain>.
Empty pathsβ
Empty paths are an advanced use case. Use them when you need to read multiple fields to decide what to change, or when you need to set multiple fields in a single expression.
With a normal path, value is the single field the patch targets. With an empty path, value is the entire set of fields being changed in this sync operation. This is only the changed fields, not the full object. The expression must return the modified change set.
patches:
- path: ""
expression: |
(value => {
value.metadata ??= {};
value.metadata.annotations ??= {};
value.metadata.annotations["patched-by"] = "vcluster";
return value;
})(value)
If the incoming change set is {"spec": {"replicas": 3}}, the expression receives that object, adds the annotation, and returns {"spec": {"replicas": 3}, "metadata": {"annotations": {"patched-by": "vcluster"}}}. Both changes are then applied together.
Reference patchesβ
Reference patches tell vCluster that a field contains the name of another synced object. When syncing, vCluster rewrites the name to its translated value in the target cluster.
vCluster renames synced objects on the control plane cluster to avoid collisions across tenant clusters. A Secret named my-secret in the default namespace becomes default-x-my-secret-x-my-vcluster on the control plane cluster. Any field that holds that name must be rewritten to match, or the reference breaks.
Simple reference patchβ
Use a simple reference patch when the field value is a plain string holding only the object name.
sync:
toHost:
pods:
enabled: true
patches:
- path: metadata.annotations["example.com/secret-name"]
reference:
apiVersion: v1
kind: Secret
If a Pod in the tenant cluster has the annotation example.com/secret-name: my-secret, vCluster rewrites it when syncing the Pod to the control plane cluster. On the control plane cluster, the annotation becomes example.com/secret-name: default-x-my-secret-x-my-vcluster. Without the patch, the annotation stays as my-secret, which would not match the actual Secret name on the control plane cluster.
Structured reference patchβ
Use a structured reference patch when the field value is a nested object that contains the name as a sub-field, such as a Kubernetes ObjectReference or LocalObjectReference.
sync:
toHost:
customResources:
widgets.example.com:
enabled: true
patches:
- path: spec.secretRef
reference:
apiVersion: v1
kind: Secret
namePath: name
namespacePath: namespace
In the tenant cluster, spec.secretRef holds an object like:
name: my-secret
namespace: default
namePath: name and namespacePath: namespace are paths relative to that object, pointing to the sub-fields vCluster should rewrite. On the control plane cluster, those values become the translated Secret name and namespace:
name: default-x-my-secret-x-my-vcluster
namespace: my-vcluster-ns
The values you set for namePath and namespacePath depend on how the resource defines its reference. If the sub-field were named secretName instead of name, you would write namePath: secretName.
namePath is required when you set namespacePath, kindPath, or apiVersionPath.
Labels patchesβ
Use labels patches only with custom resource sync. They are not supported on built-in resources.
vCluster translates pod label keys when syncing to the control plane cluster to prevent collisions across tenant clusters. If a custom resource has a field containing label selectors, such as spec.selector.matchLabels, those keys must be translated the same way or the selector will not match any pods on the control plane cluster. Setting labels: {} on a path tells vCluster to apply that translation to the field. The labels key takes an empty object β there are no configuration options inside it.
sync:
toHost:
customResources:
widgets.example.com:
enabled: true
patches:
- path: spec.selector.matchLabels
labels: {}
If spec.selector.matchLabels held {app: my-app} in the tenant cluster, vCluster rewrites the key to its translated form on the control plane cluster so the selector continues to match the correct pods.
Supported resourcesβ
To the control plane clusterβ
These resources sync tenant cluster state to the control plane cluster. Use expression for changes flowing outbound and reverseExpression for changes flowing back.
| Resource | Config key |
|---|---|
| Pods | sync.toHost.pods.patches |
| ConfigMaps | sync.toHost.configMaps.patches |
| Secrets | sync.toHost.secrets.patches |
| Services | sync.toHost.services.patches |
| Endpoints | sync.toHost.endpoints.patches |
| EndpointSlices | sync.toHost.endpointSlices.patches |
| Ingresses | sync.toHost.ingresses.patches |
| NetworkPolicies | sync.toHost.networkPolicies.patches |
| Gateway API: HTTPRoutes | sync.toHost.gatewayApi.httpRoutes.patches |
| Gateway API: TLSRoutes | sync.toHost.gatewayApi.tlsRoutes.patches |
| Gateway API: BackendTLSPolicies | sync.toHost.gatewayApi.backendTLSPolicies.patches |
| Gateway API: ReferenceGrants | sync.toHost.gatewayApi.referenceGrants.patches |
| PersistentVolumeClaims | sync.toHost.persistentVolumeClaims.patches |
| PersistentVolumes | sync.toHost.persistentVolumes.patches |
| StorageClasses | sync.toHost.storageClasses.patches |
| VolumeSnapshots | sync.toHost.volumeSnapshots.patches |
| ResourceClaims | sync.toHost.resourceClaims.patches |
| ResourceClaimTemplates | sync.toHost.resourceClaimTemplates.patches |
| PodDisruptionBudgets | sync.toHost.podDisruptionBudgets.patches |
| Custom resources | sync.toHost.customResources.<resource>.patches |
From the control plane clusterβ
These resources sync control plane cluster state into the tenant cluster. For most resources, expression transforms values as they arrive from the control plane cluster, which matches the natural reading of the keyword for this direction. ConfigMaps, Secrets, and custom resources are the exception: their sync implementations share the same direction model as sync.toHost, where reverseExpression means the control planeβtenant direction. Use the third column to confirm which keyword applies to each resource rather than reasoning from the keyword names alone.
| Resource | Config key | Use for control planeβtenant |
|---|---|---|
| ConfigMaps | sync.fromHost.configMaps.patches | reverseExpression |
| Secrets | sync.fromHost.secrets.patches | reverseExpression |
| Custom resources | sync.fromHost.customResources.<resource>.patches | reverseExpression |
| CSI Drivers | sync.fromHost.csiDrivers.patches | expression |
| CSI Nodes | sync.fromHost.csiNodes.patches | expression |
| CSI Storage Capacities | sync.fromHost.csiStorageCapacities.patches | expression |
| Device Classes | sync.fromHost.deviceClasses.patches | expression |
| Events | sync.fromHost.events.patches | expression |
| Gateway Classes | sync.fromHost.gatewayClasses.patches | expression |
| Gateways | sync.fromHost.gateways.patches | expression |
| Ingress Classes | sync.fromHost.ingressClasses.patches | expression |
| Nodes | sync.fromHost.nodes.patches | expression |
| Priority Classes | sync.fromHost.priorityClasses.patches | expression |
| Runtime Classes | sync.fromHost.runtimeClasses.patches | expression |
| Storage Classes | sync.fromHost.storageClasses.patches | expression |
| Volume Snapshot Classes | sync.fromHost.volumeSnapshotClasses.patches | expression |
Troubleshootingβ
| Symptom | What to check |
|---|---|
| Pro feature license error | Confirm the tenant cluster uses the Pro image and has a license from vCluster Platform. See Resolve Pro feature license errors. |
| Patch appears to do nothing | Confirm the path exists in the merge patch for the update. Patches run on changed fields only, not the full object. On create, vCluster converts the object to a patch, so more fields are available. |
sync.fromHost patch appears to do nothing | Confirm you are using the correct expression keyword for the resource. vCluster silently skips the wrong keyword with no error. See the Supported resources table. |
| A field stops syncing in one direction | Check whether you have an expression or reverseExpression entry for that direction. Omitting the expression for a direction causes vCluster to drop that field from the patch. |
| An object is stuck in a failing sync loop | An expression error is likely. Check vCluster pod logs for messages containing apply patches host object or apply patches virtual object. No event or status condition is set on the object. |
| A field is being deleted instead of transformed | The expression may be returning null or undefined. vCluster deletes the field when an expression returns either value. |
| Reference patch fails with a missing mapper error | The referenced resource type must be synced or otherwise known to vCluster. |