cheat-sheets

Kubernetes Workloads

Deployments, StatefulSets, DaemonSets, Jobs and CronJobs, plus the probe and resource settings every workload needs. Use it for rollout, rollback and scheduling behaviour; see Kubernetes for the concepts and Kubectl for general commands.

Documentation: Kubernetes Workloads, Configure Liveness, Readiness and Startup Probes, Resource Management for Pods and Containers

Verification: kubectl subcommands and flags were checked against kubectl --help from v1.37.2 (no cluster available, so nothing was applied). Manifest field names and enum values were checked against the OpenAPI schema of Kubernetes v1.37.2 (api/openapi-spec/swagger.json); kubectl explain needs a live API server and was not run. Rollout, probe and scheduling behaviour follows the official docs and was not observed on a cluster. Some features are version-dependent (noted below).

Choosing a Workload #

KINDUSE FORNOTES
DeploymentStateless appsRolling updates, rollback, scaling
StatefulSetDatabases, clustered appsStable names (NAME-0, NAME-1), one PVC per Pod, ordered start and update
DaemonSetNode agents (logs, monitoring, CNI)One Pod per matching node
JobRun-to-completion taskRetries until the completion count is met
CronJobScheduled taskCreates a Job on a cron schedule

Deployments #

COMMANDDESCRIPTION
kubectl create deployment <NAME> --image=<IMAGE> --replicas=2 --dry-run=client -o yamlGenerate a starter manifest without creating anything
kubectl apply -f <FILE>Create or update
kubectl set image deployment/<NAME> <CONTAINER>=<IMAGE>:<TAG>Change an image, which triggers a rollout
kubectl scale deployment/<NAME> --replicas=5Change the replica count
kubectl scale deployment/<NAME> --current-replicas=2 --replicas=5Scale only if the current count matches
kubectl autoscale deployment <NAME> --min=2 --max=10 --cpu=80%Create a HorizontalPodAutoscaler (needs metrics-server)
kubectl rollout status deployment/<NAME> --timeout=120sWait for a rollout, non-zero exit on failure (good for CI)
kubectl rollout history deployment/<NAME>List revisions
kubectl rollout history deployment/<NAME> --revision=2Show one revision’s Pod template
kubectl rollout undo deployment/<NAME>Roll back to the previous revision
kubectl rollout undo deployment/<NAME> --to-revision=2Roll back to a given revision
kubectl rollout restart deployment/<NAME>Rolling restart with the same template
kubectl rollout pause deployment/<NAME> / kubectl rollout resume deployment/<NAME>Batch several changes into one rollout
kubectl annotate deployment/<NAME> kubernetes.io/change-cause="<REASON>"Record why, shown in the CHANGE-CAUSE column of rollout history

Strategy #

FIELDDESCRIPTION
spec.strategy.type: RollingUpdateDefault. Replace Pods gradually
spec.strategy.type: RecreateStop all old Pods, then start new ones (downtime; use when two versions must never overlap)
rollingUpdate.maxSurgeExtra Pods allowed above replicas during an update (number or %, default 25%)
rollingUpdate.maxUnavailablePods allowed to be unavailable during an update (number or %, default 25%)
spec.minReadySecondsHow long a new Pod must be ready before it counts as available
spec.progressDeadlineSecondsTime before the rollout is reported as failed (default 600). Kubernetes does not roll back automatically
spec.revisionHistoryLimitOld ReplicaSets kept for rollback (default 10)
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  replicas: 3
  revisionHistoryLimit: 5
  minReadySeconds: 5
  progressDeadlineSeconds: 300
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: web
          image: nginx:1.27
          ports:
            - containerPort: 80

maxUnavailable: 0 with maxSurge: 1 keeps full capacity during updates but needs room for one extra Pod. spec.selector is immutable after creation.

StatefulSets #

Needs a headless Service (clusterIP: None) whose name matches spec.serviceName. Pods get stable DNS names <POD>.<SERVICE>.<NAMESPACE>.svc.

COMMANDDESCRIPTION
kubectl get statefulset,pods,pvc -l app=<LABEL>Show the set, its Pods and its claims
kubectl scale statefulset/<NAME> --replicas=3Scale (ordered by default)
kubectl rollout status statefulset/<NAME>Watch an update
kubectl rollout undo statefulset/<NAME>Roll back the Pod template
kubectl delete statefulset/<NAME> --cascade=orphanDelete the set but leave the Pods running
kubectl delete pvc <PVC>Claims are kept by default when the set is deleted or scaled down. This deletes the data
FIELDDESCRIPTION
spec.serviceNameHeadless Service that governs the Pods
spec.podManagementPolicyOrderedReady (default, one at a time) or Parallel
spec.updateStrategy.typeRollingUpdate (default, highest ordinal first) or OnDelete (update only when you delete a Pod)
spec.updateStrategy.rollingUpdate.partitionOnly Pods with ordinal >= partition are updated. Use for canaries
spec.volumeClaimTemplatesOne PVC per Pod, named <TEMPLATE>-<POD>
spec.persistentVolumeClaimRetentionPolicywhenDeleted / whenScaled: Retain (default) or Delete
yaml
apiVersion: v1
kind: Service
metadata:
  name: db
