Back to Blog

API Deprecations & Managing API Versions for the CKAD Exam: apiVersion, kubectl convert & the Deprecation Policy

A practitioner's guide to Kubernetes API versioning and deprecations for the CKAD exam — how alpha/beta/stable versions work, the deprecation policy that governs them, how to find the right apiVersion for any object with kubectl api-resources and explain, how to migrate manifests off removed APIs with kubectl convert, and the exact commands that stop 'no matches for kind' errors from costing you points on exam day.

By Sailor Team , August 14, 2026

Every CKAD candidate eventually hits the same wall. You copy a manifest that worked six months ago, run kubectl apply -f, and the cluster answers with a flat refusal:

error: unable to recognize "deploy.yaml": no matches for kind "Deployment" in version "extensions/v1beta1"

Nothing is wrong with your YAML. The kind is spelled correctly, the indentation is fine, the fields are valid. The problem is one line — apiVersion — pointing at an API group and version the cluster no longer serves. This is the single most common “why won’t this apply?” moment on the CKAD, and it is entirely avoidable once you understand how Kubernetes versions its APIs and how to find the correct apiVersion for any object in about five seconds.

This guide covers API versioning and deprecations the way the CKAD actually tests them: not as release-note trivia, but as a practical skill — reading apiVersion correctly, discovering the right group/version at the terminal, and migrating a manifest off a removed API without hand-editing every field. If you want the broader exam picture first, start with the CKAD exam guide for 2026 and sequence your study with the CKAD study plan. This piece sits inside the Application Observability and Maintenance domain, which explicitly asks you to “understand API deprecations.”

Why apiVersion Exists at All

Every object you send to the Kubernetes API server is addressed by two things working together: apiVersion and kind. The kind says what you are creating — a Pod, a Deployment, a CronJob. The apiVersion says which API defines that kind and which revision of it you are speaking.

apiVersion: apps/v1      # API group "apps", version "v1"
kind: Deployment
metadata:
  name: web

Read apiVersion as group/version. In apps/v1, the group is apps and the version is v1. A handful of very old, foundational kinds live in the core group, which has no group name — its apiVersion is simply v1 with nothing before the slash:

apiVersion: v1           # core group, version v1
kind: Pod

So v1 (Pod, Service, ConfigMap, Secret, Namespace, PersistentVolumeClaim) is the core group, while everything with a slash — apps/v1, batch/v1, networking.k8s.io/v1, rbac.authorization.k8s.io/v1 — belongs to a named group. Getting this split right is half the battle: a huge number of “no matches for kind” errors come from writing apiVersion: v1 for a Deployment (wrong — it is apps/v1) or apiVersion: apps/v1 for a Pod (wrong — it is v1).

Alpha, Beta, and Stable: How to Read a Version String

The version part of apiVersion is not an arbitrary label. It encodes a maturity level, and that maturity tells you how much you can trust the API to stick around.

Version patternExampleMaturityWhat it means for you
v1alpha1, v2alpha2flowcontrol.apiserver.k8s.io/v1alpha1AlphaExperimental. May be buggy, disabled by default, and can change or vanish with no notice between releases.
v1beta1, v2beta2batch/v1beta1 (CronJob, historically)BetaWell-tested but not final. Enabled by default, but fields and the version itself can still change and eventually get removed.
v1, v2apps/v1, batch/v1Stable (GA)Generally available. Will be supported for many releases; the safe default for anything you write.

The practical rule for the exam and for real clusters: prefer the stable (v1) version whenever one exists. Beta versions are where deprecation pain comes from, because a kind often lives at beta for a while, graduates to stable, and then the beta version is removed — breaking every manifest that still names it.

The classic example is CronJob. It spent years at batch/v1beta1, graduated to batch/v1, and the beta version was removed in Kubernetes 1.25. Manifests that still said apiVersion: batch/v1beta1 for a CronJob simply stopped applying. The fix was a one-line change to batch/v1 — but only if you knew to make it.

The Deprecation Policy in Plain Terms

Kubernetes does not remove APIs on a whim. It follows a published deprecation policy designed to give you time to migrate. You do not need to memorize the legal text for the CKAD, but you should understand the shape of it, because it explains why things break and how much warning you get.

  • Stable (GA) API elements cannot be removed without a major version bump of the whole API (which effectively never happens for core kinds). A GA v1 object is safe for the long haul.
  • Beta API versions are supported for a defined window after deprecation — long enough to span multiple releases — before removal. When a beta version is deprecated, a newer version (usually GA) already exists to migrate to.
  • Alpha API versions may be dropped in any release with no deprecation window at all.
  • A deprecated API keeps working until its removal release. Deprecation is a warning, not an immediate breakage. Newer kubectl versions print a warning to stderr when you use a deprecated API — read those warnings; they are telling you exactly what will break later.

The mental model: stable = safe, beta = migrate before it’s removed, alpha = don’t rely on it. When you see a deprecation warning during kubectl apply, treat it as a to-do item, not noise.

