StatefulSets + DaemonSets cheatsheet.
StatefulSet
For stateful workloads (DBs, Kafka, etc):
- Stable network identity (
<set>-0,<set>-1, …). - Ordered start / stop.
- Per-replica PVC.
apiVersion: apps/v1
kind: StatefulSet
metadata: { name: pg }
spec:
serviceName: pg # headless service
replicas: 3
selector: { matchLabels: { app: pg } }
template:
metadata: { labels: { app: pg } }
spec:
containers:
- name: pg
image: postgres:16
ports: [{ containerPort: 5432, name: pg }]
volumeMounts: [{ name: data, mountPath: /var/lib/postgresql/data }]
volumeClaimTemplates:
- metadata: { name: data }
spec:
accessModes: [ReadWriteOnce]
resources: { requests: { storage: 10Gi } }
---
apiVersion: v1
kind: Service
metadata: { name: pg }
spec:
clusterIP: None # headless
selector: { app: pg }
ports: [{ port: 5432 }]
Pods get:
pg-0,pg-1,pg-2names.- DNS:
pg-0.pg.default.svc.cluster.local. - PVCs:
data-pg-0,data-pg-1,data-pg-2.
OrderedReady / Parallel
spec:
podManagementPolicy: OrderedReady # default; one at a time
# or:
podManagementPolicy: Parallel # start all together
Update strategies
spec:
updateStrategy:
type: RollingUpdate
rollingUpdate:
partition: 0 # rolling from highest ordinal down
maxUnavailable: 1
partition: update only pods with ordinal >= partition. Useful for canary in stateful.
PVC retention
spec:
persistentVolumeClaimRetentionPolicy:
whenDeleted: Retain # or Delete
whenScaled: Retain
Avoids losing data on accidental delete.
DaemonSet
One pod per node (or selected nodes):
apiVersion: apps/v1
kind: DaemonSet
metadata: { name: node-exporter, namespace: monitoring }
spec:
selector: { matchLabels: { app: node-exporter } }
template:
metadata: { labels: { app: node-exporter } }
spec:
tolerations:
- operator: Exists # run on every node
hostNetwork: true # for system-level access
hostPID: true
containers:
- name: exporter
image: prom/node-exporter
args: ["--path.rootfs=/host"]
volumeMounts:
- { name: rootfs, mountPath: /host, readOnly: true }
volumes:
- name: rootfs
hostPath: { path: / }
Use for: monitoring agents, log shippers, CNI, CSI.
nodeSelector / affinity
spec:
template:
spec:
nodeSelector:
disk: ssd
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- { key: node-role.kubernetes.io/worker, operator: Exists }
Job
Run once until completion:
apiVersion: batch/v1
kind: Job
metadata: { name: migrate }
spec:
backoffLimit: 3
template:
spec:
restartPolicy: OnFailure
containers:
- name: migrate
image: myapp:v1
command: ["python", "manage.py", "migrate"]
kubectl get jobs
kubectl logs job/migrate
kubectl delete job migrate
Parallel job
spec:
parallelism: 5
completions: 10 # need 10 successful runs
backoffLimit: 3
For batch workloads.
CronJob
apiVersion: batch/v1
kind: CronJob
metadata: { name: backup }
spec:
schedule: "0 2 * * *"
jobTemplate:
spec:
backoffLimit: 1
template:
spec:
restartPolicy: OnFailure
containers:
- name: backup
image: myapp:v1
command: ["./backup.sh"]
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 1
concurrencyPolicy: Forbid # don't run if previous still running
startingDeadlineSeconds: 300
Concurrency: Allow (default), Forbid, Replace.
Trigger manually
kubectl create job --from=cronjob/backup backup-manual
ttlSecondsAfterFinished
spec:
ttlSecondsAfterFinished: 300 # auto-delete 5min after success
Cleanup completed Jobs.
suspending CronJob
spec:
suspend: true
kubectl patch cronjob backup -p '{"spec":{"suspend":true}}'
Indexed Job
spec:
completionMode: Indexed
parallelism: 5
completions: 5
template:
spec:
containers:
- name: worker
env:
- name: JOB_INDEX
valueFrom: { fieldRef: { fieldPath: metadata.annotations['batch.kubernetes.io/job-completion-index'] } }
Each pod gets unique index 0..N-1. Useful for partitioned work.
podFailurePolicy
spec:
podFailurePolicy:
rules:
- action: FailJob
onExitCodes: { containerName: main, values: [42] }
- action: Ignore
onExitCodes: { containerName: main, values: [1] }
Fine-grained control over what counts as failure.
Common mistakes
- StatefulSet without headless service → no DNS per pod.
- DaemonSet without tolerations → not on tainted nodes (e.g., control plane).
- Job without
ttlSecondsAfterFinished→ completed pods accumulate. - CronJob
Allowconcurrency for long jobs → overlapping runs. - StatefulSet scale-down leaves PVCs unless retention policy set.
Read this next
If you want my StatefulSet templates (Postgres, Redis), they’re at rajpoot.dev .
Building something AI-, backend-, or data-heavy and want a second pair of eyes? I do consulting and freelance work — see my projects and ways to reach me at rajpoot.dev .