CertGrid CertGrid
Hands-on Lab·Certified Kubernetes Application Developer

Editing Live Objects: apply, edit, patch and replace

CKAD hands you existing objects more often than empty namespaces, and there are four ways to change one: `set` for the handful of fields with a verb, `patch` for everything else, `edit` for exploration, and delete-and-recreate for the fields that cannot change at all. Knowing which is which saves minutes; not knowing it produces a `field is immutable` error with a task half-done.

Working at Exam Speed Guide 5 of 44 Intermediate

Written against the versions above. `kubectl set` covers `image`, `env`, `resources`, `serviceaccount` and `selector`. Everything else needs `patch` or `edit`. A merge patch replaces whole objects at the path you give it, so patching one label into `template.metadata.labels` keeps the others - but patching a list replaces the entire list.

Runs on cka4001. Two commands here are deliberately made to fail, and failing them somewhere idle is better than somewhere shared.
Server NameIP AddressOSRolesCPURAMHDD
CKA4001192.168.0.191Ubuntu 26.04 LTSSingle Node (control plane, untainted)2 Core4 GB50 GB

Before you start

  1. The fields that have a verb

    kubectl set is the fastest path for the four things you change most, and it works directly on the live object:

    nginx:1.28-alpine  [{"name":"MODE","value":"prod"}]  {"limits":{"memory":"128Mi"},"requests":{"memory":"64Mi"}}

    Three commands, three fields, no YAML. Each one triggers a rollout, because each changes the Pod template.

    Worth knowing: set env with --from=configmap/x wires a whole ConfigMap in, and set env ... KEY- (trailing dash) removes a variable. Those two save more time than they look like they should.

    bash Example session
    kubectl create namespace ckad-modnamespace/ckad-mod createdkubectl -n ckad-mod create deployment api --image=nginx:1.27-alpine --replicas=2deployment.apps/api createdkubectl -n ckad-mod set image deployment/api nginx=nginx:1.28-alpinedeployment.apps/api image updatedkubectl -n ckad-mod set env deployment/api MODE=proddeployment.apps/api env updatedkubectl -n ckad-mod set resources deployment/api --limits=memory=128Mi --requests=memory=64Mideployment.apps/api resource requirements updatedkubectl -n ckad-mod get deploy api -o jsonpath='{.spec.template.spec.containers[0].image}{"  "}{.spec.template.spec.containers[0].env}{"  "}{.spec.template.spec.containers[0].resources}'nginx:1.28-alpine  [{"name":"MODE","value":"prod"}]  {"limits":{"memory":"128Mi"},"requests":{"memory":"64Mi"}}

    Expected resultImage, env and resources all changed with no manifest edited.

    Success conditionYou can change the four common fields without opening an editor.

  2. Patch for everything else

    There is no kubectl set label --on-the-pod-template, so a template label needs a patch:

    {"app":"api","tier":"backend"}

    The existing app: api survived, because a merge patch merges maps. If you patch a list - containers, ports, volumes - the whole list is replaced by what you supply, which is the commonest way to accidentally delete a container. For lists, use --type=json with an explicit index, or edit the manifest.

    Note the trap here: kubectl label deployment api tier=backend labels the Deployment, not the Pod template. Those are different objects and only one of them reaches the Pods.

    bash Example session
    kubectl -n ckad-mod patch deployment api --type=merge -p '{"spec":{"template":{"metadata":{"labels":{"tier":"backend"}}}}}'deployment.apps/api patchedkubectl -n ckad-mod get deploy api -o jsonpath='{.spec.template.metadata.labels}'{"app":"api","tier":"backend"}

    Expected resultBoth labels present on the template.

    Success conditionYou can reach any field, and you know what a merge patch does to a list.

  3. The two that refuse

    A Deployment's selector cannot change after creation:

    The Deployment "api" is invalid: spec.selector: Invalid value: {"matchLabels":{"app":"api","tier":"backend"}}: field is immutable

    And a ConfigMap marked immutable: true refuses its own data:

    The ConfigMap "frozen" is invalid: data: Forbidden: field is immutable when `immutable` is set

    Both are deliberate. A Deployment's selector defines which Pods it owns, and changing it would orphan every running Pod. An immutable ConfigMap is a performance and safety feature - the kubelet stops watching it, and nothing can change configuration underneath a running fleet.

    Recognise the wording. field is immutable means stop patching and start planning a replacement; no amount of --force on a patch will move it.

    bash Example session
    kubectl -n ckad-mod patch deployment api --type=merge -p '{"spec":{"selector":{"matchLabels":{"app":"api","tier":"backend"}}}}'The Deployment "api" is invalid: spec.selector: Invalid value: {"matchLabels":{"app":"api","tier":"backend"}}: field is immutable[exit 1]kubectl -n ckad-mod create configmap frozen --from-literal=A=1configmap/frozen createdkubectl -n ckad-mod patch configmap frozen --type=merge -p '{"immutable":true}'configmap/frozen patchedkubectl -n ckad-mod patch configmap frozen --type=merge -p '{"data":{"A":"2"}}'The ConfigMap "frozen" is invalid: data: Forbidden: field is immutable when `immutable` is set[exit 1]

    Expected resultTwo rejections, both saying field is immutable.

    Success conditionYou can tell a field that needs a different command from one that needs a different object.

  4. When you have to replace it

    For an immutable field the only route is a new object. Export, edit, create:

    NAME   SELECTOR
    api    map[app:api]
    api2   map[app:api2]

    In a real task you would delete the original; here both are shown so the two selectors sit side by side.

    The other route is kubectl replace --force -f file.yaml, which deletes and recreates in one command. It is fast and it is genuinely destructive - the object goes away, along with anything that depended on it existing continuously. Reach for it when a task says "recreate" and not otherwise.

    bash Example session
    kubectl -n ckad-mod get deploy api -o yaml > /tmp/api.yamlsed -i 's/app: api/app: api2/g; s/name: api/name: api2/' /tmp/api.yamlkubectl -n ckad-mod create -f /tmp/api.yamldeployment.apps/api2 createdkubectl -n ckad-mod get deploy -o 'custom-columns=NAME:.metadata.name,SELECTOR:.spec.selector.matchLabels'NAME   SELECTORapi    map[app:api]api2   map[app:api2]kubectl delete namespace ckad-mod --wait=falsenamespace "ckad-mod" deleted

    Expected resultA second Deployment with the selector the first one could not have.

    Success conditionYou have a route past an immutable field that does not involve guessing.

Troubleshooting

Official sources