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
- Kubernetes1.36.4
- Runtimecontainerd 2.2.6
- CNICalico v3.32.1
- TimeAbout 16 min
- Reviewed23 August 2026
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.
| Server Name | IP Address | OS | Roles | CPU | RAM | HDD |
|---|---|---|---|---|---|---|
| CKA4001 | 192.168.0.191 | Ubuntu 26.04 LTS | Single Node (control plane, untainted) | 2 Core | 4 GB | 50 GB |
Before you start
- A cluster and kubectl.
- The session creates namespace
ckad-mod, a Deploymentapi, and an immutable ConfigMap, then deliberately triggers two rejections.
-
The fields that have a verb
kubectl setis 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 envwith--from=configmap/xwires a whole ConfigMap in, andset 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.
-
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: apisurvived, 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=jsonwith an explicit index, or edit the manifest.Note the trap here:
kubectl label deployment api tier=backendlabels 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.
-
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 immutableAnd a ConfigMap marked
immutable: truerefuses its own data:The ConfigMap "frozen" is invalid: data: Forbidden: field is immutable when `immutable` is setBoth 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 immutablemeans stop patching and start planning a replacement; no amount of--forceon 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.
-
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" deletedExpected 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
field is immutableon a Deployment selector.Why: Selectors are fixed at creation.
Fix:Export, edit, delete the old one and create the new - or
kubectl replace --force -f, understanding that it deletes first.A patch removed containers or ports you did not touch.
Why: A merge patch replaces whole lists.
Fix:Use
--type=jsonwith an explicit path and index, or edit the full manifest and apply it.kubectl labeldid not reach the Pods.Why: It labelled the controller object, not
spec.template.metadata.labels.Fix:Patch the template instead. Labels on the Deployment and labels on the Pods are separate.
A ConfigMap will not update.
Why: It is marked
immutable: true.Fix:Immutability cannot be removed either. Create a new ConfigMap and repoint the workload at it.