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/v1beta1for its Ingress resource. Kubernetes 1.22 permanently removed this API — the cluster's API server no longer recognizes it, sokubectlcannot instantiate the object and the release fails entirely. - How to fix it: Update the Ingress manifest to
apiVersion: networking.k8s.io/v1, add the requiredpathTypefield, and migrate thebackendstanza to the new nestedservice.name/service.port.numberstructure. - Fast path: Use our Client-Side Sandbox below to auto-refactor this — paste your
templates/ingress.yamland 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 upgradecalls 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
failedstate, requiringhelm rollbackor manual Secret deletion before the next attempt. - Cascading failures: If this chart is a dependency (
requirements.yaml/Chart.yamldependencies), the parent chart's install also fails entirely — taking down any co-deployed services in the samehelm installinvocation.
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:
apiVersionchanges.- Each path entry requires an explicit
pathTypefield (Prefix,Exact, orImplementationSpecific). - The
backendfield is restructured from a flat map to a nestedserviceobject.
- 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.