Skip to content

Take ownership of Helm-managed resources

Use a one-time server-side apply takeover when an existing Helm release should become part of a Kix cluster. The Kix definition must render the same resources before you transfer ownership.

This guide uses the Reflector chart. Perform the takeover during a normal change window.

Record the installed chart and values:

❱ helm list --namespace reflector-system
❱ helm get values reflector --namespace reflector-system --all --output yaml > reflector.values.yaml
❱ helm get manifest reflector --namespace reflector-system > reflector.manifest.yaml

Keep these files until the migrated release has been deployed and checked. They provide the inputs and rendered resources you need for comparison.

Pin the installed chart version and use the same release name:

how-to/adoption/helm-bridge-cluster.nix (L16–L24)
instances.reflector-system.reflector = {
package = kix.helmChart {
repo = "https://emberstack.github.io/helm-charts";
name = "reflector";
version = "10.0.60";
hash = "sha256-UdCVcUqJogyUYmGo1HnQ1fMxY7ZEV56+9wQSJfWOhVM=";
releaseName = "reflector";
};
};

View source on GitHub ↗

Copy the release’s non-default values into config.values. Do not proceed with different chart values merely because the Kix definition evaluates.

Check the cluster without contacting Kubernetes:

Run in kix-examples/
❱ kix check how-to-helm-bridge
 TOOL         RESULT  DETAILS                                                       
 eval         pass    11 manifests evaluated                                        
 kubeconform  pass    skipped (this validation tool is not yet integrated with Kix) 
 pluto        pass    skipped (this validation tool is not yet integrated with Kix) 
 kyverno      pass    skipped (this validation tool is not yet integrated with Kix) 
 scorecard    pass    0 errors, 5 warnings, 2 info

Inspect the rendered Deployment:

Run in kix-examples/ Output excerpt
❱ kix build how-to-helm-bridge --output json
{
  "apiVersion": "apps/v1",
  "kind": "Deployment",
  "metadata": {
    "name": "reflector",
    "namespace": "reflector-system"
  },
  "spec": {
    "replicas": 1,
    "selector": {
      "matchLabels": {
        "app.kubernetes.io/instance": "reflector",
        "app.kubernetes.io/name": "reflector"
      }
    },
    "serviceAccountName": "reflector",
    "containers": [
      {
        "name": "reflector",
        "image": "docker.io/emberstack/kubernetes-reflector:10.0.60"
      }
    ]
  }
}

Save the complete Kix output, then compare each resource from the Helm manifest with the resource of the same kind, namespace, and name:

Run in kix-examples/
❱ kix build how-to-helm-bridge --output yaml > reflector.kix.yaml

The Kix output also contains Kix tracking resources. Chart resources gain Kix management metadata and lose Helm release metadata, test hooks, and fields that only repeat Kubernetes defaults. Correct any other difference before deploying.

Ask the API server to validate the takeover without persisting it:

Run in kix-examples/ Output excerpt
❱ kix deploy how-to-helm-bridge --dry-run -y Show output
⚠ ClusterRole/reflector field-ownership conflict on ClusterRole/reflector: another field manager owns one or more fields kix is trying to set. Re-run with --force-conflicts to take ownership. Underlying error: ApiError: Apply failed with 1 conflict: conflict with "helm" using rbac.authorization.k8s.io/v1: .metadata.labels.app.kubernetes.io/managed-by: Conflict (ErrorResponse { status: "Failure", message: "Apply failed with 1 conflict: conflict with \"helm\" using rbac.authorization.k8s.io/v1: .metadata.labels.app.kubernetes.io/managed-by", reason: "Conflict", code: 409 })
✗ halt-on-first-failure: field-ownership conflict on ClusterRole/reflector: another field manager owns one or more fields kix is trying to set. Re-run with --force-conflicts to take ownership. Underlying error: ApiError: Apply failed with 1 conflict: conflict with "helm" using rbac.authorization.k8s.io/v1: .metadata.labels.app.kubernetes.io/managed-by: Conflict (ErrorResponse { status: "Failure", message: "Apply failed with 1 conflict: conflict with \"helm\" using rbac.authorization.k8s.io/v1: .metadata.labels.app.kubernetes.io/managed-by", reason: "Conflict", code: 409 })
Dry run complete: 2 created, 2 configured, 0 unchanged, 1 failed, 6 cancelled
(exit code: 1)

The conflict identifies fields still owned by Helm. Review those fields against the saved manifest and values. If the Kix output is not meant to replace them, change the Kix definition instead of forcing the deployment.

By default, the run stops after the first conflict and ends with 1 failed, 6 cancelled. A chart takeover may conflict on several resources. Run the dry run again with --on-error continue to check the remaining resources:

Run in kix-examples/
❱ kix deploy how-to-helm-bridge --dry-run -y --on-error continue

This still skips resources that depend on a failed resource, but continues checking independent branches of the dependency graph.

Once the rendered resources are equivalent, deploy once with --force-conflicts:

Run in kix-examples/ Output excerpt
❱ kix deploy how-to-helm-bridge --force-conflicts -y
~ Deployment/reflector@reflector-system configured
✔ Deployment/reflector@reflector-system ready
• activation 'how-to-helm-bridge-7zjjps55cybw' → Active
Deploy complete: 4 created, 7 configured, 0 unchanged, 0 failed

Check the workload before changing the Helm release record:

Run in kix-examples/
❱ kix status how-to-helm-bridge
❱ kubectl rollout status deployment/reflector --namespace reflector-system

Helm stores release records as Secrets. Delete the record only after Kix owns the resources and the workload is healthy:

Run in kix-examples/
❱ kubectl delete secret --namespace reflector-system --selector owner=helm,name=reflector
secret "sh.helm.release.v1.reflector.v1" deleted from reflector-system namespace

This leaves the workload in place but makes the release unavailable to future helm upgrade and helm uninstall commands. Retain the saved values and manifest with your migration records.

Use ordinary kix deploy how-to-helm-bridge commands after the takeover. Do not keep --force-conflicts in routine commands.

For more about defining chart-backed packages, see Use Helm charts through the Kix Helm bridge.