Initializing Enclave...

Fixing Helm 'no matches for kind Ingress in version networking.k8s.io/v1beta1' on Kubernetes 1.22+

Threat/Impact Level: HIGH | Downtime Risk: HIGH | Time to Fix: 5–15 mins


TL;DR

  • What broke: Your Helm chart hardcodes apiVersion: networking.k8s.io/v1beta1 for its Ingress resource. Kubernetes 1.22 permanently removed this API — the cluster's API server no longer recognizes it, so kubectl cannot instantiate the object and the release fails entirely.
  • How to fix it: Update the Ingress manifest to apiVersion: networking.k8s.io/v1, add the required pathType field, and migrate the backend stanza to the new nested service.name / service.port.number structure.
  • Fast path: Use our Client-Side Sandbox below to auto-refactor this — paste your templates/ingress.yaml and it rewrites the manifest locally without touching your cluster or leaking your hostnames.

The Incident (What Does the Error Mean?)

Raw error output from helm install or helm upgrade:

Error: INSTALLATION FAILED: unable to build kubernetes objects from release manifest:
[unable to recognize "": no matches for kind "Ingress" in version "networking.k8s.io/v1beta1"]

The Kubernetes API server uses a registry of Group/Version/Kind (GVK) tuples. networking.k8s.io/v1beta1 was graduated to networking.k8s.io/v1 in Kubernetes 1.19 and hard-removed in 1.22 (released August 2021). When Helm renders the chart templates and sends the manifest to the API server via a kubectl apply-equivalent call, the server returns a 404 on the GVK lookup. Helm interprets this as a fatal pre-flight failure and rolls back immediately — nothing is deployed. No pods, no services, no ingress rules. Your application is completely down if this is an upgrade path.


The Attack Vector / Blast Radius

This is not a security exploit, but the blast radius is a full deployment outage:

  • New cluster provisioning: Any cluster on EKS 1.22+, GKE 1.22+, AKS 1.22+, or vanilla kubeadm 1.22+ will reject every Helm release that contains this chart. CI/CD pipelines fail at the deploy stage.
  • In-place cluster upgrades: A cluster upgraded from 1.21 → 1.22 will cause all subsequent helm upgrade calls to fail, even for charts that were previously installed successfully. The existing Ingress object in etcd survives the upgrade, but Helm can no longer manage it.
  • Helm release state corruption: Because Helm stores its release history as Secrets in the cluster, a failed install/upgrade can leave the release in a failed state, requiring helm rollback or manual Secret deletion before the next attempt.
  • Cascading failures: If this chart is a dependency (requirements.yaml / Chart.yaml dependencies), the parent chart's install also fails entirely — taking down any co-deployed services in the same helm install invocation.

How to Fix It

Basic Fix — Update the Ingress template directly

Locate templates/ingress.yaml inside your chart. The v1 spec has three breaking structural changes from v1beta1:

  1. apiVersion changes.
  2. Each path entry requires an explicit pathType field (Prefix, Exact, or ImplementationSpecific).
  3. The backend field is restructured from a flat map to a nested service object.
- apiVersion: networking.k8s.io/v1beta1
+ apiVersion: networking.k8s.io/v1
  kind: Ingress
  metadata:
    name: my-app-ingress
    annotations:
      nginx.ingress.kubernetes.io/rewrite-target: /
  spec:
    rules:
    - host: app.example.com
      http:
        paths:
-       - path: /
-         backend:
-           serviceName: my-app-svc
-           servicePort: 80
+       - path: /
+         pathType: Prefix
+         backend:
+           service:
+             name: my-app-svc
+             port:
+               number: 80

Enterprise Best Practice — Helm chart version gate + conditional template rendering

If you maintain a chart consumed by multiple teams across mixed cluster versions (unlikely post-2023 but still seen in air-gapped environments), use Helm's built-in Capabilities object to render the correct API version at template time:

- apiVersion: networking.k8s.io/v1beta1
+ {{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion }}
+ apiVersion: networking.k8s.io/v1
+ {{- else }}
+ apiVersion: networking.k8s.io/v1beta1
+ {{- end }}
  kind: Ingress
  metadata:
    name: {{ include "mychart.fullname" . }}
  spec:
    rules:
    - host: {{ .Values.ingress.host }}
      http:
        paths:
+       {{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion }}
        - path: {{ .Values.ingress.path | default "/" }}
+         pathType: {{ .Values.ingress.pathType | default "Prefix" }}
          backend:
+           service:
+             name: {{ include "mychart.fullname" . }}
+             port:
+               number: {{ .Values.service.port }}
+       {{- else }}
+       - path: {{ .Values.ingress.path | default "/" }}
+         backend:
+           serviceName: {{ include "mychart.fullname" . }}
+           servicePort: {{ .Values.service.port }}
+       {{- end }}

Also run helm template locally before pushing:

# Render manifests and pipe through kubectl dry-run against your actual cluster
helm template my-release ./mychart | kubectl apply --dry-run=server -f -

--dry-run=server (not client) hits the real API server GVK registry — it will catch deprecated API versions before helm install does.


💡 Tired of pasting proprietary configs into ChatGPT? Generic AI tools log your company's ARNs, DB strings, and private keys. StackEngine is a zero-backend, pure Client-Side WASM utility. Drop your failing config into the sandbox above. We redact your secrets locally in the browser and auto-generate the refactored code using your own API key.


Prevention in CI/CD

1. pluto — the definitive deprecated API detector for Helm charts

# Install
brew install FairwindsOps/tap/pluto

# Scan a rendered chart before deploy
helm template my-release ./mychart | pluto detect -

# Expected output for a bad chart:
# NAME               KIND      VERSION                          REPLACEMENT                REMOVED   DEPRECATED
# my-app-ingress     Ingress   networking.k8s.io/v1beta1        networking.k8s.io/v1       true      true

Add to your GitHub Actions pipeline:

- name: Check deprecated Kubernetes APIs
  run: |
    helm template ${{ env.RELEASE_NAME }} ./chart | pluto detect --target-versions k8s=v1.27.0 -

2. Conftest + OPA policy to block v1beta1 Ingress at PR time

# policy/deny_deprecated_ingress.rego
package main

deny[msg] {
  input.apiVersion == "networking.k8s.io/v1beta1"
  input.kind == "Ingress"
  msg := sprintf("DENIED: Ingress '%v' uses removed API networking.k8s.io/v1beta1. Migrate to networking.k8s.io/v1.", [input.metadata.name])
}
helm template my-release ./mychart | conftest test -p policy/ -

3. Renovate Bot / Dependabot for Helm chart updates

If the deprecated API is inside a third-party chart (e.g., an old version of ingress-nginx, cert-manager, or prometheus-community charts), the fix is a chart version bump, not manual template editing. Pin your chart versions in Chart.yaml and configure Renovate to open PRs on chart updates:

# renovate.json
{
  "extends": ["config:base"],
  "helm-values": {
    "fileMatch": ["Chart\\.yaml$"]
  }
}

4. Enforce minimum chart API versions in ArgoCD

If you use ArgoCD, enable resource tracking with server-side apply and configure the resource.customizations to reject deprecated GVKs before sync.

Related Diagnostics

"Part of the Syntax Utility Matrix."

View all 153 Syntax Tools →