Warning: batch/v1beta1 CronJob is deprecated in v1.21+, unavailable in v1.25+; use batch/v1 CronJob

That single warning line tells you the old version, the release it disappears in, and the exact replacement. On exam day, if you see this, you fix the apiVersion and move on.

Finding the Right apiVersion in Five Seconds

You will never memorize the correct apiVersion for every kind, and you do not need to. The cluster will tell you. This is the most important exam skill in this whole topic, because it turns a potential dead end into a two-command lookup.

kubectl api-resources

kubectl api-resources lists every kind the cluster currently serves, along with its group and — critically — its shortnames and whether it is namespaced:

kubectl api-resources

The output is wide, so filter it. To find the group for Deployments:

kubectl api-resources | grep -i deployment
# deployments   deploy   apps/v1   true   Deployment

The APIVERSION column (shown as apps/v1 here) is exactly what goes in your manifest. Note the shortname deploy and that it is namespaced (true). A few high-value filters worth practicing:

# Only namespaced resources in the core group
kubectl api-resources --namespaced=true --api-group=''

# Everything in the batch group (Jobs, CronJobs)
kubectl api-resources --api-group=batch

# Just the names, for scripting or a quick scan
kubectl api-resources -o name

kubectl api-versions

Where api-resources lists kinds, kubectl api-versions lists the group/version pairs the server currently enables:

kubectl api-versions
# apps/v1
# batch/v1
# networking.k8s.io/v1
# rbac.authorization.k8s.io/v1
# v1
# ...

If a version you need is not in this list, the cluster will not accept a manifest that names it — that is precisely the “no matches for kind” condition. Cross-checking your manifest’s apiVersion against kubectl api-versions is the fastest way to diagnose the error.

kubectl explain

kubectl explain doubles as a version lookup and a field reference. By default it documents the current preferred version of a kind:

kubectl explain deployment
# KIND:       Deployment
# VERSION:    apps/v1
# ...

The VERSION: line is the preferred apiVersion. You can also drill into fields, which is invaluable when a field moved between versions:

kubectl explain deployment.spec.strategy
kubectl explain cronjob.spec.jobTemplate --recursive

Between api-resources, api-versions, and explain, you have everything you need to write a correct apiVersion for any object without guessing. Practice these until they are reflex — they save real minutes on exam day and eliminate an entire class of avoidable failures.

Common apiVersion Values Worth Knowing Cold

While you should always verify against the live cluster, a handful of mappings come up so often that recognizing them instantly is worth it. These reflect the stable versions on current Kubernetes:

KindCorrect apiVersionGroup
Pod, Service, ConfigMap, Secret, Namespace, PersistentVolumeClaimv1core
Deployment, ReplicaSet, StatefulSet, DaemonSetapps/v1apps
Job, CronJobbatch/v1batch
Ingress, NetworkPolicynetworking.k8s.io/v1networking
Role, RoleBinding, ClusterRole, ClusterRoleBindingrbac.authorization.k8s.io/v1rbac
HorizontalPodAutoscalerautoscaling/v2autoscaling
CustomResourceDefinitionapiextensions.k8s.io/v1apiextensions

Notice the traps: Deployments are not v1, and Pods are not apps/v1. Ingress and NetworkPolicy both moved to networking.k8s.io/v1 from older beta/extensions locations — a very common source of stale-manifest errors. If you learn nothing else from this table, learn that “core v1 kinds” and “apps v1 kinds” are different lists.

Migrating a Manifest with kubectl convert

Sometimes you inherit a manifest written against a removed API and you need to bring it up to date. You could hand-edit the apiVersion and any renamed fields, but for non-trivial objects that is error-prone. The purpose-built tool is kubectl convert, distributed as a kubectl plugin (kubectl-convert).

Suppose you have this legacy Deployment:

# old-deploy.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: web
spec:
  replicas: 3
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: nginx
          image: nginx:1.27

extensions/v1beta1 for Deployment was removed long ago, so this will not apply. Convert it to the current stable version:

kubectl convert -f old-deploy.yaml --output-version apps/v1

kubectl convert reads the object, maps its fields into the target version’s schema (including renamed or restructured fields), and prints the migrated manifest. Redirect it to a file and apply:

kubectl convert -f old-deploy.yaml --output-version apps/v1 -o yaml > new-deploy.yaml
kubectl apply -f new-deploy.yaml

A few things to know about kubectl convert for the exam:

  • It is a separate binary (kubectl-convert) that may or may not be installed. If kubectl convert returns “unknown command,” the plugin is not present — in that case you fall back to fixing the apiVersion (and any moved fields) by hand using kubectl explain to confirm the new schema.
  • --output-version takes the target group/version. If you omit it, convert targets the latest available version for that kind.
  • Convert operates on the manifest, not the live cluster — it is a translation tool, not an apply tool. You still kubectl apply the result.

