Kubernetes Workload & Configuration Objects
Pods
The smallest deployable unit in Kubernetes — one or more tightly-coupled containers sharing network namespace and storage.
apiVersion: v1
kind: Pod
metadata:
name: payment-app
labels:
app: payment-app
spec:
containers:
- name: payment-app
image: myrepo/payment-app:1.2.0
ports:
- containerPort: 8080
resources:
requests:
cpu: "200m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"
livenessProbe:
httpGet:
path: /health/live
port: 8080
initialDelaySeconds: 10
periodSeconds: 15
readinessProbe:
httpGet:
path: /health/ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
Architect-level point: you almost never create bare Pods directly in production. A bare Pod has no self-healing — if its node dies, the pod is gone, permanently. Pods are managed indirectly through Deployments, StatefulSets, DaemonSets, or Jobs, which create and supervise them. Knowing the Pod spec matters because it’s embedded as the template inside every one of those higher-level objects.
ReplicaSets
Ensures a specified number of identical pod replicas are running at all times — the reconciliation-loop pattern applied to pod count.
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: payment-app-rs
spec:
replicas: 3
selector:
matchLabels:
app: payment-app
template:
metadata:
labels:
app: payment-app
spec:
containers:
- name: payment-app
image: myrepo/payment-app:1.2.0
If a pod dies, the ReplicaSet controller notices the actual count (2) doesn’t match desired (3) and creates a replacement. You rarely create a ReplicaSet directly — it’s the object a Deployment manages on your behalf. Knowing it matters because rolling updates work by creating a new ReplicaSet and scaling it up while scaling the old one down — visible if you kubectl get rs during a deployment rollout.
Deployments
The standard way to run stateless applications — adds rolling updates, rollback, and revision history on top of ReplicaSets.
apiVersion: apps/v1
kind: Deployment
metadata:
name: payment-app
spec:
replicas: 5
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # extra pods allowed above desired count during rollout
maxUnavailable: 0 # pods allowed to be unavailable during rollout
selector:
matchLabels:
app: payment-app
template:
metadata:
labels:
app: payment-app
spec:
containers:
- name: payment-app
image: myrepo/payment-app:2.0.0
What a Deployment gives you that a ReplicaSet alone doesn’t:
kubectl rollout status deployment/payment-app— watch a rollout progresskubectl rollout undo deployment/payment-app— instant rollback to the previous ReplicaSetkubectl rollout history deployment/payment-app— revision history- Configurable rollout strategy:
maxSurge/maxUnavailablecontrol how aggressive vs cautious the rolling update is —maxUnavailable: 0guarantees zero downtime at the cost of needing extra capacity headroom (maxSurge).
Architect note: Deployments assume pods are interchangeable and stateless — no pod has a “special identity.” That assumption is exactly what breaks for databases, which is why StatefulSets exist.
StatefulSets
For workloads that need stable network identity and stable, per-pod persistent storage — databases, Kafka brokers, Elasticsearch, ZooKeeper.
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres
spec:
serviceName: postgres-headless
replicas: 3
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
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: 20Gi
What makes this different from a Deployment:
- Stable, predictable pod names:
postgres-0,postgres-1,postgres-2— not random suffixes. These names persist across restarts/rescheduling. - One PersistentVolumeClaim per pod, created from
volumeClaimTemplates—postgres-0always gets the same storage volume back, even if rescheduled to a different node. - Ordered, sequential creation/scaling/deletion: pod 0 starts before pod 1 starts before pod 2 — critical for clustered systems with leader-election or bootstrap ordering (e.g., the first ZooKeeper node).
- Requires a headless Service (
clusterIP: None) to give each pod its own stable DNS name:postgres-0.postgres-headless.namespace.svc.cluster.local.
Interview-level distinction: Deployment = “I don’t care which pod, just give me N healthy replicas.” StatefulSet = “I need pod 0, 1, 2 specifically, each with its own durable identity and storage, in a defined order.”
DaemonSets
Runs exactly one pod per node (or per matching subset of nodes) — for node-level infrastructure rather than application replicas.
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd-log-collector
spec:
selector:
matchLabels:
app: fluentd
template:
metadata:
labels:
app: fluentd
spec:
containers:
- name: fluentd
image: fluent/fluentd:v1.16
volumeMounts:
- name: varlog
mountPath: /var/log
volumes:
- name: varlog
hostPath:
path: /var/log
Common use cases: log collectors (Fluentd/Fluent Bit), node monitoring agents (Prometheus Node Exporter, Datadog agent), CNI/networking plugins, storage daemons. As nodes are added to the cluster, the DaemonSet controller automatically schedules a pod onto the new node — no manual intervention. Can be restricted to specific nodes via nodeSelector or tolerations (e.g., running only on GPU nodes).
Jobs & CronJobs
For run-to-completion workloads, not long-running services.
Job — runs a pod (or several) until successful completion, then stops:
apiVersion: batch/v1
kind: Job
metadata:
name: db-migration
spec:
backoffLimit: 3 # retry up to 3 times on failure
template:
spec:
restartPolicy: Never
containers:
- name: migrate
image: myrepo/migration-tool:1.0
command: ["./run-migration.sh"]
CronJob — schedules a Job to run on a recurring schedule, standard cron syntax:
apiVersion: batch/v1
kind: CronJob
metadata:
name: nightly-report
spec:
schedule: "0 2 * * *" # 2 AM daily
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: report-generator
image: myrepo/report-gen:1.0
Architect-level points:
backoffLimitcontrols retry attempts before the Job is marked failed.parallelismandcompletionscontrol fan-out for batch workloads (e.g., process 100 items, 10 pods at a time).- CronJob’s
concurrencyPolicy(Allow/Forbid/Replace) matters in interviews — what happens if a scheduled run is still executing when the next one is due? Misconfiguring this is a real production bug class (duplicate report generation, overlapping migrations).
ConfigMaps
Externalizes non-sensitive configuration from container images, so the same image can run differently across environments.
apiVersion: v1
kind: ConfigMap
metadata:
name: payment-app-config
data:
LOG_LEVEL: "INFO"
FEATURE_FLAG_NEW_CHECKOUT: "true"
application.properties: |
server.port=8080
spring.datasource.max-pool-size=10
Consumed either as environment variables or mounted as files:
containers:
- name: payment-app
envFrom:
- configMapRef:
name: payment-app-config
volumeMounts:
- name: config-volume
mountPath: /app/config
volumes:
- name: config-volume
configMap:
name: payment-app-config
Key architectural point: volume-mounted ConfigMaps can update live (the file on disk changes when the ConfigMap is updated) without a pod restart — though the application must watch the file and reload; env-var-injected ConfigMaps require a pod restart to pick up changes, since env vars are fixed at container start.
Secrets
Structurally identical to ConfigMaps, but intended for sensitive data — API keys, passwords, TLS certs, tokens.
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
type: Opaque
data:
username: cG9zdGdyZXM= # base64-encoded, NOT encrypted
password: c3VwZXJzZWNyZXQ=
Critical architect-level caveat: Secret values are base64-encoded, not encrypted, by default. Anyone with get secret RBAC permission (or etcd access) can trivially decode them. Real protection requires:
- Encryption at rest enabled for etcd (
EncryptionConfigurationon the API server) — not on by default in many setups. - RBAC restricting who can read Secrets — this is the actual security boundary, not the encoding.
- For serious production use, integrating an external secrets manager (AWS Secrets Manager, HashiCorp Vault) via the External Secrets Operator, rather than relying on native Secrets as the source of truth.
Namespaces
A logical partition within a single cluster — scopes names, RBAC, resource quotas, and network policies.
apiVersion: v1
kind: Namespace
metadata:
name: production
kubectl apply -f deployment.yaml -n production
kubectl get pods -n staging
What namespaces give you:
- Name scoping —
payment-appinstagingandpayment-appinproductionare entirely separate objects. - RBAC boundary — Roles and RoleBindings are namespace-scoped (vs ClusterRole/ClusterRoleBinding, which are cluster-wide).
- ResourceQuota and LimitRange application — capping how much CPU/memory/object count a namespace (often = a team) can consume.
- NetworkPolicy scoping — default-deny rules typically defined per namespace.
Architect-level caveat, frequently tested in interviews: namespaces are not a security boundary by themselves. Without RBAC and NetworkPolicy explicitly layered on top, any pod in any namespace can, by default, talk to any pod in any other namespace, and namespace isolation provides no compute/network isolation guarantee on its own — it’s organizational scoping, not a sandbox.