| Tested on | Rocky Linux 10.2 (Red Quartz) workstation |
|---|---|
| Package | kubectl 1.36.3 |
| Applies to | Any host with kubectl configured; any Kubernetes cluster |
| Cert prep | CKA · CKAD |
| Lab environment | Multi-node kubeadm cluster with containerd — install Kubernetes with kubeadm |
| Privilege | Normal user (no sudo required on the workstation) |
| Scope | Deprecated versus removed APIs, cluster version checks, api-resources and api-versions discovery, kubectl explain for schema checks, searching manifests, common API migration table, Ingress v1beta1 to v1 walkthrough, server-side dry-run, diff, apply and verify, optional kubectl convert, checklist, and troubleshooting. Does not cover control-plane upgrades, full per-release removed-API catalogues, runtime-config, etcd storage migration, CRD conversion webhooks, or GitOps pipelines. |
Before a cluster upgrade or when copying YAML from older tutorials, manifests may still reference API versions the server no longer serves. This guide shows how to discover supported APIs on your cluster, find stale apiVersion lines, migrate one legacy Ingress manifest to networking.k8s.io/v1, and validate the result without treating the topic as a full upgrade runbook.
What Is a Deprecated Kubernetes API?
Kubernetes API groups are versioned independently. A Deployment uses apps/v1 while a Pod uses v1; each group can evolve on its own schedule.
| Status | Meaning |
|---|---|
| Supported | Served normally by the cluster |
| Deprecated | Still served but scheduled for removal |
| Removed | No longer accepted by the API server |
A deprecated API may still work today. A removed API rejects create and update requests. Migration often requires more than swapping the apiVersion string because field names, required fields, and defaults can change between versions.
Discover and Detect Deprecated APIs
Check client and server versions
API availability depends on the Kubernetes server version, not only the kubectl client on your workstation.
kubectl versionSample output:
Client Version: v1.36.3
Kustomize Version: v5.8.1
Server Version: v1.36.3API availability is determined by the server version. Keep kubectl within one minor version of the control plane, then check the APIs served by the target server before deployment. The supported skew is one minor version in either direction.
For additional version checks on nodes and control-plane components, see check Kubernetes cluster version.
List served resources and API versions
List resource types discovered from the server:
kubectl api-resources -o wideSample output (trimmed):
NAME SHORTNAMES APIVERSION NAMESPACED KIND
configmaps cm v1 true ConfigMap
deployments deploy apps/v1 true Deployment
ingresses ing networking.k8s.io/v1 true Ingress
pods po v1 true PodList served group/version pairs:
kubectl api-versionsSample output (trimmed):
apps/v1
batch/v1
networking.k8s.io/v1
policy/v1
autoscaling/v2api-resources lists the resource types discovered from the server. api-versions lists the group/version pairs the server currently serves. Use these commands to detect unsupported or removed APIs, but use API warnings and the official deprecation guide to identify versions that are still served but deprecated.
A deprecated API can still appear in both commands until it is removed. Neither command labels a served API as deprecated.
If a group/version is absent from kubectl api-versions, the server does not serve it. If the group/version exists but the required kind is missing, check kubectl api-resources and confirm that any required CRD or aggregated API is installed.
Full discovery workflows with kubectl explain live in Kubernetes API resources and kubectl explain.
Detect deprecation warnings
On a cluster that still serves a deprecated API, the API server returns a deprecation warning. Convert that warning into a failing check with:
kubectl apply --dry-run=server --validate=strict --warnings-as-errors -f manifest.yaml--warnings-as-errors converts a deprecation warning into a non-zero command result. If the API has already been removed, the request fails with an unsupported group/version or no matches for kind error instead.
Server warnings for deprecated API requests have been supported since Kubernetes 1.19, and kubectl provides the inherited --warnings-as-errors flag.
Search source and rendered manifests
Search manifest trees for apiVersion declarations:
grep -R "^apiVersion:" ./manifests/Also check generated output, not only hand-written files:
- Helm-rendered manifests —
helm template <release> <chart>(Kubernetes Helm charts) - Kustomize builds —
kubectl kustomize <overlay-directory>(Kubernetes Kustomize) - CI/CD deployment bundles
- Example YAML copied from older blog posts
Watch API server warnings during kubectl apply and upgrade preflight tools your distribution provides.
Inspect replacement schemas with kubectl explain
After you pick a target apiVersion, confirm the schema the connected cluster expects.
Inspect Deployment fields:
kubectl explain deployment --api-version=apps/v1Sample output:
GROUP: apps
KIND: Deployment
VERSION: v1
DESCRIPTION:
Deployment enables declarative updates for Pods and ReplicaSets.Inspect Ingress at the supported networking API:
kubectl explain ingress --api-version=networking.k8s.io/v1Sample output:
GROUP: networking.k8s.io
KIND: Ingress
VERSION: v1
DESCRIPTION:
Ingress is a collection of rules that allow inbound connections to reach the
endpoints defined by a backend.For Ingress migration, pathType is required on each path in v1. Inspect it directly:
kubectl explain ingress.spec.rules.http.paths.pathType --api-version=networking.k8s.io/v1The explain output lists allowed values: Exact, Prefix, and ImplementationSpecific.
Understand Common API Migrations
Representative replacements readers encounter during upgrades:
| Removed API | Replacement | Removed starting | Important migration point |
|---|---|---|---|
extensions/v1beta1 Deployment |
apps/v1 |
1.16 | spec.selector is required and immutable |
networking.k8s.io/v1beta1 Ingress |
networking.k8s.io/v1 |
1.22 | Structured Service backend and required pathType |
batch/v1beta1 CronJob |
batch/v1 |
1.25 | No notable schema migration |
policy/v1beta1 PodDisruptionBudget |
policy/v1 |
1.25 | Empty selector behavior changed |
autoscaling/v2beta2 HPA |
autoscaling/v2 |
1.26 | Metric target fields moved under target |
For PodDisruptionBudget, an empty selector selects no Pods in policy/v1beta1 but all namespace Pods in policy/v1. For HPA, fields such as targetAverageUtilization move into the structured target object.
Check the official migration guide for each resource type. Field moves, new required fields, and default changes are not always visible from the version string alone.
This article does not catalogue every removed API per Kubernetes release. Use kubectl api-versions on your target cluster as the source of truth.
Migrate Ingress from v1beta1 to v1
Ingress is a practical example because v1 restructured backend fields and added required pathType, not only a new apiVersion.
Create the backend workload
Create the namespace:
kubectl create namespace deprec-labSample output:
namespace/deprec-lab createdSave web.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
namespace: deprec-lab
spec:
replicas: 1
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: nginx
image: nginx:1.27.0
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: web
namespace: deprec-lab
spec:
selector:
app: web
ports:
- port: 80
targetPort: 80Apply the backend workload:
kubectl apply -f web.yamlSample output:
deployment.apps/web created
service/web createdWait until the Deployment is ready before you attach an Ingress rule to the Service:
kubectl rollout status deployment/web -n deprec-lab --timeout=120sSample output:
deployment "web" successfully rolled outTest the removed manifest
Save the legacy manifest as legacy-ingress.yaml:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: web
namespace: deprec-lab
spec:
rules:
- host: web.example.local
http:
paths:
- path: /
backend:
serviceName: web
servicePort: 80Older clusters accepted networking.k8s.io/v1beta1 with flat backend fields. On Kubernetes 1.36.3 this API version is removed.
kubectl apply -f legacy-ingress.yamlSample output:
error: resource mapping not found for name: "web" namespace: "deprec-lab" from "legacy-ingress.yaml": no matches for kind "Ingress" in version "networking.k8s.io/v1beta1"
ensure CRDs are installed firstThe final CRD suggestion is a generic kubectl message. In this case, the failure is caused by the removed built-in Ingress API version.
Update apiVersion, backend fields, and pathType
Save the migrated manifest as migrated-ingress.yaml:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web
namespace: deprec-lab
spec:
rules:
- host: web.example.local
http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
service:
name: web
port:
number: 80Field changes from v1beta1 to v1:
| v1beta1 field | v1 replacement |
|---|---|
backend.serviceName |
backend.service.name |
backend.servicePort |
backend.service.port.number (or name for named ports) |
path without pathType |
path plus required pathType (Prefix, Exact, or ImplementationSpecific) |
The old v1beta1 manifest did not define pathType. The official migration guide says ImplementationSpecific is the closest replacement for that undefined behavior. Use Prefix or Exact only when you intentionally want those matching rules.
For TLS, ingress class, and path rules beyond this minimal host rule, see expose Services with Ingress.
Validate, Diff, Apply, and Verify
Server-side dry-run
Server-side dry-run submits the manifest to the API server for validation without persisting it:
kubectl apply --dry-run=server --validate=strict -f migrated-ingress.yamlSample output:
ingress.networking.k8s.io/web created (server dry run)(server dry run) confirms admission and schema checks ran on the server.
Preview changes with kubectl diff
Preview the migrated Ingress before apply:
kubectl diff -f migrated-ingress.yamlBecause the Ingress does not exist yet, the command displays the object as additions and exits with status 1. Status 1 means differences were found, not that kubectl diff failed. Status 0 means no differences, while values greater than 1 indicate an error. These are the documented kubectl diff exit codes.
See kubectl apply, edit, patch and replace for diff and dry-run details.
Apply the migrated resource
After validation succeeds, apply the migrated file:
kubectl apply -f migrated-ingress.yamlSample output:
ingress.networking.k8s.io/web createdVerify the stored API and backend
Confirm API version and routing fields:
kubectl get ingress web -n deprec-labSample output:
NAME CLASS HOSTS ADDRESS PORTS AGE
web <none> web.example.local 80 0sRead stored API version and path type:
kubectl get ingress web -n deprec-lab -o jsonpath='apiVersion={.apiVersion} host={.spec.rules[0].host} pathType={.spec.rules[0].http.paths[0].pathType}{"\n"}'Sample output:
apiVersion=networking.k8s.io/v1 host=web.example.local pathType=ImplementationSpecificCheck rules and backends when routing does not behave as expected:
kubectl describe ingress web -n deprec-labSample output (trimmed):
Rules:
Host Path Backends
---- ---- --------
web.example.local
/ web:80 (192.168.5.36:80)
Events: <none>This verifies the migrated API object and backend reference, not end-to-end ingress traffic. An Ingress controller must be installed and must select this Ingress before the rule can route requests. An empty ADDRESS is expected when no controller has fulfilled the resource.
Convert Manifests with kubectl-convert
kubectl convert can rewrite a manifest to another API version. It is distributed as a separate plugin, not bundled in the default kubectl binary on many installations.
Example invocation:
kubectl convert -f old-deployment.yaml --output-version apps/v1Review converted output before apply. The plugin may choose defaults that differ from your intended configuration. Treat conversion as a starting point, then run kubectl explain and server-side dry-run on the result.
Manual migration with explicit field edits remains the primary method in this guide.
Troubleshoot Migration Errors
| Symptom | Likely cause | Fix |
|---|---|---|
no matches for kind |
Removed or wrong apiVersion, or missing CRD |
Run kubectl api-versions; update apiVersion and kind; install required CRDs |
unable to recognize |
Unsupported API version or malformed YAML | Validate syntax; confirm the group/version is served |
strict decoding error: unknown field |
Field removed or moved in the new API | Compare schemas with kubectl explain; remove or relocate the field |
| Version updated but apply still fails | Required new fields or changed defaults | Read the official migration note for that resource type |
| Client dry-run passes, server apply fails | Admission policies or server-only validation | Use --dry-run=server --validate=strict on the target cluster |
kubectl edit change lost after upgrade |
Source YAML still uses old API | Update Git, Helm, and Kustomize sources, not only live objects |
Manifest migration checklist
- Identify the target Kubernetes server version.
- Search hand-written and generated manifests for
apiVersion. - List APIs the target cluster serves with
kubectl api-versions. - Review field and default changes in official migration notes.
- Update YAML and render Helm or Kustomize output again.
- Validate with
kubectl apply --dry-run=server --validate=strict. - Test in a non-production namespace or cluster.
- Apply and verify resource status and Events.
- Update charts, bases, overlays, and deployment automation to match.
What's Next
- Kubernetes Liveness, Readiness and Startup Probes
- kubectl logs, Events and describe with Examples
- Monitor Kubernetes Pods and Nodes with kubectl top
References
- Deprecated API Migration Guide — official field and version mappings
- Kubernetes API Concepts — API groups and versioning model
- kubectl reference: api-resources — discovery commands
Summary
You checked the server version, listed served APIs with kubectl api-resources and kubectl api-versions, and used kubectl explain to confirm Ingress v1 requires pathType and structured service backends. Discovery commands show what the server serves today; they do not label a served API as deprecated.
The Ingress walkthrough showed a removed networking.k8s.io/v1beta1 manifest failing with no matches for kind, then a v1 replacement with pathType: ImplementationSpecific validated with server-side dry-run and applied successfully. The main lesson is that migration is schema work: update apiVersion, required fields, and backend structure together, then validate on the cluster you will run against.
For discovery commands in depth, continue with Kubernetes API resources and kubectl explain. For apply, diff, and dry-run mechanics, use kubectl apply, edit, patch and replace.

