Home › CKAD practice tasks › Migrate manifests off removed APIs
CKAD · Application Observability and Maintenance

Migrate Manifests Off Removed Kubernetes APIs

The `legacy-apps` team is moving to this Kubernetes 1.35 cluster. Their manifests in `/root/legacy` (a CronJob, a HorizontalPodAutoscaler and an Ingress for the existing Deployment and Service `web`) were written for an older release, and `kubectl apply` now rejects them.

Live lab access

This task description is public. Live practice requires full access unless this is the selected free weekly task. AI Lab Coach is available in single-task labs, including the free weekly task. See this week's free task.

Create an account to continue →

Read this week's free task →

On a phone? Read the task and create your account here, then sign in on a desktop or laptop for the terminal lab. Nothing starts until you choose Start.

The task

The legacy-apps team is moving to this Kubernetes 1.35 cluster. Their manifests in /root/legacy (a CronJob, a HorizontalPodAutoscaler and an Ingress for the existing Deployment and Service web) were written for an older release, and kubectl apply now rejects them.

Requirements
1. Update each file in /root/legacy to an API version supported by this cluster. Keep every object's name, namespace and behaviour.
2. For the Ingress, keep the rule for host web.legacy.local and path / (use path type Prefix) routed to Service web on port 80.
3. Apply all three files so the objects exist in legacy-apps.

Keep the files in place: the updated manifests are what the team will reuse.

Original Prepium guide · Written for Kubernetes v1.35 and checked against the official documentation on 6 October 2026. Use a disposable practice cluster.

Migrate manifests off removed API versions without changing what they do

Kubernetes retires beta API versions on a published schedule. Objects already stored in the cluster survive, because the API server can serve them through the newer version, but a YAML file that still names a removed version is rejected by kubectl apply. Migration is therefore two jobs: change apiVersion, then rewrite any fields whose names or shape changed in the newer version.

old file (removed version) ──→ apply rejected
        │ 1. new apiVersion   2. renamed / restructured fields
        └──→ new file ──→ server dry-run ──→ apply

1. Ask the cluster what it serves

kubectl api-versions | grep -E 'batch|autoscaling|networking'
kubectl api-resources --api-group=networking.k8s.io
kubectl explain ingress --api-version=networking.k8s.io/v1

Trust the cluster over memory. api-versions lists every group/version the server offers, and kubectl explain documents the fields of the version you name, which is exactly what you need when a field has moved. Both answer from the live cluster, so you do not have to hunt through the docs for a field path during the exam.

2. Know the removals that matter for application manifests

3. Rewrite an Ingress backend by hand

Here is an old-style rule of our own, routing /catalog on shop.example.test to a Service port that is referenced by name:

# networking.k8s.io/v1beta1 (no longer served)
- path: /catalog
  backend:
    serviceName: catalog-svc
    servicePort: http

In networking.k8s.io/v1 the backend becomes a nested service object, a string port goes under port.name (a numeric one would go under port.number), and every path must declare a pathType:

# networking.k8s.io/v1
- path: /catalog
  pathType: Prefix
  backend:
    service:
      name: catalog-svc
      port:
        name: http

Choose pathType deliberately. Exact matches only the literal path, Prefix matches by path elements split on /, and ImplementationSpecific leaves matching to the Ingress class. The guide suggests ImplementationSpecific when you need the undefined old behaviour; when you know the intended matching, state it. A top-level spec.backend is now spec.defaultBackend.

4. Validate before you apply

Assume the migrated file is shop-ingress.yaml and defines an Ingress named shop in prepium-tutorial.

kubectl apply --dry-run=server -f shop-ingress.yaml
kubectl apply -f shop-ingress.yaml
kubectl -n prepium-tutorial get ingress shop -o jsonpath='{.spec.rules[0].http.paths[0].backend.service}'

A server-side dry run submits the request to the API server without persisting it, so the server's schema checks run and leftover old fields or a missing pathType surface before anything changes. kubectl apply validates strictly by default, failing on unknown fields instead of dropping them. Then compare the stored object with the old file field by field: the goal is the same routing, schedule or scaling target, only expressed in the new schema.

The kubectl convert plugin can rewrite a file to another version, but it is not part of kubectl by default (it was removed from the built-in commands and is now installed separately), and the documentation warns that it may fill in non-ideal defaults. Review its output as carefully as a hand edit.

Common mistakes

Example checkEvidence
Each file uses a served versionapiVersion appears in kubectl api-versions
Schema is validkubectl apply --dry-run=server succeeds
Ingress routing preservedHost, path, pathType and backend.service match the intent
Behaviour unchangedSchedule, scale target and limits read the same as before

Cleanup: delete anything you created in prepium-tutorial with kubectl delete namespace prepium-tutorial. References: Deprecated API Migration Guide, Ingress, Install the kubectl convert plugin. Next, try Ingress configuration or CronJobs.

Difficulty
Medium · 9 minutes target
Domain
Application Observability and Maintenance
Grading
Live checks · partial credit

Practice scope

This additional curriculum lab is available in single-task practice with the existing 12-minute limit. The three CKAD mocks keep their original 17-task sets. Hints, an explained solution and requirement-by-requirement feedback are available in the lab.

Browse CKAD practice tasks →