spec:
  clusterIP: None
  selector:
    app: db
  ports:
    - port: 5432
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: db
spec:
  serviceName: db
  replicas: 3
  selector:
    matchLabels:
      app: db
  updateStrategy:
    type: RollingUpdate
    rollingUpdate:
      partition: 0
  template:
    metadata:
      labels:
        app: db
    spec:
      containers:
        - name: db
          image: postgres:17
          ports:
            - containerPort: 5432
          volumeMounts:
            - name: data
              mountPath: /var/lib/postgresql/data
  volumeClaimTemplates:
    - metadata:
        name: data
      spec:
        accessModes: ["ReadWriteOnce"]
        resources:
          requests:
            storage: 1Gi

Image and credentials above are illustrative: a real Postgres Pod also needs POSTGRES_PASSWORD from a Secret.

DaemonSets #

COMMANDDESCRIPTION
kubectl get daemonset -n <NAMESPACE>DESIRED, CURRENT and READY per node count
kubectl rollout status daemonset/<NAME>Watch an update
kubectl rollout undo daemonset/<NAME>Roll back
kubectl get pods -o wide -l <LABEL>See which node each Pod landed on
FIELDDESCRIPTION
spec.updateStrategy.typeRollingUpdate (default) or OnDelete
spec.updateStrategy.rollingUpdate.maxUnavailableNodes updated at once (default 1)
spec.updateStrategy.rollingUpdate.maxSurgeNodes that may run an extra updated Pod before the old one is removed (default 0; cannot be 0 if maxUnavailable is 0)
spec.template.spec.nodeSelectorRestrict to labelled nodes
spec.template.spec.tolerationsRun on tainted nodes, e.g. control plane
yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: node-agent
spec:
  selector:
    matchLabels:
      app: node-agent
  updateStrategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1
  template:
    metadata:
      labels:
        app: node-agent
    spec:
      tolerations:
        - key: node-role.kubernetes.io/control-plane
          operator: Exists
          effect: NoSchedule
      containers:
        - name: agent
          image: busybox:1.37
          command: ["sh", "-c", "while true; do sleep 3600; done"]

Jobs #

COMMANDDESCRIPTION
kubectl create job <NAME> --image=busybox -- dateCreate a one-off Job
kubectl create job <NAME> --from=cronjob/<CRONJOB>Run a CronJob now
kubectl get jobsCOMPLETIONS shows succeeded/required
kubectl wait --for=condition=complete job/<NAME> --timeout=300sBlock until done
kubectl logs job/<NAME>Logs from one of the Job’s Pods
kubectl delete job <NAME>Remove the Job and its Pods
FIELDDESCRIPTION
spec.completionsSuccessful Pods needed (default 1)
spec.parallelismPods running at once (default 1)
spec.backoffLimitRetries before the Job is marked failed (default 6)
spec.activeDeadlineSecondsHard time limit for the whole Job
spec.ttlSecondsAfterFinishedAuto-delete the Job after it finishes
spec.completionModeNonIndexed (default) or Indexed (each Pod gets an index)
spec.suspendPause creation of Pods
spec.template.spec.restartPolicyMust be Never or OnFailure, not Always
yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: migrate
spec:
  backoffLimit: 3
  activeDeadlineSeconds: 600
  ttlSecondsAfterFinished: 3600
  template:
    spec:
      restartPolicy: Never
      containers:
        - name: migrate
          image: busybox:1.37
          command: ["sh", "-c", "echo migrating"]

CronJobs #

COMMANDDESCRIPTION
kubectl create cronjob <NAME> --image=busybox --schedule="*/5 * * * *" -- dateCreate a CronJob
kubectl get cronjobShows schedule, SUSPEND, ACTIVE and LAST SCHEDULE
kubectl patch cronjob <NAME> -p '{"spec":{"suspend":true}}'Pause scheduling (false to resume)
kubectl create job <NAME> --from=cronjob/<CRONJOB>Trigger a manual run
FIELDDESCRIPTION
spec.scheduleStandard five-field cron expression
spec.timeZoneIANA zone such as Europe/London. Without it the schedule uses the time zone of the controller manager
spec.concurrencyPolicyAllow (default), Forbid (skip if still running) or Replace
spec.startingDeadlineSecondsHow late a run may start before it counts as missed
spec.successfulJobsHistoryLimitFinished Jobs kept (default 3)
spec.failedJobsHistoryLimitFailed Jobs kept (default 1)
spec.suspendStop creating Jobs
spec.jobTemplateA Job spec, as above
yaml
apiVersion: batch/v1
kind: CronJob
metadata:
  name: nightly-backup
