Stage 5 · Platform
Workloads & Scheduling
Pod Lifecycle
Phases, conditions, probes, init containers, and ephemeral containers.
Pod Phases
A Pod is the smallest deployable unit in Kubernetes. It wraps one or more containers with shared networking and storage. Pods move through phases: Pending, Running, Succeeded, Failed, and Unknown. Understanding these phases is essential for debugging stuck or crashing workloads.
| Phase | Meaning | Common Cause |
|---|---|---|
| Pending | Accepted but not running | Scheduling failure, image pull |
| Running | At least one container running | Normal state |
| Succeeded | All containers exited 0 | Job completed |
| Failed | At least one container exited non-zero | Application crash |
| Unknown | Cannot get pod status | Node communication failure |
Pod Conditions
Pod conditions describe the current state of a pod in more detail. Key conditions include PodScheduled, Initialized, ContainersReady, and Ready. A pod is Ready only when all containers are running and readiness probes pass.
status:
phase: Running
conditions:
- type: PodScheduled
status: "True"
lastTransitionTime: "2024-01-15T10:30:00Z"
- type: Initialized
status: "True"
lastTransitionTime: "2024-01-15T10:30:05Z"
- type: ContainersReady
status: "True"
lastTransitionTime: "2024-01-15T10:30:15Z"
- type: Ready
status: "True"
lastTransitionTime: "2024-01-15T10:30:15Z"
containerStatuses:
- name: nginx
ready: true
restartCount: 0
image: nginx:1.25
state:
running:
startedAt: "2024-01-15T10:30:10Z"The Ready condition is what matters for Services. A pod only receives traffic when its Ready condition is True. This is determined by the readiness probe, not just whether the container process is running.
Liveness, Readiness, Startup Probes
Probes check container health. Liveness probes detect deadlocks — if they fail, kubelet restarts the container. Readiness probes determine if the pod receives traffic. Startup probes run once during container startup — if they fail, the container is killed and restarted.
apiVersion: v1
kind: Pod
metadata:
name: app-with-probes
spec:
containers:
- name: app
image: my-app:1.0
startupProbe:
httpGet:
path: /healthz
port: 8080
failureThreshold: 30
periodSeconds: 2
# 30 * 2s = 60s max startup time
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
failureThreshold: 3
# Restarts if unhealthy for 30s
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 0
periodSeconds: 5
failureThreshold: 2
# Removed from service after 10s of unreadyStartup probes prevent slow-starting apps from being killed by liveness probes. While startupProbe is running, liveness and readiness probes are disabled. Once startup succeeds, liveness and readiness take over.
If your app takes 30 seconds to start, don't set a 5-second liveness probe. Use a startup probe with failureThreshold * periodSeconds >= startup time. This gives the app enough time to initialize without being killed.
Init Containers
Init containers run before the main container starts. They execute sequentially — each must complete before the next one starts. Use them for setup tasks: waiting for a dependency, generating configuration, or running migrations.
apiVersion: v1
kind: Pod
metadata:
name: app-with-init
spec:
initContainers:
- name: wait-for-db
image: busybox:1.36
command: ["sh", "-c", "until nc -z postgres-service 5432; do sleep 2; done"]
resources:
limits:
memory: "32Mi"
cpu: "100m"
- name: run-migrations
image: my-app:1.0
command: ["./migrate", "up"]
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: db-credentials
key: url
containers:
- name: app
image: my-app:1.0Init containers run to completion before app containers start. If an init container fails (non-zero exit), kubelet retries based on the restart policy. The pod remains in Init phase until all init containers succeed.
Ephemeral Containers
Ephemeral containers are debugging containers you can add to a running pod. They don't have probes, ports, or lifecycle hooks. Use them for live debugging — attaching to a running pod without restarting it.
# Add a debugging container to a running pod
kubectl debug -it my-pod --image=busybox:1.36 --target=app
# Debug using the node's process namespace
kubectl debug node/worker-1 -it --image=busybox:1.36
# List existing ephemeral containers
kubectl get pod my-pod -o jsonpath='{.spec.ephemeralContainers}'The --target flag shares the process namespace with the target container, letting you see its processes. The debug node command creates a pod on the node with host filesystem access.
Restart Policies
Restart policies control whether and how containers restart after failure. Always (default) restarts on any exit. OnFailure restarts only on non-zero exit. Never never restarts. StatefulSets require Always; Jobs use OnFailure or Never.
apiVersion: v1
kind: Pod
metadata:
name: one-shot-task
spec:
restartPolicy: Never
containers:
- name: task
image: my-task:1.0
command: ["python", "process.py"]
env:
- name: RETRY_COUNT
value: "3"With restartPolicy: Never, a failed pod stays in Failed state. Use this for batch jobs where you want to see the failure and decide manually. With OnFailure, kubelet restarts the container in the same pod, preserving the pod IP and volumes.
Kubernetes increases the delay between restarts: 10s, 20s, 40s, ... up to 5 minutes. After a container runs for 10 minutes without failing, the backoff resets. This prevents tight restart loops from overwhelming the node.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.