Pods
A Pod is the smallest deployable unit in Kubernetes — one or more containers sharing network + storage. Usually you don’t create them directly; Deployments / StatefulSets / DaemonSets create + manage them.
Spec, multi-container, init, lifecycle
EXAMPLE
# 1) Minimal Pod
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels: { app: web }
spec:
containers:
- name: nginx
image: nginx:1.27
ports:
- containerPort: 80
# kubectl apply -f nginx.yaml
# kubectl get pods
# kubectl logs nginx
# kubectl exec -it nginx -- sh
# kubectl describe pod nginx
# kubectl delete pod nginx
# 2) Container resources + probes
spec:
containers:
- name: web
image: myapp:1.0
ports: [{ containerPort: 3000 }]
env:
- name: NODE_ENV
value: production
- name: DB_URL
valueFrom:
secretKeyRef:
name: db-creds
key: url
resources:
requests: { cpu: 100m, memory: 128Mi } # SCHEDULER uses
limits: { cpu: 500m, memory: 512Mi } # KERNEL enforces
readinessProbe:
httpGet: { path: /health, port: 3000 }
periodSeconds: 5
initialDelaySeconds: 5
livenessProbe:
httpGet: { path: /health, port: 3000 }
periodSeconds: 30
initialDelaySeconds: 30
startupProbe: # for slow-starting apps
httpGet: { path: /health, port: 3000 }
periodSeconds: 5
failureThreshold: 30 # 150s grace
# Probe types:
# httpGet — request the path; 2xx-3xx = healthy
# tcpSocket — open a connection on port
# exec — run a command; exit 0 = healthy
# readiness → controls Service routing; failing pod removed from endpoint list
# liveness → controls restart; failing pod killed + restarted by kubelet
# startup → bypasses liveness during slow init; once succeeds, liveness takes over
# 3) Multi-container Pod — shared network + volumes
spec:
containers:
- name: app
image: myapp:1.0
ports: [{ containerPort: 3000 }]
volumeMounts:
- { name: shared, mountPath: /var/log/app }
- name: log-sidecar
image: fluent/fluent-bit:latest
volumeMounts:
- { name: shared, mountPath: /logs }
volumes:
- name: shared
emptyDir: {}
# Both containers share localhost networking; the volume shares files.
# Common sidecar patterns:
# - Logging (fluent-bit, vector, fluentd)
# - Metrics (node-exporter, prometheus-statsd)
# - Proxy (Envoy, Istio sidecar)
# - Auth (oauth2-proxy)
# - Init / config refresh
# 4) Init containers — run BEFORE main containers
spec:
initContainers:
- name: wait-for-db
image: alpine
command: ['sh', '-c', 'until nc -zv db 5432; do sleep 1; done']
- name: migrate
image: myapp:1.0
command: ['npm', 'run', 'migrate']
envFrom:
- secretRef: { name: db-creds }
containers:
- name: app
image: myapp:1.0
# Init containers run SEQUENTIALLY; main containers start after all succeed.
# Use for: waiting for dependencies, schema migrations, fetching config, permission setup.
# 5) Volumes — Pod-local storage
spec:
containers:
- name: app
image: myapp:1.0
volumeMounts:
- { name: data, mountPath: /data }
- { name: config, mountPath: /etc/app, readOnly: true }
volumes:
- name: data
persistentVolumeClaim: { claimName: app-data }
- name: config
configMap: { name: app-config }
- name: secret-files
secret:
secretName: app-secrets
defaultMode: 0400
- name: scratch
emptyDir: { sizeLimit: 1Gi }
- name: host-log
hostPath: { path: /var/log/host, type: DirectoryOrCreate }
# 6) Pod lifecycle phases
# Pending — accepted by API; containers not yet running
# Running — at least one container is running
# Succeeded — all containers exited successfully (Jobs)
# Failed — all containers exited; at least one failed
# Unknown — kubelet unreachable
kubectl get pods
# NAME READY STATUS RESTARTS AGE
# web-1 2/2 Running 0 5m
# web-2 1/2 CrashLoopBackOff 3 2m
# 7) Restart policies
spec:
restartPolicy: Always # Default for Deployments; restart on any exit
# OnFailure # restart only on non-zero exit; Jobs use this
# Never # don't restart; Pods used for batch
# 8) Pod security context
spec:
securityContext:
runAsUser: 1000
runAsGroup: 3000
fsGroup: 2000
runAsNonRoot: true
seccompProfile: { type: RuntimeDefault }
containers:
- name: app
image: myapp:1.0
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ['ALL']
# 9) Affinity + tolerations — control scheduling
spec:
nodeSelector:
disktype: ssd
affinity:
# Prefer pods on nodes with SSDs
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- { key: disktype, operator: In, values: [ssd] }
# Spread replicas across zones
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- { key: app, operator: In, values: [web] }
topologyKey: topology.kubernetes.io/zone
tolerations:
- key: node.kubernetes.io/unschedulable
operator: Exists
effect: NoSchedule
# 10) Topology spread (newer + simpler than anti-affinity)
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels: { app: web }
# Spreads replicas evenly across zones; refuses to schedule if it can't.
# 11) ImagePullSecrets — private registry auth
spec:
imagePullSecrets:
- name: ghcr-creds
# 12) Service account — Pod's identity for API access
spec:
serviceAccountName: app-sa
automountServiceAccountToken: false # disable if Pod doesn't need API access
# 13) Pod priority + preemption
spec:
priorityClassName: high-priority
# Higher-priority Pods can preempt lower-priority ones when nodes are full.
# 14) Termination grace period
spec:
terminationGracePeriodSeconds: 60 # default 30
# SIGTERM → wait → SIGKILL
# Set high enough for graceful drain (e.g. close DB connections, finish requests)
# 15) Pod debugging
kubectl get pods -o wide # node + IP
kubectl describe pod web-1 # events, conditions
kubectl logs web-1 -c app # specific container
kubectl logs web-1 --previous # logs from crashed previous instance
kubectl exec -it web-1 -c app -- sh
kubectl port-forward pod/web-1 8080:3000 # local debug
kubectl debug -it web-1 --image=busybox # ephemeral debug container (k8s 1.25+)
# 16) Common bugs
# ❌ Forgetting requests / limits → no scheduling guarantees; OOMKilled later
# ❌ Liveness probe too aggressive → restart loop on slow paths
# ❌ Readiness probe missing → traffic to unready Pods, 502s
# ❌ Forgetting startupProbe for slow-starting Java/Python apps
# ❌ Init containers running too long → ECS-like timeouts
# ❌ Privileged Pods (runAsUser: 0) → security risk
# ❌ Same Pod definition for prod + dev — diverge resource requests
# 17) When to use Pod directly vs higher-level resources
# Bare Pod : one-off debug, never in production
# Deployment : stateless app with N replicas
# StatefulSet : stateful (DB, cache cluster) with stable identity
# DaemonSet : one Pod per node (log collector, monitoring agent)
# Job : one-shot work that completes
# CronJob : periodic Job
# 18) Pod design principles
# • One process per container (12-factor)
# • Multi-container only for tightly-coupled sidecar pattern
# • Stateless preferred; PVC + StatefulSet for state
# • Resource requests + limits ALWAYS
# • Probes: readiness gates traffic; liveness restarts; startup for slow init
# • Security: non-root, read-only root FS, dropped capabilities
# • Graceful shutdown: handle SIGTERM, drain connections
# • Logs to stdout/stderr; metrics on /metrics; health on /health
Why it matters
Pods are the unit, but you almost never create them directly — let Deployments / StatefulSets manage them. Set requests + limits + all three probes (startup + readiness + liveness); the scheduler and traffic routing depend on them.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
apiVersion: v1
kind: Pod
metadata: { name: web }
spec:
containers:
- name: web
image: nginx:1.27
ports: [{ containerPort: 80 }]
Try it Yourself »
Exercise
Smallest deployable unit.
Three letters; PascalCase.
Discussion
Loading…