Definition
These are Kubernetes workload objects at increasing levels of management.
- Pod – the smallest deployable unit, one or more tightly-coupled containers sharing a network namespace and optional volumes, but ephemeral and not self-healing on its own.
- ReplicaSet – ensures a specified number of identical Pod replicas are always running, recreating any that die.
- Deployment – manages ReplicaSets to provide declarative rolling updates and rollbacks for stateless applications, and is the object you normally use.
- StatefulSet – manages Pods that need a stable ordinal name, a stable network identity via a headless Service, and per-Pod persistent storage for stateful workloads.
- DaemonSet – ensures exactly one copy of a Pod runs on every matching node, for node-level infrastructure agents.
- Job / CronJob – Job runs Pods to successful completion for a finite task with retries; CronJob wraps a Job with a schedule.
Think of staffing at the National Bank of India. A Pod is a single teller doing a job, but if they quit nobody automatically replaces them. A ReplicaSet is a rule saying always keep exactly five tellers on duty, hiring a replacement the moment one leaves. A Deployment is the HR manager who not only keeps five tellers but can smoothly roll in a newly-trained batch and roll back if the new training was bad. A StatefulSet is for named specialists who each own a specific locked drawer and must keep the same desk and drawer even after a break. A DaemonSet is the rule that every branch must have exactly one security guard. A Job is a one-off task like run the end-of-day reconciliation until it finishes successfully.
Understanding the Concepts
What is a Pod?
A Pod is the smallest deployable unit in Kubernetes. It wraps one or more containers that share the same network namespace, meaning the same IP address and port space, and can share volumes. All containers in a Pod are co-scheduled on the same node and act as a single unit.
You rarely create bare Pods in production because a bare Pod is not replaced if its node dies. It has no controller watching over it. Pods are ephemeral by design and get a new IP each time they are recreated.
# bare-pod.yaml - a Pod with no controller (not recommended for production)
apiVersion: v1
kind: Pod
metadata:
name: transfer-service
labels:
app: transfer-service
spec:
containers:
- name: transfer
image: nbi-registry.example.com/transfer-service:1.0.0
ports:
- containerPort: 8080
kubectl apply -f bare-pod.yaml
kubectl get pods
# If the node dies, this Pod is gone forever
What is a ReplicaSet?
A ReplicaSet ensures a specified number of identical Pod replicas exist at all times. If a Pod dies, the ReplicaSet creates a replacement. You normally do not create ReplicaSets directly because they do not handle updates gracefully, but they are the mechanism a Deployment uses under the hood.
# replicaset.yaml - keeps 3 transfer-service Pods running
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: transfer-service-rs
spec:
replicas: 3
selector:
matchLabels:
app: transfer-service
template:
metadata:
labels:
app: transfer-service
spec:
containers:
- name: transfer
image: nbi-registry.example.com/transfer-service:1.0.0
ports:
- containerPort: 8080
kubectl apply -f replicaset.yaml
kubectl get rs
# Delete one Pod and watch the ReplicaSet recreate it
kubectl delete pod transfer-service-rs-abc12
kubectl get pods # back to 3 within seconds
What is a Deployment?
A Deployment is the standard object for stateless services. It manages ReplicaSets to give declarative rolling updates. When you change the image tag, the Deployment creates a new ReplicaSet and scales it up while scaling the old one down. It keeps the old ReplicaSet so you can roll back instantly.
# deployment.yaml - the standard way to run a stateless banking API
apiVersion: apps/v1
kind: Deployment
metadata:
name: transfer-service
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
app: transfer-service
template:
metadata:
labels:
app: transfer-service
spec:
containers:
- name: transfer
image: nbi-registry.example.com/transfer-service:2.0.0
ports:
- containerPort: 8080
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: "1"
memory: 512Mi
# Update the image to trigger a rolling update
kubectl set image deployment/transfer-service \
transfer=nbi-registry.example.com/transfer-service:2.1.0
# Watch the rollout progress
kubectl rollout status deployment/transfer-service
# Roll back to the previous revision if the new version is broken
kubectl rollout undo deployment/transfer-service
What is a StatefulSet?
A StatefulSet is for workloads that need identity and storage stability. Each Pod gets a stable ordinal name like ledger-db-0, ledger-db-1, a stable network identity via a headless Service, and its own PersistentVolumeClaim that stays bound to that specific Pod. Pods are created, scaled, and terminated in a predictable order.
# statefulset.yaml - a clustered database for the banking ledger
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: ledger-db
spec:
serviceName: ledger-db-headless
replicas: 3
selector:
matchLabels:
app: ledger-db
template:
metadata:
labels:
app: ledger-db
spec:
containers:
- name: postgres
image: postgres:16
ports:
- containerPort: 5432
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 100Gi
kubectl get pods
# NAME READY STATUS
# ledger-db-0 1/1 Running
# ledger-db-1 1/1 Running
# ledger-db-2 1/1 Running
# Each Pod owns its own PVC: data-ledger-db-0, data-ledger-db-1, data-ledger-db-2
How is a Deployment different from a StatefulSet?
This is the most common comparison in interviews. The core difference is whether replicas are interchangeable.
| Aspect | Deployment | StatefulSet |
|---|---|---|
| Pod names | Random suffix (e.g. abc12) | Stable ordinal (e.g. db-0, db-1) |
| Pod identity | Interchangeable | Unique and persistent |
| Storage | Shared or none | Per-Pod PersistentVolumeClaim |
| Scaling order | Parallel (all at once) | Sequential (0, 1, 2...) |
| Network identity | Via Service ClusterIP | Stable DNS per Pod via headless Service |
| Use case | Stateless APIs | Databases, message brokers |
# WRONG - running a database as a Deployment
# Pods are interchangeable, so replication state breaks
kubectl get pods -l app=ledger-db
# NAME READY STATUS
# ledger-db-6d4f9c7b8a-x9k2m 1/1 Running <- random name, no stable storage
# CORRECT - running a database as a StatefulSet
kubectl get pods -l app=ledger-db
# NAME READY STATUS
# ledger-db-0 1/1 Running <- stable name, owns data-ledger-db-0 PVC
# ledger-db-1 1/1 Running <- stable name, owns data-ledger-db-1 PVC
What is a DaemonSet?
A DaemonSet ensures one Pod per node. As nodes join or leave the cluster, the DaemonSet automatically adds or removes its Pod. This is for node-level infrastructure such as log collectors, metrics agents, or network plugins.
# daemonset.yaml - a log collector on every node
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: log-collector
spec:
selector:
matchLabels:
app: log-collector
template:
metadata:
labels:
app: log-collector
spec:
containers:
- name: fluentbit
image: fluent/fluent-bit:2.2
volumeMounts:
- name: varlog
mountPath: /var/log
readOnly: true
volumes:
- name: varlog
hostPath:
path: /var/log
kubectl get ds
# NAME DESIRED CURRENT READY
# log-collector 5 5 5 <- one per node in a 5-node cluster
# Add a new node and the DaemonSet automatically schedules a Pod on it
What is a Job and a CronJob?
A Job runs one or more Pods to successful completion for a finite task. It retries on failure up to a backoff limit. A CronJob wraps a Job with a cron schedule.
# job.yaml - nightly end-of-day reconciliation
apiVersion: batch/v1
kind: Job
metadata:
name: eod-reconciliation
spec:
backoffLimit: 3
completions: 1
template:
spec:
containers:
- name: reconciler
image: nbi-registry.example.com/eod-reconciler:1.0.0
env:
- name: RECONCILIATION_DATE
value: "2025-01-15"
restartPolicy: Never
# cronjob.yaml - schedule the reconciliation every night at 11:30 PM
apiVersion: batch/v1
kind: CronJob
metadata:
name: nightly-reconciliation
spec:
schedule: "30 23 * * *"
jobTemplate:
spec:
backoffLimit: 3
template:
spec:
containers:
- name: reconciler
image: nbi-registry.example.com/eod-reconciler:1.0.0
restartPolicy: Never
kubectl get jobs
# NAME COMPLETIONS DURATION
# eod-reconciliation 1/1 45s
kubectl get cronjobs
# NAME SCHEDULE LAST SCHEDULE
# nightly-reconciliation 30 23 * * * 2m ago
What are common mistakes?
The most frequent mistake is using a Deployment for a database. A Deployment treats all replicas as interchangeable and does not provide stable names or per-Pod storage, so database replication state breaks when Pods are replaced.
# WRONG - database as a Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: ledger-db
spec:
replicas: 3
template:
spec:
containers:
- name: postgres
image: postgres:16
# No stable identity, no per-Pod volume
# Replication state corrupts on Pod replacement
# CORRECT - database as a StatefulSet
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: ledger-db
spec:
serviceName: ledger-db-headless
replicas: 3
template:
spec:
containers:
- name: postgres
image: postgres:16
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 100Gi
Another mistake is not making Job tasks idempotent. A Job Pod can be retried, and for financial batch work an accidentally-double-run reconciliation is dangerous. Always design the task so running it twice produces the same result.
When should you use each workload object?
The decision is based on the nature of the workload.
- Deployment for stateless services where any replica is interchangeable: the Transfer API, the Loan Calculator, the API Gateway.
- StatefulSet for stateful workloads that need stable identity and per-Pod storage: a clustered Postgres database, a Kafka broker, a Redis cluster.
- DaemonSet for node-level agents that must run on every node: Fluent Bit for logs, Prometheus Node Exporter for metrics, a CNI plugin.
- Job for one-off finite tasks: a database migration, an end-of-day reconciliation, a data export.
- CronJob for scheduled finite tasks: nightly interest accrual, weekly report generation, daily backup.
# Quick summary of what is running in the banking namespace
kubectl get deployments,statefulsets,daemonsets,jobs,cronjobs -n banking
What should a senior engineer know?
A senior engineer should understand that even StatefulSets in banking are often avoided for the primary ledger in favour of a managed database service like Amazon RDS, keeping stateful risk out of the cluster. Running a database in Kubernetes is operationally complex, so many teams use StatefulSets only for workloads like Kafka or Redis where the operational tooling is mature.
Job semantics must be set carefully. The backoffLimit controls retries, completions controls how many Pods must succeed, and parallelism controls how many run at once. For financial batch work, the task must be idempotent because retries mean the code runs more than once.
DaemonSets respect taints and tolerations, so a DaemonSet can be scoped to a subset of nodes using a node selector or toleration rather than running on every single node.
# Check the QoS and controller of each Pod
kubectl get pods -o custom-columns=NAME:.metadata.name,CONTROLLER:.metadata.ownerReferences[0].kind,QOS:.status.qosClass
Visual Overview
Kubernetes Workload Object Hierarchy ===================================== ┌─────────────────────────────────────────────────┐ │ Deployment │ │ (manages rolling updates and rollbacks) │ │ │ │ ┌───────────────────┐ ┌───────────────────┐ │ │ │ ReplicaSet v2 │ │ ReplicaSet v1 │ │ │ │ (current) │ │ (previous) │ │ │ │ ┌─────┐ ┌─────┐ │ │ (scaled to 0) │ │ │ │ │Pod A│ │Pod B│ │ │ │ │ │ │ └─────┘ └─────┘ │ └───────────────────┘ │ │ └───────────────────┘ │ └─────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────┐ │ StatefulSet │ │ (stable names, ordered, per-Pod PVCs) │ │ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ db-0 │ │ db-1 │ │ db-2 │ │ │ │ PVC: d-0 │ │ PVC: d-1 │ │ PVC: d-2 │ │ │ └──────────┘ └──────────┘ └──────────┘ │ └─────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────┐ │ DaemonSet │ │ (one Pod per node) │ │ │ │ Node-1: [log-collector] │ │ Node-2: [log-collector] │ │ Node-3: [log-collector] │ └─────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────┐ │ CronJob → Job │ │ (schedule triggers a Job, Job runs Pods) │ │ │ │ CronJob: 30 23 * * * │ │ └─→ Job: eod-reconciliation-28456 │ │ └─→ Pod: eod-reconciliation-28456-xk9 │ │ status: Completed │ └─────────────────────────────────────────────────┘
Key Takeaway
These six workload objects sit at increasing levels of management, and the key skill is picking the right one for the nature of the workload. A Pod is the smallest unit, but it is ephemeral and not self-healing, so you rarely run bare Pods. A ReplicaSet keeps a specified number of replicas alive, but it does not handle updates, so you use a Deployment instead. A Deployment manages ReplicaSets to give rolling updates and instant rollback, and it is the standard choice for any stateless service where replicas are interchangeable, like the Transfer API at the National Bank of India.
A StatefulSet is for workloads that need stability. Each Pod gets a stable ordinal name, a stable DNS identity through a headless Service, and its own PersistentVolumeClaim that stays bound across restarts and reschedules. That is what a clustered database or a Kafka broker needs. A DaemonSet ensures one Pod per node for infrastructure agents like log collectors and metrics exporters. A Job runs a finite task to completion with retries, and a CronJob schedules Jobs on a recurring basis for batch work like nightly reconciliation.
The classic mistake is running a database as a Deployment. The interchangeable, unordered Pods break replication state, so databases belong in a StatefulSet or a managed external service. For Job-based financial batch work, the task must be idempotent because retries mean the code can run more than once.
Remember one sentence: use a Deployment for stateless services, a StatefulSet for stateful workloads that need identity and storage, a DaemonSet for one-per-node agents, and a Job for finite tasks.
---