For simple objects like a Deployment whose only issue is the apiVersion string, hand-editing one line is often faster than reaching for convert. Convert earns its keep when fields have genuinely moved between versions and you want the tool to do the remapping for you.

A Realistic Exam Scenario

Here is how this topic typically shows up under time pressure. You are given a manifest and told to deploy it, but kubectl apply fails. Walk it methodically:

  1. Read the error. no matches for kind "CronJob" in version "batch/v1beta1" tells you the kind and the bad version immediately.
  2. Confirm what the cluster serves. kubectl api-versions | grep batch shows batch/v1 but not batch/v1beta1 — the beta version is gone.
  3. Find the correct current version. kubectl explain cronjob reports VERSION: batch/v1.
  4. Fix the manifest. Change apiVersion: batch/v1beta1 to apiVersion: batch/v1. For CronJob that is the only change needed; the spec fields are compatible.
  5. Re-apply and verify. kubectl apply -f cron.yaml then kubectl get cronjob to confirm it exists.

The whole loop takes under a minute once the commands are muscle memory. The candidates who lose time here are the ones who reread their YAML looking for a syntax bug that isn’t there, instead of reading the apiVersion the error is pointing at.

Build the Muscle Memory Before Exam Day

Understanding the deprecation policy is worth a few marks; being fast at diagnosing and fixing a wrong apiVersion is worth far more, because it appears woven into other tasks — a Deployment task that fails because of a stale group, a CronJob task with a beta version, an Ingress written against the old extensions group. The only way to make these reflexive is to practice them under realistic, timed conditions where a failed apply costs you the same way it would on exam day.

That is exactly the gap a full-length practice exam closes. The CKAD Certification Ready Mock Exam Bundle runs five browser-based mock exams that mirror the real environment, so you get repeated reps at spotting a bad apiVersion, running kubectl api-resources and explain to find the right one, and fixing manifests against the clock — until it stops feeling like a puzzle and starts feeling like a checklist. Pair it with the CKAD application troubleshooting guide and keep the CKAD kubectl cheat sheet open while you drill.

Frequently Asked Questions

How do I find the correct apiVersion for a Kubernetes object?

Ask the cluster. kubectl api-resources | grep <kind> shows the group/version in the APIVERSION column, and kubectl explain <kind> prints the preferred VERSION: at the top. Cross-check against kubectl api-versions to confirm the version is actually enabled. Never guess — the two-command lookup is faster and reliable.

What does “no matches for kind” mean and how do I fix it?

It means the apiVersion in your manifest names a group/version the API server does not serve — usually a removed beta version or the wrong group (e.g., v1 for a Deployment instead of apps/v1). Run kubectl api-versions to see what the cluster offers, kubectl explain <kind> to find the correct version, then update the apiVersion line and re-apply.

Are alpha and beta APIs safe to use on the CKAD exam?

Prefer stable (v1) versions whenever they exist. Beta versions are enabled by default but can be removed, and alpha versions may be disabled entirely. On the exam, if a stable version is available for a kind, use it — it is the version the graders and the cluster expect.

What is the difference between kubectl api-resources and kubectl api-versions?

kubectl api-resources lists the kinds the cluster serves (Pod, Deployment, CronJob…) with their group, shortnames, and namespaced status. kubectl api-versions lists the group/version pairs the server enables (apps/v1, batch/v1, v1…). Use api-resources to find which group a kind belongs to, and api-versions to confirm a specific version is available.

When should I use kubectl convert versus editing the manifest by hand?

Use kubectl convert when fields have genuinely moved or been restructured between API versions and you want the tool to remap them. For a simple case where only the apiVersion string is stale (like CronJob batch/v1beta1 → batch/v1), editing the one line by hand is faster. Note that kubectl convert is a separate plugin that may not be installed on every cluster.

Does a deprecation warning mean my manifest will stop working immediately?

No. Deprecation is advance notice, not immediate removal. A deprecated API keeps functioning until its designated removal release, and kubectl prints a warning meanwhile telling you the replacement and the release it disappears in. Treat the warning as a migration to-do so the API is gone before it breaks you, not after.

Conclusion

API versioning looks like bookkeeping until a manifest refuses to apply and the clock is running. The concepts are small: apiVersion is group/version, maturity runs alpha → beta → stable, the deprecation policy gives beta versions a migration window before removal, and stable v1 is always the safe default. The skills are smaller still: kubectl api-resources, kubectl api-versions, and kubectl explain find the correct version for any kind in seconds, and kubectl convert migrates the awkward cases. Master those three lookups and a wrong apiVersion stops being a dead end and becomes a ten-second fix.

Ground the theory in reps. Work through the CKAD study plan, keep the exam guide for 2026 as your map, and drill the diagnose-and-fix loop under time pressure with the CKAD mock exam bundle until reading apiVersion is the first thing you do when an apply fails.

Limited Time Offer: Get 80% off all Mock Exam Bundles | Sale ends in 7 days. Start learning today.

Claim Now