Check what Flux would change before it adopts something that is already running

Handing a kubectl-applied add-on or Secret to Flux should change nothing. Two read-only checks prove it: kubectl diff and a server-side dry run under Flux's own field manager. And why you must never print the diff of a Secret.

I had two kinds of things to hand over to Flux: an add-on that was installed once with kubectl apply, and a handful of Secrets that an Ansible role had been creating. In both cases the goal is that Flux takes ownership and nothing changes. You can prove that before you merge anything.

Vendor the manifest, don’t wrap it in a HelmRelease

For an add-on that came from a plain manifest, the lowest-risk move is to commit that exact manifest, with a kustomization.yaml and a Flux Kustomization (prune: true, wait: true) next to it. The objects stay identical. A HelmRelease would have to adopt resources that carry no Helm ownership labels.

Two read-only checks

kubectl diff -f components.yaml
kubectl apply -k path/to/the/kustomization --server-side --dry-run=server \
  --field-manager=kustomize-controller

The first shows any difference against the running objects. The second asks the API server whether Flux, under its own field manager name, could apply the objects without an ownership conflict. For my add-on: no differences, and every object accepted.

Dropping extraManifests from a Talos config doesn’t delete what it deployed either, so ownership has to move first.

Secrets: use the exit code, not the output

For an encrypted Secret in Git, the equivalent check is

sops -d secret.sops.yaml | kubectl diff --server-side --force-conflicts -f -
echo $?

Exit 0 means Flux would change nothing, 1 means there is a difference. A Secret that was created with kubectl apply differs only in the last-applied annotation, which Flux drops, and in gaining type: Opaque. That counts as harmless.

The thing I’d like to have known earlier: don’t print that diff to filter it afterwards. If the live object has the kubectl.kubernetes.io/last-applied-configuration annotation, the diff prints the whole base64 data of the Secret inside that one annotation line. A sed that strips key: value lines doesn’t catch it. Look at the exit code, or compare hashes.