spec:
  schedule: "0 2 * * *"
  timeZone: Europe/London
  concurrencyPolicy: Forbid
  startingDeadlineSeconds: 300
  successfulJobsHistoryLimit: 3
  failedJobsHistoryLimit: 1
  jobTemplate:
    spec:
      backoffLimit: 2
      ttlSecondsAfterFinished: 86400
      template:
        spec:
          restartPolicy: OnFailure
          containers:
            - name: backup
              image: busybox:1.37
              command: ["sh", "-c", "echo backup"]

Probes #

PROBEPURPOSEON FAILURE
startupProbeApp has finished starting. Other probes are disabled until it succeedsContainer restarted
livenessProbeApp is still workingContainer restarted
readinessProbeApp can take trafficPod removed from Service endpoints (no restart)
HANDLERDESCRIPTION
httpGetSuccess on HTTP status 200 to 399
tcpSocketSuccess if the port accepts a connection
execSuccess if the command exits 0
grpcSuccess if the gRPC health check returns SERVING
TIMING FIELDDEFAULTDESCRIPTION
initialDelaySeconds0Wait before the first probe
periodSeconds10Interval between probes
timeoutSeconds1Time allowed per probe
successThreshold1Passes needed to recover (must be 1 for liveness and startup)
failureThreshold3Failures before the probe is considered failed
terminationGracePeriodSecondsPod valueOverrides the Pod’s grace period when this probe fails (feature-gated in some versions)
yaml
containers:
  - name: web
    image: nginx:1.27
    ports:
      - containerPort: 80
    startupProbe:
      httpGet: { path: /, port: 80 }
      periodSeconds: 5
      failureThreshold: 30    # up to 150s to start
    livenessProbe:
      httpGet: { path: /, port: 80 }
      periodSeconds: 10
    readinessProbe:
      httpGet: { path: /, port: 80 }
      periodSeconds: 5

Keep liveness probes cheap and independent of dependencies such as the database, otherwise an outage restarts every Pod. Probe failures appear as events in kubectl describe pod <POD>.

Resource Requests and Limits #

FIELDDESCRIPTION
resources.requestsUsed by the scheduler to place the Pod, and the guaranteed share
resources.limitsHard ceiling. CPU over the limit is throttled; memory over the limit gets the container OOMKilled
cpu: 500m500 millicores (half a core)
memory: 256MiBinary units (Mi, Gi). M and G are decimal
QOS CLASSWHEN
GuaranteedEvery container has requests equal to limits for both CPU and memory
BurstableAt least one request or limit is set, but not Guaranteed
BestEffortNo requests or limits. First to be evicted under node pressure
COMMANDDESCRIPTION
kubectl set resources deployment/<NAME> -c=<CONTAINER> --requests=cpu=100m,memory=128Mi --limits=memory=256MiChange resources on a live Deployment (triggers a rollout)
kubectl top pod --containersLive usage per container (needs metrics-server)
kubectl top pod --sort-by=memoryHeaviest Pods first
kubectl top nodeNode usage
kubectl describe node <NODE>Allocatable capacity and what is already requested
kubectl get pod <POD> -o jsonpath='{.status.qosClass}'Show the QoS class
kubectl get pod <POD> -o jsonpath='{.status.containerStatuses[*].lastState.terminated.reason}'OOMKilled after a memory kill
yaml
resources:
  requests:
    cpu: 100m
    memory: 128Mi
  limits:
    memory: 256Mi    # no CPU limit: avoids throttling, still requests CPU

Omitting a CPU limit is a common choice, not a rule: set one if you need a hard cap. Set a namespace default with a LimitRange and a cap with a ResourceQuota.

Troubleshooting #

SYMPTOMCHECK
Rollout stuck, new Pods 0/1kubectl describe pod <POD>: failing readiness probe, image pull or no resources. kubectl rollout undo to revert
ProgressDeadlineExceededprogressDeadlineSeconds passed. The old Pods keep serving. Fix and re-apply, or undo
Pod restarts repeatedlyLiveness probe too strict or initialDelaySeconds too short: add a startupProbe
Pod PendingRequests exceed free node capacity (kubectl describe node), or taints and selectors do not match
StatefulSet Pod stuckOrdered start waits for the previous Pod to be ready. Check its PVC is Bound
CronJob never runsSUSPEND is true, bad cron expression or too many missed runs (check kubectl describe cronjob)
Job Pods keep failingkubectl logs job/<NAME>; remember backoffLimit retries before the Job fails
Old Jobs pile upSet ttlSecondsAfterFinished or the CronJob history limits

See Also #

Search all sheets

Tip: / or Ctrl/Cmd+K to open, Esc to close.