Choosing a Workload Object
"Choose and use the right workload resource" is a CKAD objective, and the differences are easier to see than to remember. All five run side by side here - Deployment, StatefulSet, DaemonSet, Job and CronJob - and the comparison is made from what they actually did: how they named their Pods, how many they made, what storage they claimed, and what happened when one was deleted.
Application Design and Build Guide 15 of 44 Beginner
- Kubernetes1.36.4
- Runtimecontainerd 2.2.6
- CNICalico v3.32.1
- TimeAbout 16 min
- Reviewed23 August 2026
Written against the versions above. The DaemonSet's DESIRED count is computed from the nodes that will accept the Pod, so on this four-node cluster it reads 3 - the control plane's taint excludes it. That is a property of the cluster, not of the DaemonSet, and it is the number people misread as a fault.
| Server Name | IP Address | OS | Roles | CPU | RAM | HDD |
|---|---|---|---|---|---|---|
| CKA1001 | 192.168.0.175 | Ubuntu 26.04 LTS | Control Plane Node | 2 Core | 4 GB | 50 GB |
| CKA1001-NODE01 | 192.168.0.176 | Ubuntu 26.04 LTS | Worker Node | 2 Core | 4 GB | 50 GB |
| CKA1001-NODE02 | 192.168.0.177 | Ubuntu 26.04 LTS | Worker Node | 2 Core | 4 GB | 50 GB |
| CKA1001-NODE03 | 192.168.0.178 | Ubuntu 26.04 LTS | Worker Node | 2 Core | 4 GB | 50 GB |
Before you start
- A cluster with a default StorageClass, for the StatefulSet's volume claims.
- The session creates namespace
ckad-wlcontaining one of each workload kind, then deletes a Pod from two of them to compare the replacements.
-
Five objects, one namespace
One of each, all running at once:
- Deployment
web, 2 replicas - StatefulSet
db, 2 replicas with a claim each - DaemonSet
agent - Job
once - CronJob
nightly, at 03:00
The listing shows them in their own vocabularies - a Deployment reports
READY 2/2, a Job reportsCOMPLETIONS 1/1, a CronJob reports a schedule and no Pods at all until it fires. Those columns are the first hint that these are not variations on one idea.bash Example session kubectl create namespace ckad-wlnamespace/ckad-wl createdkubectl -n ckad-wl rollout status deploy/web --timeout=240sWaiting for deployment "web" rollout to finish: 0 of 2 updated replicas are available...Waiting for deployment "web" rollout to finish: 1 of 2 updated replicas are available...deployment "web" successfully rolled outkubectl -n ckad-wl rollout status statefulset/db --timeout=240sWaiting for 2 pods to be ready...Waiting for 1 pods to be ready...Waiting for 1 pods to be ready...partitioned roll out complete: 2 new pods have been updated...sleep 20; kubectl -n ckad-wl get deploy,statefulset,daemonset,job,cronjobNAME READY UP-TO-DATE AVAILABLE AGEdeployment.apps/web 2/2 2 2 29s NAME READY AGEstatefulset.apps/db 2/2 28s NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGEdaemonset.apps/agent 3 3 3 3 3 <none> 28s NAME STATUS COMPLETIONS DURATION AGEjob.batch/once Complete 1/1 3s 28s NAME SCHEDULE TIMEZONE SUSPEND ACTIVE LAST SCHEDULE AGEcronjob.batch/nightly 0 3 * * * <none> False 0 <none> 28sExpected resultAll five present, each reporting different columns.
Success conditionYou have every workload kind in front of you at once.
- Deployment
-
How they name their Pods
POD OWNER NODE agent-... DaemonSet cka1001-node01 agent-... DaemonSet cka1001-node02 agent-... DaemonSet cka1001-node03 db-0 StatefulSet ... db-1 StatefulSet ... once-... Job ... web-...-... ReplicaSet ...Three naming schemes, and each says something:
db-0,db-1- an ordinal. Stable, predictable, addressable.web-- a template hash and a random suffix. Interchangeable by design.- agent-, one per node - placement is the identity.
Note the Deployment's Pods are owned by a ReplicaSet, not by the Deployment. That extra layer is what makes rollbacks cheap, and it is why
kubectl get rsis part of debugging a rollout.bash Example session kubectl -n ckad-wl get pods -o 'custom-columns=POD:.metadata.name,OWNER:.metadata.ownerReferences[0].kind,NODE:.spec.nodeName' --sort-by=.metadata.namePOD OWNER NODEagent-6ckgf DaemonSet cka1001-node01agent-h2c7b DaemonSet cka1001-node02agent-rfhqf DaemonSet cka1001-node03db-0 StatefulSet cka1001-node02db-1 StatefulSet cka1001-node03once-xstmc Job cka1001-node03web-5d6b5c7df9-4pmxx ReplicaSet cka1001-node02web-5d6b5c7df9-r7q9f ReplicaSet cka1001-node01Expected resultThree distinct naming patterns and two owner kinds.
Success conditionYou can tell which controller made a Pod from its name alone.
-
What each one guarantees
Storage. Only the StatefulSet claimed any:
CLAIM STATUS data-db-0 Bound data-db-1 BoundOne PVC per replica, from
volumeClaimTemplates. A Deployment cannot do this - every replica would share one claim, andReadWriteOncemeans only one node could mount it.Node coverage. The DaemonSet computed its own replica count:
DESIRED READY 3 3against four nodes, because the control plane's taint excludes it. You never set that number.
Completion. The Job finished and stopped:
COMPLETIONS CONDITION 1 Completeand the CronJob has done nothing at all, correctly:
SCHEDULE LASTRUN ACTIVE 0 3 * * * <none> <none>So the decision, in the order worth asking it:
| Question | Answer | |---|---| | Does it finish? | Job, or CronJob on a schedule | | One per node? | DaemonSet | | Stable name or its own disk? | StatefulSet | | None of the above | Deployment |
Most of the time the answer is Deployment. The point of the other four is knowing when it is not.
bash Example session kubectl -n ckad-wl get pvc -o 'custom-columns=CLAIM:.metadata.name,STATUS:.status.phase,OWNER:.metadata.labels.app'CLAIM STATUS OWNERdata-db-0 Bound dbdata-db-1 Bound dbkubectl -n ckad-wl get daemonset agent -o 'custom-columns=DESIRED:.status.desiredNumberScheduled,READY:.status.numberReady'DESIRED READY3 3kubectl get nodes --no-headers | wc -l4kubectl -n ckad-wl get job once -o 'custom-columns=COMPLETIONS:.status.succeeded,CONDITION:.status.conditions[*].type'COMPLETIONS CONDITION1 SuccessCriteriaMet,Completekubectl -n ckad-wl get cronjob nightly -o 'custom-columns=SCHEDULE:.spec.schedule,LASTRUN:.status.lastScheduleTime,ACTIVE:.status.active'SCHEDULE LASTRUN ACTIVE0 3 * * * <none> <none>Expected resultClaims only for the StatefulSet, DESIRED 3 on four nodes, a completed Job, an idle CronJob.
Success conditionYou can pick a workload kind from what the application needs.
-
The difference that decides it
Delete a Pod from the Deployment and one from the StatefulSet, and look at what came back:
POD OWNER agent-... DaemonSet db-0 StatefulSet db-1 StatefulSet web-<hash>-<new> ReplicaSetdb-1isdb-1again - same name, and it reattached the samedata-db-1claim. The Deployment's replacement has a new random suffix and carries nothing over.That single behaviour is the whole choice. If any part of your application cares which instance it is - a database replica, a queue consumer with a partition, anything that owns data on disk - it needs a StatefulSet. If every instance is interchangeable, a Deployment is simpler, rolls faster and scales more freely.
CKAD leans heavily on Deployments, and being able to say why the other four exist is what the objective is asking for.
bash Example session kubectl -n ckad-wl delete pod web-$(kubectl -n ckad-wl get pods -l app=web -o jsonpath='{.items[0].metadata.name}' | cut -d- -f2-) --wait=true 2>/dev/null || kubectl -n ckad-wl delete pod -l app=web --field-selector=status.phase=Running --wait=truepod "web-5d6b5c7df9-4pmxx" deleted from ckad-wl namespacekubectl -n ckad-wl delete pod db-1 --wait=truepod "db-1" deleted from ckad-wl namespacesleep 25; kubectl -n ckad-wl get pods -o 'custom-columns=POD:.metadata.name,OWNER:.metadata.ownerReferences[0].kind' --sort-by=.metadata.namePOD OWNERagent-6ckgf DaemonSetagent-h2c7b DaemonSetagent-rfhqf DaemonSetdb-0 StatefulSetdb-1 StatefulSetonce-xstmc Jobweb-5d6b5c7df9-ht4qj ReplicaSetweb-5d6b5c7df9-r7q9f ReplicaSetkubectl delete namespace ckad-wl --ignore-not-found --wait=trueExpected result
db-1back under its own name; the Deployment Pod replaced by a stranger.Success conditionYou can justify a StatefulSet instead of reaching for one by habit.
Troubleshooting
A DaemonSet has fewer Pods than the cluster has nodes.
Why: Some nodes carry taints it does not tolerate.
Fix:Expected on a kubeadm control plane. Add a toleration if you want it there.
A Deployment with two replicas and one PVC has a Pod stuck.
Why:
ReadWriteOnceallows one node at a time, and both replicas want it.Fix:Use a StatefulSet with
volumeClaimTemplatesso each replica gets its own.A CronJob has never run.
Why: The schedule has not come round, or it is suspended.
Fix:
kubectl get cronjobshowsLAST SCHEDULEandSUSPEND. See guide 24.Deleting a StatefulSet left its storage behind.
Why: PVCs from
volumeClaimTemplatesare retained deliberately.Fix:Delete the claims explicitly. See guide 27.