Deploying to a Remote Cluster
This tutorial deploys a resource to a second, remote Kubernetes cluster. It demonstrates spec.kubeConfig for targeting a remote cluster, and the mustLocalLookup template function, which reads objects from the local cluster even when the component is deploying to a remote one.
Prerequisites
You need two clusters.
Local cluster (kind) — hosts Flux source-controller and component-operator. If you don’t have one yet, the Cluster Setup guide walks you through creating it.
Remote cluster (kind-target) — the deployment target. No Flux or component-operator installation is needed here. Create it now:
kind create cluster --name kind-target
Switch back the context to the local cluster:
kubectl config use-context kind-kind
1. Create the source secret in the local cluster
Switch to the local cluster context and create a secret that the component will mirror to the remote cluster:
kubectl create secret generic original --from-literal foo=bar
2. Make the remote kubeconfig available as a secret
component-operator reads a kubeconfig from a Kubernetes Secret to authenticate against the remote cluster. The default kubeconfig produced by kind get kubeconfig uses server: https://127.0.0.1:<port> — this is reachable from your laptop, but not from inside a pod. From a pod in the kind cluster, the kind-target API server is reachable at kind-target-control-plane:6443, the hostname of the kind-target control plane container on the shared Docker kind network.
Fetch the kubeconfig, patch the server address, and store it as a secret in one step:
kind get kubeconfig --name kind-target \
| sed 's|server: https://127.0.0.1:[0-9]*|server: https://kind-target-control-plane:6443|' \
| kubectl create secret generic kind-target-kubeconfig \
--from-file=value=/dev/stdin
The key name value is the convention component-operator tries first when spec.kubeConfig.secretRef.key is not set.
3. Create the Blueprint
The Blueprint contains a single template that reads the original secret from the local cluster and renders it as a manifest to be applied to the remote cluster.
# secret-copy-blueprint.yaml
---
apiVersion: core.cs.sap.com/v1alpha1
kind: Blueprint
metadata:
name: secret-copy
namespace: default
spec:
files:
secret.yaml: |
{{- $src := mustLocalLookup "v1" "Secret" "default" "original" }}
apiVersion: v1
kind: Secret
metadata:
name: original-copy
namespace: default
type: Opaque
data: {{ $src.data | toJson }}
kubectl apply -f secret-copy-blueprint.yaml
Why mustLocalLookup and not lookup?
When spec.kubeConfig is set, the regular lookup and mustLookup functions query the remote (target) cluster. The original secret lives in the local cluster (where the component lives), so using lookup would make no sense. mustLocalLookup always queries the local cluster regardless of where the component deploys. The must variant causes the reconciliation to fail immediately if the source secret is absent, rather than silently propagating empty data.
See Impersonation and Remote Clusters for the full reference on local vs. target lookup.
4. Create the Component
# secret-copy-component.yaml
---
apiVersion: core.cs.sap.com/v1alpha1
kind: Component
metadata:
name: secret-copy
namespace: default
spec:
sourceRef:
blueprint:
name: secret-copy
kubeConfig:
secretRef:
name: kind-target-kubeconfig
kubectl apply -f secret-copy-component.yaml
Watch the component converge:
kubectl get component secret-copy -w
Once STATE shows Ready, the secret has been written to the remote cluster.
5. Verify
Check that the secret was created in the remote cluster:
kubectl --context kind-kind-target get secret original-copy -n default
Decode the value to confirm the content matches:
kubectl --context kind-kind-target \
get secret original-copy -o jsonpath='{.data.foo}' | base64 -d
# bar
6. Observe live propagation
Update the source secret in the local cluster:
kubectl patch secret original -p '{"stringData":{"foo":"updated"}}'
mustLocalLookup is evaluated on every reconcile cycle, so the next time component-operator reconciles the component (within the default requeueInterval of 10 minutes) the remote secret will reflect the new value. To trigger a reconcile immediately, touch the component:
kubectl annotate component secret-copy reconcile=now --overwrite
Then verify the remote secret was updated:
kubectl --context kind-kind-target \
get secret original-copy -o jsonpath='{.data.foo}' | base64 -d
# updated
7. Cleanup
Delete the component first — this removes the original secret from the remote cluster:
kubectl delete component secret-copy
Then remove the local resources:
kubectl delete blueprint secret-copy
kubectl delete secret original kind-target-kubeconfig
Optionally delete the remote cluster:
kind delete cluster --name kind-target