Skip to content
kix /docs
Install the CLI

How-to guide Adopt existing resources

Take ownership of Helm-managed resources

Move an existing Helm release under Kix without recreating its 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
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:

kix-examples/
❱ kix check how-to-helm-bridge
 TOOL       RESULT  DETAILS                      
 eval       pass    12 manifests evaluated       
 scorecard  pass    0 errors, 5 warnings, 2 info

Inspect the rendered Deployment:

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:

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:

kix-examples/ output excerpt live capture
❱ kix deploy how-to-helm-bridge --dry-run -y
Show outputHide output · 5 lines
⚠ ServiceAccount/reflector@reflector-system cannot update ServiceAccount/reflector@reflector-system: another tool owns fields that Kix is trying to change. Review the fields listed below. If Kix should manage them, rerun with --force-conflicts to take ownership; if the other tool is meant to own them, declare them on the resource with `ownership.cede`. Kubernetes reported: ApiError: Apply failed with 1 conflict: conflict with "helm" using v1: .metadata.labels.app.kubernetes.io/managed-by: Conflict (ErrorResponse { status: "Failure", message: "Apply failed with 1 conflict: conflict with \"helm\" using v1: .metadata.labels.app.kubernetes.io/managed-by", reason: "Conflict", code: 409 })
⚠ ClusterRole/reflector cannot update ClusterRole/reflector: another tool owns fields that Kix is trying to change. Review the fields listed below. If Kix should manage them, rerun with --force-conflicts to take ownership; if the other tool is meant to own them, declare them on the resource with `ownership.cede`. Kubernetes reported: 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: cannot update ServiceAccount/reflector@reflector-system: another tool owns fields that Kix is trying to change. Review the fields listed below. If Kix should manage them, rerun with --force-conflicts to take ownership; if the other tool is meant to own them, declare them on the resource with `ownership.cede`. Kubernetes reported: ApiError: Apply failed with 1 conflict: conflict with "helm" using v1: .metadata.labels.app.kubernetes.io/managed-by: Conflict (ErrorResponse { status: "Failure", message: "Apply failed with 1 conflict: conflict with \"helm\" using v1: .metadata.labels.app.kubernetes.io/managed-by", reason: "Conflict", code: 409 })
Dry run complete: 2 created, 2 configured, 0 unchanged, 2 failed, 5 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.

A chart takeover may conflict on several resources. The dry run carries on past a conflict, so the plan lists every conflicting resource with the manager that owns each field.

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

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:

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:

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.