interview-prep

Crisp answer: Deployments manage stateless pods that are interchangeable. StatefulSets manage stateful pods that each need a stable identity, stable storage, and ordered startup and shutdown.

Deployment — for stateless workloads

A Deployment creates a ReplicaSet which creates pods. All pods are identical and interchangeable. The Deployment can replace any pod with a new one from the same template — the new pod gets a random name and may land on a different node. This is fine for stateless apps: web servers, API services, workers.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-server
spec:
  replicas: 3
  selector:
    matchLabels:
      app: api-server
  template:
    spec:
      containers:
      - name: api
        image: myapp:v1

Pods get random names: api-server-7d4f9b8c6-xk2pq, api-server-7d4f9b8c6-mn3rt

Scale up: new pod starts immediately, no ordering. Scale down: any pod can be deleted, no ordering. Rolling update: new pods start before old pods stop (configurable).

StatefulSet — for stateful workloads

A StatefulSet gives each pod a stable, predictable identity that persists across restarts and rescheduling:

  • Stable pod name: postgres-0, postgres-1, postgres-2 — always these exact names, regardless of which node the pod runs on
  • Stable network identity: Each pod gets a DNS record: postgres-0.postgres.default.svc.cluster.local
  • Stable storage: Each pod gets its own PVC created from a volumeClaimTemplate. The PVC persists when the pod is deleted and is reattached to the replacement pod with the same ordinal.
  • Ordered startup: Pods start in order: 0 must be Running before 1 starts. This allows postgres-0 to become the primary before replicas join.
  • Ordered shutdown: Pods terminate in reverse order: N-1, N-2, ... 0
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: postgres
spec:
  serviceName: postgres        # Headless service for DNS
  replicas: 3
  selector:
    matchLabels:
      app: postgres
  template:
    spec:
      containers:
      - name: postgres
        image: postgres:16
        volumeMounts:
        - name: data
          mountPath: /var/lib/postgresql/data
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteOnce"]
      resources:
        requests:
          storage: 10Gi

This creates: postgres-0 with PVC data-postgres-0, postgres-1 with PVC data-postgres-1, etc.

The headless service:

StatefulSets use a headless service (clusterIP: None) to create DNS records for individual pods. Normal Services load-balance across pods. A headless Service creates A records directly to pod IPs:

apiVersion: v1
kind: Service
metadata:
  name: postgres
spec:
  clusterIP: None       # headless
  selector:
    app: postgres
  ports:
  - port: 5432
# DNS resolution with headless service:
nslookup postgres-0.postgres.default.svc.cluster.local
# → 10.244.1.5  (the actual pod IP, not a ClusterIP VIP)

# Normal service resolution:
nslookup postgres.default.svc.cluster.local
# → 10.96.45.12  (ClusterIP that load-balances across all pods)

When to use each:

Use case Type
Web API server, microservice Deployment
Worker queue consumer Deployment
Postgres, MySQL, Redis with replication StatefulSet
Kafka, Elasticsearch StatefulSet
Prometheus, Vault StatefulSet
CNPG (CloudNativePG) operator Manages StatefulSets internally

Practical differences:

# Delete a Deployment pod — it's immediately replaced by a new random pod:
kubectl delete pod api-server-7d4f9b8c6-xk2pq
# New pod: api-server-7d4f9b8c6-zzq9m (different name, same template)

# Delete a StatefulSet pod — it's replaced with the SAME name and reattaches the SAME PVC:
kubectl delete pod postgres-1
# New pod: postgres-1 (same name, reattaches data-postgres-1 PVC)

# Scale down a StatefulSet — ordinals are preserved:
kubectl scale statefulset postgres --replicas=2
# postgres-2 is deleted. postgres-0 and postgres-1 remain.
# Scale back to 3: postgres-2 is created and gets data-postgres-2 back.

What to say in the interview:

"Deployments manage interchangeable stateless pods — any pod can be killed and replaced with a new one from the same template, names are random. That's fine for API servers and workers. StatefulSets give each pod a stable identity: a predictable name like postgres-0, a DNS record specific to that pod via a headless service, and its own PVC from a volumeClaimTemplate that persists when the pod is deleted and reattaches to the replacement. Startup and shutdown are ordered so a primary can be established before replicas join. I run Postgres, Vault, and Prometheus as StatefulSets in my homelab through CNPG and the respective operators — they all rely on this stable identity for leader election and replication."


My notes