Gateway API
By default, this is disabled.
When enabled, vCluster syncs the following Kubernetes Gateway API resources from the tenant cluster to the control plane cluster:
GatewayHTTPRouteTLSRouteReferenceGrantBackendTLSPolicy
GRPCRoute, TCPRoute, and UDPRoute are not synced.
vCluster also enforces Gateway API reference authorization before sync. Cross-namespace allowedRoutes, ReferenceGrant, and unsupported parentRef/backendRef group+kind combinations are validated in the tenant cluster, and rejected resources surface a RefNotPermitted or SyncError event on the tenant object.
The control plane cluster must have standard-channel v1 Gateway API CRDs installed (compatible with the embedded v1.5.1 CRDs) and a Gateway API controller running. vCluster does not install either. To import a host GatewayClass into the tenant cluster, also enable sync.fromHost.gatewayClasses.
Gateway API sync requires a vCluster chart version that includes the sync.toHost.gatewayApi and sync.fromHost.gatewayClasses configuration fields. If Helm rejects either field as an additional property, upgrade vCluster before using these examples.
For native Gateway API sync, use this configuration instead of custom-resource syncing. The custom-resource path is now only needed for Gateway API extensions outside the supported set (for example, Istio waypoint Gateways using the HBONE listener).
Enable Gateway API sync​
sync:
toHost:
gatewayApi:
enabled: true
fromHost:
gatewayClasses:
enabled: true
selector:
matchLabels:
gateway-demo.loft.sh/sync: "yes"
The examples on this page use GatewayClass names such as gw-demo-01 and gw-demo-02. Create those GatewayClass resources in the control plane cluster before applying the tenant examples, or change each gatewayClassName to an existing imported GatewayClass. Replace GATEWAY_CONTROLLER_NAME with the controllerName for your Gateway API controller.
export HOST_CONTEXT="$(kubectl config current-context)"
export GATEWAY_CONTROLLER_NAME="gateway.envoyproxy.io/gatewayclass-controller"
for GATEWAY_CLASS in gw-demo-01 gw-demo-02 gw-demo-03 gw-demo-05 gw-demo-07 gw-demo-09; do
kubectl --context "$HOST_CONTEXT" apply -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: ${GATEWAY_CLASS}
labels:
gateway-demo.loft.sh/sync: "yes"
spec:
controllerName: ${GATEWAY_CONTROLLER_NAME}
EOF
done
Most examples create a Gateway with listener ports 80 or 443. On single-node clusters, only one LoadBalancer service can usually bind each host port at a time. Run the examples one at a time, or use controller-specific settings such as NodePort or custom listener ports.
Example: HTTP route​
The tenant defines a Gateway with an HTTP listener and an HTTPRoute that points at a same-namespace Service backend. vCluster syncs all three objects to the control plane cluster and the host Gateway controller programs the data plane.
Apply the namespace and backend resources first:
apiVersion: v1
kind: Namespace
metadata:
name: gw-demo-01
labels:
gateway-demo.loft.sh/case: "01-http-route-basic"
---
apiVersion: v1
kind: ConfigMap
metadata:
name: basic-echo-html
namespace: gw-demo-01
data:
index.html: |
gateway demo 01 basic http route
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: basic-echo
namespace: gw-demo-01
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: basic-echo
template:
metadata:
labels:
app.kubernetes.io/name: basic-echo
spec:
containers:
- name: nginx
image: nginx:1.27-alpine
ports:
- name: http
containerPort: 80
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html/index.html
subPath: index.html
readOnly: true
volumes:
- name: html
configMap:
name: basic-echo-html
---
apiVersion: v1
kind: Service
metadata:
name: basic-echo
namespace: gw-demo-01
spec:
selector:
app.kubernetes.io/name: basic-echo
ports:
- name: http
port: 80
targetPort: http
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: edge
namespace: gw-demo-01
spec:
gatewayClassName: gw-demo-01
listeners:
- name: http
protocol: HTTP
port: 80
allowedRoutes:
kinds:
- group: gateway.networking.k8s.io
kind: HTTPRoute
namespaces:
from: Same
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: basic
namespace: gw-demo-01
spec:
parentRefs:
- name: edge
sectionName: http
hostnames:
- basic.gw-demo.test
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: basic-echo
port: 80
Inspect the synced status from the tenant context:
kubectl get gateway edge -n gw-demo-01 -o yaml
kubectl get httproute basic -n gw-demo-01 -o yaml
The Gateway should report Accepted=True and usually Programmed=True. The HTTPRoute should report a parent for edge with Accepted=True and ResolvedRefs=True. The host-assigned address mirrors back to status.addresses on the tenant Gateway.
ADDR=$(kubectl get gateway edge -n gw-demo-01 -o jsonpath='{.status.addresses[0].value}')
curl -H 'Host: basic.gw-demo.test' "http://${ADDR}:80/"
Example: TLS passthrough route​
TLSRoute is supported when the host Gateway controller implements it. The tenant defines a Gateway with a TLS listener in passthrough mode and a TLSRoute that selects the backend by SNI. Because the listener is passthrough, the backend itself terminates TLS — it must present a certificate for the route's hostname.
Apply the namespace first:
apiVersion: v1
kind: Namespace
metadata:
name: gw-demo-02
labels:
gateway-demo.loft.sh/case: "02-tls-route-passthrough"
Create a self-signed certificate for the backend hostname and a synced TLS Secret:
openssl req -x509 -newkey rsa:2048 -nodes -days 365 \
-subj "/CN=tls.gw-demo.test" \
-addext "subjectAltName=DNS:tls.gw-demo.test" \
-keyout tls-echo.key \
-out tls-echo.crt
kubectl -n gw-demo-02 create secret tls tls-echo-cert \
--cert=tls-echo.crt \
--key=tls-echo.key
kubectl -n gw-demo-02 annotate secret tls-echo-cert vcluster.loft.sh/force-sync=true
Then create the TLS-terminating backend Service:
apiVersion: v1
kind: ConfigMap
metadata:
name: tls-echo-config
namespace: gw-demo-02
data:
Caddyfile: |
{
auto_https off
}
:8443 {
tls /etc/certs/tls.crt /etc/certs/tls.key
respond "gateway demo 02 tls passthrough\n"
}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: tls-echo
namespace: gw-demo-02
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: tls-echo
template:
metadata:
labels:
app.kubernetes.io/name: tls-echo
spec:
containers:
- name: caddy
image: caddy:2.8-alpine
ports:
- name: https
containerPort: 8443
volumeMounts:
- name: caddy-config
mountPath: /etc/caddy
readOnly: true
- name: tls-echo-cert
mountPath: /etc/certs
readOnly: true
volumes:
- name: caddy-config
configMap:
name: tls-echo-config
- name: tls-echo-cert
secret:
secretName: tls-echo-cert
---
apiVersion: v1
kind: Service
metadata:
name: tls-echo
namespace: gw-demo-02
spec:
selector:
app.kubernetes.io/name: tls-echo
ports:
- name: https
port: 443
targetPort: https
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: edge
namespace: gw-demo-02
spec:
gatewayClassName: gw-demo-02
listeners:
- name: tls
protocol: TLS
port: 443
hostname: tls.gw-demo.test
tls:
mode: Passthrough
allowedRoutes:
kinds:
- group: gateway.networking.k8s.io
kind: TLSRoute
namespaces:
from: Same
---
apiVersion: gateway.networking.k8s.io/v1
kind: TLSRoute
metadata:
name: passthrough
namespace: gw-demo-02
spec:
parentRefs:
- name: edge
sectionName: tls
hostnames:
- tls.gw-demo.test
rules:
- name: tls-echo
backendRefs:
- name: tls-echo
port: 443
ADDR=$(kubectl get gateway edge -n gw-demo-02 -o jsonpath='{.status.addresses[0].value}')
curl -k --connect-to "tls.gw-demo.test:443:${ADDR}:443" "https://tls.gw-demo.test:443/"
TLSRoute is not part of the Gateway API standard channel. Confirm support in the host Gateway controller before relying on this example.
Cross-namespace routing​
Gateway API distinguishes three cross-namespace references: route attachment to a Gateway, backend references from a route, and TLS certificate references on a Gateway listener. vCluster mirrors the upstream rules: routes must be allowed by the Gateway's allowedRoutes, and cross-namespace backend or certificate references must be granted by a ReferenceGrant in the target namespace.
Route attachment by namespace selector​
The Gateway lives in one namespace and accepts routes from any namespace whose label matches the selector.
Apply the edge namespace, route namespace, and backend resources first:
apiVersion: v1
kind: Namespace
metadata:
name: gw-demo-03-edge
labels:
gateway-demo.loft.sh/case: "03-cross-namespace-route-selector"
---
apiVersion: v1
kind: Namespace
metadata:
name: gw-demo-03-apps
labels:
gateway-demo.loft.sh/case: "03-cross-namespace-route-selector"
gateway-demo.loft.sh/route-access: demo-03
---
apiVersion: v1
kind: ConfigMap
metadata:
name: selected-echo-html
namespace: gw-demo-03-apps
data:
index.html: |
gateway demo 03 selected namespace route
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: selected-echo
namespace: gw-demo-03-apps
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: selected-echo
template:
metadata:
labels:
app.kubernetes.io/name: selected-echo
spec:
containers:
- name: nginx
image: nginx:1.27-alpine
ports:
- name: http
containerPort: 80
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html/index.html
subPath: index.html
readOnly: true
volumes:
- name: html
configMap:
name: selected-echo-html
---
apiVersion: v1
kind: Service
metadata:
name: selected-echo
namespace: gw-demo-03-apps
spec:
selector:
app.kubernetes.io/name: selected-echo
ports:
- name: http
port: 80
targetPort: http
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: shared
namespace: gw-demo-03-edge
spec:
gatewayClassName: gw-demo-03
listeners:
- name: http
protocol: HTTP
port: 80
allowedRoutes:
kinds:
- group: gateway.networking.k8s.io
kind: HTTPRoute
namespaces:
from: Selector
selector:
matchLabels:
gateway-demo.loft.sh/route-access: demo-03
The route then targets the Gateway by name and namespace:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: selected
namespace: gw-demo-03-apps
spec:
parentRefs:
- name: shared
namespace: gw-demo-03-edge
sectionName: http
hostnames:
- selector.gw-demo.test
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: selected-echo
port: 80
If the route's namespace does not match the selector, vCluster does not sync the route and emits a RefNotPermitted event on the tenant object. See Reference authorization.
Backend reference via ReferenceGrant​
When an HTTPRoute or TLSRoute points at a Service in a different namespace, that target namespace must contain a ReferenceGrant permitting the route's namespace.
Apply the route namespace, backend namespace, and backend resources first:
apiVersion: v1
kind: Namespace
metadata:
name: gw-demo-05-routes
labels:
gateway-demo.loft.sh/case: "05-cross-namespace-backend-referencegrant"
---
apiVersion: v1
kind: Namespace
metadata:
name: gw-demo-05-backends
labels:
gateway-demo.loft.sh/case: "05-cross-namespace-backend-referencegrant"
---
apiVersion: v1
kind: ConfigMap
metadata:
name: backend-echo-html
namespace: gw-demo-05-backends
data:
index.html: |
gateway demo 05 cross namespace backend allowed
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend-echo
namespace: gw-demo-05-backends
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: backend-echo
template:
metadata:
labels:
app.kubernetes.io/name: backend-echo
spec:
containers:
- name: nginx
image: nginx:1.27-alpine
ports:
- name: http
containerPort: 80
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html/index.html
subPath: index.html
readOnly: true
volumes:
- name: html
configMap:
name: backend-echo-html
---
apiVersion: v1
kind: Service
metadata:
name: backend-echo
namespace: gw-demo-05-backends
spec:
selector:
app.kubernetes.io/name: backend-echo
ports:
- name: http
port: 80
targetPort: http
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: edge
namespace: gw-demo-05-routes
spec:
gatewayClassName: gw-demo-05
listeners:
- name: http
protocol: HTTP
port: 80
allowedRoutes:
kinds:
- group: gateway.networking.k8s.io
kind: HTTPRoute
namespaces:
from: Same
---
apiVersion: gateway.networking.k8s.io/v1
kind: ReferenceGrant
metadata:
name: allow-routes-to-echo
namespace: gw-demo-05-backends
spec:
from:
- group: gateway.networking.k8s.io
kind: HTTPRoute
namespace: gw-demo-05-routes
to:
- group: ""
kind: Service
name: backend-echo
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: backend-grant
namespace: gw-demo-05-routes
spec:
parentRefs:
- name: edge
sectionName: http
hostnames:
- backend-grant.gw-demo.test
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: backend-echo
namespace: gw-demo-05-backends
port: 80
ReferenceGrant is enforcedvCluster validates ReferenceGrant differently depending on the tenancy mode:
- Single-namespace target mode (default): vCluster evaluates the tenant
ReferenceGrantitself. If the grant authorizes the reference, vCluster syncs the route (or Gateway, for cert references); if not, it emitsRefNotPermittedon the tenant object and does not sync. TheReferenceGrantis not synced to the host: every virtual namespace collapses into the configured host namespace, so the cross-namespace reference becomes a same-namespace request that the host Gateway controller does not need a grant for. - Multi-namespace target mode (
sync.toHost.namespaces.enabled: truewithmappings.byName): vCluster syncs theReferenceGrantto the host alongside the route, and the host Gateway controller performs the authorization check.
Either mode supports cross-namespace ReferenceGrant. Choose multi-namespace mode if you want the host controller to enforce the check itself, or to keep per-namespace status visibility on the host.
TLS certificate via ReferenceGrant​
An HTTPS Gateway listener can reference a TLS Secret in a separate namespace if that namespace contains a ReferenceGrant for the Gateway.
Apply the edge namespace and certificate namespace first:
apiVersion: v1
kind: Namespace
metadata:
name: gw-demo-07-edge
labels:
gateway-demo.loft.sh/case: "07-cross-namespace-certificate-referencegrant"
---
apiVersion: v1
kind: Namespace
metadata:
name: gw-demo-07-certs
labels:
gateway-demo.loft.sh/case: "07-cross-namespace-certificate-referencegrant"
Create a synced TLS Secret for the HTTPS listener:
openssl req -x509 -newkey rsa:2048 -nodes -days 365 \
-subj "/CN=cert-grant.gw-demo.test" \
-addext "subjectAltName=DNS:cert-grant.gw-demo.test" \
-keyout edge.key \
-out edge.crt
kubectl -n gw-demo-07-certs create secret tls edge-cert \
--cert=edge.crt \
--key=edge.key
kubectl -n gw-demo-07-certs annotate secret edge-cert vcluster.loft.sh/force-sync=true
Then create the backend resources for the HTTPS route:
apiVersion: v1
kind: ConfigMap
metadata:
name: https-echo-html
namespace: gw-demo-07-edge
data:
index.html: |
gateway demo 07 cross namespace certificate allowed
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: https-echo
namespace: gw-demo-07-edge
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: https-echo
template:
metadata:
labels:
app.kubernetes.io/name: https-echo
spec:
containers:
- name: nginx
image: nginx:1.27-alpine
ports:
- name: http
containerPort: 80
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html/index.html
subPath: index.html
readOnly: true
volumes:
- name: html
configMap:
name: https-echo-html
---
apiVersion: v1
kind: Service
metadata:
name: https-echo
namespace: gw-demo-07-edge
spec:
selector:
app.kubernetes.io/name: https-echo
ports:
- name: http
port: 80
targetPort: http
apiVersion: gateway.networking.k8s.io/v1
kind: ReferenceGrant
metadata:
name: allow-edge-cert
namespace: gw-demo-07-certs
spec:
from:
- group: gateway.networking.k8s.io
kind: Gateway
namespace: gw-demo-07-edge
to:
- group: ""
kind: Secret
name: edge-cert
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: edge
namespace: gw-demo-07-edge
spec:
gatewayClassName: gw-demo-07
listeners:
- name: https
protocol: HTTPS
port: 443
hostname: cert-grant.gw-demo.test
tls:
mode: Terminate
certificateRefs:
- kind: Secret
name: edge-cert
namespace: gw-demo-07-certs
allowedRoutes:
kinds:
- group: gateway.networking.k8s.io
kind: HTTPRoute
namespaces:
from: Same
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: https-app
namespace: gw-demo-07-edge
spec:
parentRefs:
- name: edge
sectionName: https
hostnames:
- cert-grant.gw-demo.test
rules:
- backendRefs:
- name: https-echo
port: 80
Reference authorization​
vCluster validates Gateway API references in the tenant cluster before syncing to the host. Resources with missing grants, unresolved references, or unsupported groups and kinds are not synced. Instead, vCluster records a Warning event on the tenant object so the reason stays close to the user.
| Misconfiguration | Tenant event reason | Inspect |
|---|---|---|
Route attaches to a Gateway whose allowedRoutes does not permit its namespace | RefNotPermitted | kubectl describe httproute <name> -n <route-ns> |
Route references a Service in another namespace without a matching ReferenceGrant | RefNotPermitted | kubectl describe httproute <name> -n <route-ns> |
Gateway references a TLS Secret in another namespace without a matching ReferenceGrant | RefNotPermitted | kubectl describe gateway <name> -n <gateway-ns> |
BackendTLSPolicy references a CA ConfigMap that has no synced host object | SyncError | kubectl describe backendtlspolicy <name> -n <policy-ns> |
Route uses an unsupported parentRef or backendRef group+kind | SyncError | kubectl describe httproute <name> -n <route-ns> |
For deeper recipes (including how to recover from each event), see Gateway API sync troubleshooting.
Example: BackendTLSPolicy​
BackendTLSPolicy tells the Gateway controller to originate TLS to a backend and validate it against a CA bundle. vCluster syncs the policy and translates the CA ConfigMap reference for the host. The policy is only effective if the host Gateway controller implements BackendTLSPolicy.
Apply the namespace first:
apiVersion: v1
kind: Namespace
metadata:
name: gw-demo-09
labels:
gateway-demo.loft.sh/case: "09-backend-tls-policy"
Create a backend certificate and synced CA bundle:
openssl req -x509 -newkey rsa:2048 -nodes -days 365 \
-subj "/CN=backend.gw-demo.test" \
-addext "subjectAltName=DNS:backend.gw-demo.test" \
-keyout backend.key \
-out backend.crt
kubectl -n gw-demo-09 create secret tls backend-cert \
--cert=backend.crt \
--key=backend.key
kubectl -n gw-demo-09 annotate secret backend-cert vcluster.loft.sh/force-sync=true
kubectl -n gw-demo-09 create configmap backend-ca --from-file=ca.crt=backend.crt
kubectl -n gw-demo-09 annotate configmap backend-ca vcluster.loft.sh/force-sync=true
Then create the TLS-speaking backend Service:
apiVersion: v1
kind: ConfigMap
metadata:
name: backend-caddy-config
namespace: gw-demo-09
data:
Caddyfile: |
{
auto_https off
}
:8443 {
tls /etc/certs/tls.crt /etc/certs/tls.key
respond "gateway demo 09 backend tls policy\n"
}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: secure-backend
namespace: gw-demo-09
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: secure-backend
template:
metadata:
labels:
app.kubernetes.io/name: secure-backend
spec:
containers:
- name: caddy
image: caddy:2.8-alpine
ports:
- name: https
containerPort: 8443
volumeMounts:
- name: caddy-config
mountPath: /etc/caddy
readOnly: true
- name: backend-cert
mountPath: /etc/certs
readOnly: true
volumes:
- name: caddy-config
configMap:
name: backend-caddy-config
- name: backend-cert
secret:
secretName: backend-cert
---
apiVersion: v1
kind: Service
metadata:
name: secure-backend
namespace: gw-demo-09
spec:
selector:
app.kubernetes.io/name: secure-backend
ports:
- name: https
port: 443
targetPort: https
appProtocol: https
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: edge
namespace: gw-demo-09
spec:
gatewayClassName: gw-demo-09
listeners:
- name: http
protocol: HTTP
port: 80
hostname: backend-tls.gw-demo.test
allowedRoutes:
kinds:
- group: gateway.networking.k8s.io
kind: HTTPRoute
namespaces:
from: Same
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: backend-tls
namespace: gw-demo-09
spec:
parentRefs:
- name: edge
sectionName: http
hostnames:
- backend-tls.gw-demo.test
rules:
- backendRefs:
- name: secure-backend
port: 443
---
apiVersion: gateway.networking.k8s.io/v1
kind: BackendTLSPolicy
metadata:
name: secure-backend
namespace: gw-demo-09
spec:
targetRefs:
- group: ""
kind: Service
name: secure-backend
validation:
hostname: backend.gw-demo.test
caCertificateRefs:
- group: ""
kind: ConfigMap
name: backend-ca
A successful policy reports an ancestor condition with Accepted=True against the route's parent Gateway. If the referenced CA ConfigMap is not synced to the host, vCluster emits a SyncError event instead. See SyncError: referenced ConfigMap has no synced host object.
CLI guidance​
Connect to the tenant cluster​
Gateway API resources live in the tenant cluster. Connect to the tenant with the vCluster CLI before issuing kubectl commands:
vcluster connect my-vcluster --namespace my-vcluster-host-ns
vcluster connect writes a context entry to your kubeconfig and switches the current context. To run side-by-side checks against the host cluster, export both contexts explicitly:
export TENANT_CONTEXT="$(kubectl config current-context)"
export HOST_CONTEXT=<your-host-kube-context>
There is no separate vcluster verb for Gateway API resources. Inspect them with kubectl against the tenant context; the host data plane is reached using the address from status.addresses on the tenant Gateway.
Inspect Gateway API resources​
kubectl --context "$TENANT_CONTEXT" get gateway,httproute,tlsroute,backendtlspolicy -A
kubectl --context "$TENANT_CONTEXT" -n <ns> get gateway <name> -o yaml
kubectl --context "$TENANT_CONTEXT" -n <ns> describe httproute <name>
kubectl --context "$TENANT_CONTEXT" -n <ns> get events --field-selector type=Warning
ReferenceGrant has no status, so confirm it by checking that the dependent route, Gateway, or BackendTLSPolicy reaches Accepted=True and ResolvedRefs=True.
Discover the host-assigned address​
kubectl --context "$TENANT_CONTEXT" -n <ns> get gateway <name> \
-o jsonpath='{.status.addresses[0].value}'
The address is whatever the host Gateway controller assigned (LoadBalancer IP, NodePort, or controller-published hostname). Tenant clients curl that address directly; vCluster does not proxy data-plane traffic.
Patches​
This feature is an Enterprise feature. See our pricing plans or contact our sales team for more information.
Patches override the default resource syncing rules in your vcluster.yaml configurations.
By default, vCluster syncs specific resources between tenant clusters and the Control Plane Cluster. To modify the sync behavior, you can use patches to specify which fields to edit, exclude, or override during syncing.
For example, vCluster can mirror resources from the tenant cluster to the Control Plane Cluster—any changes made in the tenant cluster are automatically applied to the Control Plane Cluster. The same applies in the other direction if syncing is set up from the Control Plane Cluster to the tenant cluster.
vCluster supports two patch types:
- JavaScript expression patch: Uses JavaScript ES6 expressions to dynamically modify fields during syncing. You can define how a field changes when syncing from a tenant cluster to the Control Plane Cluster, or from the Control Plane Cluster to a tenant cluster.
- Reference patch: Modifies specific fields within a resource to point to different resources. If the referenced resource exists in the Control Plane Cluster, vCluster automatically imports it into the tenant cluster. If the referenced resource exists in the tenant cluster and syncing is configured, vCluster can import it into the Control Plane Cluster.
You can apply a wildcard, using an asterisk (*) in a specified path, to modify all elements of an array or object.
For instance, spec.containers[*].name selects the name field of every container in the spec.containers array, and spec.containers[*].volumeMounts[*] selects all volumeMounts for each container.
When using the asterisk (*) notation, vCluster applies changes individually to every element that matches the path.
JavaScript expression patch​
A JavaScript expression patch allows you to use JavaScript ES6 expressions to change specific fields when syncing between tenant clusters and the Control Plane Cluster. This is useful when modifying resource configurations to align with differing environments or naming conventions between clusters. If your clusters use different container name prefixes, a JavaScript expression patch can automatically update them.
You can define a path for spec.listeners[*].hostname field in vcluster.yaml using the following configuration:
sync:
toHost:
gatewayApi:
enabled: true
patches:
- path: spec.listeners[*].hostname
expression: '"my-prefix-"+value'
# optional reverseExpression to reverse the change from the Control Plane Cluster
# reverseExpression: 'value.slice("my-prefix-".length)'
In the example:
- The path targets the
spec.listeners[*].hostnamefield to override when syncing to the Control Plane Cluster. The*wildcard applies the patch to each matching value individually. "'my-prefix-' + value"defines a JavaScript expression that prepends"my-prefix-"to thespec.listeners[*].hostnamefield in the Control Plane Cluster.
You can use the reverseExpression field to define how to revert changes when syncing from the Control Plane Cluster back to the tenant cluster.
For example, add reverseExpression: 'value.slice("my-prefix-".length)' to vcluster.yaml to remove the "my-prefix-" prefix when syncing back from the Control Plane Cluster to the tenant cluster in the previous example.
To replace value with a hardcoded one, put the desired value in the quotation marks:
sync:
toHost:
gatewayApi:
enabled: true
patches:
- path: spec.listeners[*].hostname
expression: '"my-value"'
The JavaScript expression patch supports a limited set of JavaScript features. For example, it does not support import statements or other Node.js-specific features. You can use standard JavaScript expressions, functions, and operators.
The expression is evaluated in a sandboxed environment, so it cannot access external resources or the host system.
Also, the JavaScript expression must be a valid ES6 expression. This means it should not contain any statements, only expressions that return a value.
A common use is to use ternary operators to conditionally modify values based on their existence or other criteria.
Another common use is the optional chaining operator like in: value.myfield?.anotherfield || "default-value".
Additional functions available:
atob(encodedData): Decodes a base64-encoded string and returns the decoded string. Ex:atob("bXktdmFsdWU=")returnsmy-value.btoa(stringToEncode): Encodes a string in base64 and returns the encoded string. Ex:btoa("my-value")returnsbXktdmFsdWU=.virtualToHostDNS(virtualSvc): Translates a virtual service to its corresponding host FQDN. The domain must follow the format<name>.<namespace>.svc[.<cluster-domain>]. Ex:virtualToHostDNS(nginx.default.svc.cluster.local)returnsnginx-x-default-x-vcluster.vcluster-ns.svc.cluster.local.
These functions are useful for encoding/decoding values or translating services when syncing between clusters, but be sure a value exists before operating on it:
sync:
toHost:
gatewayApi:
enabled: true
patches:
- path: metadata.annotations["encoded-value"]
expression: 'btoa(value)'
- path: metadata.annotations["decoded-value"]
expression: 'atob(value)'
- path: metadata.annotations["referenced-service"]
expression: 'virtualToHostDNS(value)'
There are two kinds of expression categories in the configuration:
expression— Specifies the synchronization rules from tenant clusters to the Control Plane Cluster. Use this to control which resources, fields, or attributes in the tenant clusters are reflected in the Control Plane Cluster.reverseExpression— Specifies the synchronization rules from the Control Plane Cluster to the tenant clusters. Use this to control how updates or resources in the Control Plane Cluster are propagated back to the tenant clusters.
If expression is not defined, no patches will be applied during synchronization from the tenant clusters to the Control Plane Cluster.
Likewise, if reverseExpression is not defined, no patches will be applied during synchronization from the Control Plane Cluster back to the tenant clusters.
For example, when expression is used to apply patches and modify an object in a tenant cluster, the corresponding reverseExpression should
also be defined. This ensures that when the modified object is synchronized from the Control Plane Cluster back to the tenant clusters,
the reverseExpression patches can transform it back to its original form.
To prevent unintended modifications of the synced objects in either direction, define both expression and reverseExpression,
and ensure that the patches in each logically cancel out the changes introduced by the other.
sync:
toHost:
<resource>:
enabled: true
patches:
- path: metadata.annotations["encoded-value"]
expression: 'btoa(value)'
reverseExpression: 'atob(value)'
Context variable​
The context variable is an object supported in JavaScript expression patches, that provides access to tenant cluster data during syncing. The context object includes the following properties:
context.vcluster.name: Name of the tenant cluster.context.vcluster.namespace: Namespace of the tenant cluster.context.vcluster.config: Configuration of the tenant cluster, basicallyvcluster.yamlmerged with the defaults.context.hostObject: Host object (nullif not available).context.virtualObject: Virtual object (nullif not available).context.path: Matched path on the object, useful when using wildcard path selectors (*).
Reference patch​
A reference patch links a field in one resource to another resource. During syncing, vCluster updates the reference and imports the linked resource from the tenant cluster to the Control Plane Cluster or from the Control Plane Cluster to the tenant cluster, depending on the sync direction and whether the resource exists.
You can use reference patches to share resources, such as Secrets or ConfigMaps, between clusters without manually recreating or duplicating them.
For example, if the Control Plane Cluster contains a secret named "my-example-secret", vCluster automatically imports it into the tenant cluster.
Workloads in the tenant cluster can then use the secret without manual syncing.
You can sync between the tenant cluster and the Control Plane Cluster by treating a field value as a Secret reference:
sync:
toHost:
gatewayApi:
enabled: true
patches:
- path: metadata.annotations["my-secret-ref"]
reference:
apiVersion: v1
kind: Secret
In the example:
- The patch treats
metadata.annotations["my-secret-ref"]as the name of a Secret reference. - vCluster rewrites the field to the corresponding Secret name in the target cluster and syncs or imports the referenced Secret when the resource is configured for syncing.
If you have enabled syncing namespaces, reference patches are only required if the namespace is part of the patch. You can use the namespacePath option to specify the path of the namespace of the reference.
patches applies to Gateway resources. To patch the other synced kinds, use the kind-specific list: httpRoutePatches, tlsRoutePatches, backendTLSPolicyPatches, or referenceGrantPatches. Each list accepts the same expression and reference patch shapes as patches.
Config reference​
gatewayApi required object ​
GatewayAPI defines if Gateway API resources created within the virtual cluster should get synced to the host cluster. When enabled, vCluster syncs Gateways, HTTPRoutes, TLSRoutes, BackendTLSPolicies, and ReferenceGrants. GRPCRoutes, TCPRoutes, and UDPRoutes are not synced. ReferenceGrant to[].name translation supports the core Gateway API target kinds Service, Secret, and ConfigMap; in single-namespace mode the grant becomes a same-namespace assertion on host because all virtual namespaces collapse to the configured target namespace. The host cluster must provide standard-channel v1 Gateway API CRDs compatible with the embedded v1.5.1 CRDs; experimental v1alpha* CRDs alone are not supported. Gateway status addresses mirror host network addresses.
gatewayApi required object ​to[].name translation supports the core Gateway API target kinds Service, Secret, and ConfigMap; in single-namespace mode the grant becomes a same-namespace assertion on host because all virtual namespaces collapse to the configured target namespace. The host cluster must provide standard-channel v1 Gateway API CRDs compatible with the embedded v1.5.1 CRDs; experimental v1alpha* CRDs alone are not supported. Gateway status addresses mirror host network addresses.enabled required boolean false ​
Enabled defines if this option should be enabled.
enabled required boolean false ​patches required object[] ​
Patches patch Gateway resources according to the provided specification.
patches required object[] ​path required string ​
Path is the path within the patch to target. If the path is not found within the patch, the patch is not applied.
path required string ​expression required string ​
Expression transforms the value according to the given JavaScript expression.
expression required string ​reverseExpression required string ​
ReverseExpression transforms the value according to the given JavaScript expression.
reverseExpression required string ​reference required object ​
Reference treats the path value as a reference to another object and will rewrite it based on the chosen mode
automatically. In single-namespace mode this will translate the name to "vxxxxxxxxx" to avoid conflicts with
other names, in multi-namespace mode this will not translate the name.
reference required object ​apiVersion required string ​
APIVersion is the apiVersion of the referenced object.
apiVersion required string ​apiVersionPath required string ​
APIVersionPath is optional relative path to use to determine the kind. If APIVersionPath is not found, will fallback to apiVersion.
apiVersionPath required string ​kind required string ​
Kind is the kind of the referenced object.
kind required string ​kindPath required string ​
KindPath is the optional relative path to use to determine the kind. If KindPath is not found, will fallback to kind.
kindPath required string ​namePath required string ​
NamePath is the optional relative path to the reference name within the object.
namePath required string ​namespacePath required string ​
NamespacePath is the optional relative path to the reference namespace within the object. If omitted or not found, namespacePath equals to the
metadata.namespace path of the object.
namespacePath required string ​labels required object ​
Labels treats the path value as a labels selector.
labels required object ​httpRoutePatches required object[] ​
HTTPRoutePatches patch HTTPRoute resources according to the provided specification.
httpRoutePatches required object[] ​path required string ​
Path is the path within the patch to target. If the path is not found within the patch, the patch is not applied.
path required string ​expression required string ​
Expression transforms the value according to the given JavaScript expression.
expression required string ​reverseExpression required string ​
ReverseExpression transforms the value according to the given JavaScript expression.
reverseExpression required string ​reference required object ​
Reference treats the path value as a reference to another object and will rewrite it based on the chosen mode
automatically. In single-namespace mode this will translate the name to "vxxxxxxxxx" to avoid conflicts with
other names, in multi-namespace mode this will not translate the name.
reference required object ​apiVersion required string ​
APIVersion is the apiVersion of the referenced object.
apiVersion required string ​apiVersionPath required string ​
APIVersionPath is optional relative path to use to determine the kind. If APIVersionPath is not found, will fallback to apiVersion.
apiVersionPath required string ​kind required string ​
Kind is the kind of the referenced object.
kind required string ​kindPath required string ​
KindPath is the optional relative path to use to determine the kind. If KindPath is not found, will fallback to kind.
kindPath required string ​namePath required string ​
NamePath is the optional relative path to the reference name within the object.
namePath required string ​namespacePath required string ​
NamespacePath is the optional relative path to the reference namespace within the object. If omitted or not found, namespacePath equals to the
metadata.namespace path of the object.
namespacePath required string ​labels required object ​
Labels treats the path value as a labels selector.
labels required object ​tlsRoutePatches required object[] ​
TLSRoutePatches patch TLSRoute resources according to the provided specification.
tlsRoutePatches required object[] ​path required string ​
Path is the path within the patch to target. If the path is not found within the patch, the patch is not applied.
path required string ​expression required string ​
Expression transforms the value according to the given JavaScript expression.
expression required string ​reverseExpression required string ​
ReverseExpression transforms the value according to the given JavaScript expression.
reverseExpression required string ​reference required object ​
Reference treats the path value as a reference to another object and will rewrite it based on the chosen mode
automatically. In single-namespace mode this will translate the name to "vxxxxxxxxx" to avoid conflicts with
other names, in multi-namespace mode this will not translate the name.
reference required object ​apiVersion required string ​
APIVersion is the apiVersion of the referenced object.
apiVersion required string ​apiVersionPath required string ​
APIVersionPath is optional relative path to use to determine the kind. If APIVersionPath is not found, will fallback to apiVersion.
apiVersionPath required string ​kind required string ​
Kind is the kind of the referenced object.
kind required string ​kindPath required string ​
KindPath is the optional relative path to use to determine the kind. If KindPath is not found, will fallback to kind.
kindPath required string ​namePath required string ​
NamePath is the optional relative path to the reference name within the object.
namePath required string ​namespacePath required string ​
NamespacePath is the optional relative path to the reference namespace within the object. If omitted or not found, namespacePath equals to the
metadata.namespace path of the object.
namespacePath required string ​labels required object ​
Labels treats the path value as a labels selector.
labels required object ​backendTLSPolicyPatches required object[] ​
BackendTLSPolicyPatches patch BackendTLSPolicy resources according to the provided specification.
backendTLSPolicyPatches required object[] ​path required string ​
Path is the path within the patch to target. If the path is not found within the patch, the patch is not applied.
path required string ​expression required string ​
Expression transforms the value according to the given JavaScript expression.
expression required string ​reverseExpression required string ​
ReverseExpression transforms the value according to the given JavaScript expression.
reverseExpression required string ​reference required object ​
Reference treats the path value as a reference to another object and will rewrite it based on the chosen mode
automatically. In single-namespace mode this will translate the name to "vxxxxxxxxx" to avoid conflicts with
other names, in multi-namespace mode this will not translate the name.
reference required object ​apiVersion required string ​
APIVersion is the apiVersion of the referenced object.
apiVersion required string ​apiVersionPath required string ​
APIVersionPath is optional relative path to use to determine the kind. If APIVersionPath is not found, will fallback to apiVersion.
apiVersionPath required string ​kind required string ​
Kind is the kind of the referenced object.
kind required string ​kindPath required string ​
KindPath is the optional relative path to use to determine the kind. If KindPath is not found, will fallback to kind.
kindPath required string ​namePath required string ​
NamePath is the optional relative path to the reference name within the object.
namePath required string ​namespacePath required string ​
NamespacePath is the optional relative path to the reference namespace within the object. If omitted or not found, namespacePath equals to the
metadata.namespace path of the object.
namespacePath required string ​labels required object ​
Labels treats the path value as a labels selector.
labels required object ​referenceGrantPatches required object[] ​
ReferenceGrantPatches patch ReferenceGrant resources according to the provided specification.
referenceGrantPatches required object[] ​path required string ​
Path is the path within the patch to target. If the path is not found within the patch, the patch is not applied.
path required string ​expression required string ​
Expression transforms the value according to the given JavaScript expression.
expression required string ​reverseExpression required string ​
ReverseExpression transforms the value according to the given JavaScript expression.
reverseExpression required string ​reference required object ​
Reference treats the path value as a reference to another object and will rewrite it based on the chosen mode
automatically. In single-namespace mode this will translate the name to "vxxxxxxxxx" to avoid conflicts with
other names, in multi-namespace mode this will not translate the name.
reference required object ​apiVersion required string ​
APIVersion is the apiVersion of the referenced object.
apiVersion required string ​apiVersionPath required string ​
APIVersionPath is optional relative path to use to determine the kind. If APIVersionPath is not found, will fallback to apiVersion.
apiVersionPath required string ​kind required string ​
Kind is the kind of the referenced object.
kind required string ​kindPath required string ​
KindPath is the optional relative path to use to determine the kind. If KindPath is not found, will fallback to kind.
kindPath required string ​namePath required string ​
NamePath is the optional relative path to the reference name within the object.
namePath required string ​namespacePath required string ​
NamespacePath is the optional relative path to the reference namespace within the object. If omitted or not found, namespacePath equals to the
metadata.namespace path of the object.
namespacePath required string ​labels required object ​
Labels treats the path value as a labels selector.
labels required object ​