Intro
Kubernetes Pods are the smallest deployable units in a cluster, but treating them as simple wrappers around containers hides the advanced behaviors that determine whether workloads survive node failures, scale efficiently, and stay within resource budgets. This article moves past the basic kubectl run tutorial and explains Pod internals, architecture, and lifecycle patterns that operators and developers need for production-grade workloads.
You will learn how the kubelet manages Pods through the Pod lifecycle, how to control scheduling with affinity, taints, and topology spread constraints, and how to prevent outage cascades with resource limits, probes, and disruption budgets. Every section includes concrete manifests, commands, and expected output so you can validate these concepts in your own cluster.
We assume a running Kubernetes cluster (v1.24 or later) and kubectl configured with cluster-admin or namespace-level permissions. All examples use a dedicated namespace called pod-lab. Create it before proceeding:
kubectl create namespace pod-lab
Version and Environment Inventory
Before changing any production Pod, capture the cluster version, node operating system, container runtime, and the exact Pod spec that is running. This inventory is the foundation for safe troubleshooting and rollback.
Cluster and Node Information
Run the following read-only commands to establish your environment:
kubectl version --short
# Example output:
# Client Version: v1.25.3
# Server Version: v1.25.3
kubectl get nodes -o wide
# NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
# node-1 Ready control-plane 10d v1.25.3 192.168.1.10 <none> Ubuntu 22.04.1 LTS 5.15.0-52-generic containerd://1.6.9
# node-2 Ready <none> 10d v1.25.3 192.168.1.11 <none> Ubuntu 22.04.1 LTS 5.15.0-52-generic containerd://1.6.9
Note the container runtime (containerd, CRI-O, or Docker) and kernel version; they affect Pod security context settings and volume mount behavior.
Collecting Current Pod State
To inspect a running Pod without modifying anything:
kubectl get pod <pod-name> -n <namespace> -o yaml > pod-current.yaml
This YAML backup is your rollback reference. Pay special attention to the status block, which includes conditions like PodScheduled, Initialized, ContainersReady, and Ready. These conditions tell you where the Pod is in its lifecycle.
Example status excerpt:
status:
conditions:
- lastProbeTime: null
lastTransitionTime: "2023-05-01T12:00:00Z"
status: "True"
type: PodScheduled
- lastProbeTime: null
lastTransitionTime: "2023-05-01T12:00:10Z"
status: "True"
type: Initialized
- lastProbeTime: null
lastTransitionTime: "2023-05-01T12:00:20Z"
status: "True"
type: ContainersReady
- lastProbeTime: null
lastTransitionTime: "2023-05-01T12:00:25Z"
status: "True"
type: Ready
Each condition has a lastTransitionTime and reason field that aids incident timelines.
Safe Configuration Path
Advanced Pod configuration should follow a progression from local validation to production rollout. This reduces the risk of a misconfigured Pod causing an outage.
Start with a Minimal Pod Manifest
Create a baseline Pod manifest that only includes the container image and a label:
# baseline-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: baseline-pod
namespace: pod-lab
labels:
app: baseline
spec:
containers:
- name: nginx
image: nginx:1.25-alpine
ports:
- containerPort: 80
Apply it and verify:
kubectl apply -f baseline-pod.yaml
kubectl get pod baseline-pod -n pod-lab
# NAME READY STATUS RESTARTS AGE
# baseline-pod 1/1 Running 0 10s
Add Resource Requests and Limits
Resource requests guarantee a minimum amount of CPU and memory, while limits cap the maximum. Without them, the kubelet cannot make scheduling decisions fairly, and a noisy neighbor can starve other Pods.
Update the manifest with resource specifications:
spec:
containers:
- name: nginx
image: nginx:1.25-alpine
resources:
requests:
cpu: "100m" # 0.1 CPU
memory: "128Mi" # 128 MiB
limits:
cpu: "500m" # 0.5 CPU
memory: "256Mi" # 256 MiB
Apply and confirm the QoS class is set to Burstable (because requests < limits):
kubectl apply -f baseline-pod.yaml
kubectl get pod baseline-pod -n pod-lab -o jsonpath='{.status.qosClass}'
# Burstable
For latency-critical workloads, set requests equal to limits to achieve the Guaranteed QoS class, which gives the Pod the highest eviction priority during node pressure.
Configure Probes for Health and Readiness
Probes are essential for advanced Pod operation. Without them, Kubernetes cannot detect a hung application or stop traffic to a not-yet-ready Pod.
Add liveness and readiness probes to the nginx container:
livenessProbe:
httpGet:
path: /healthz
port: 80
initialDelaySeconds: 10
periodSeconds: 5
timeoutSeconds: 2
failureThreshold: 3
readinessProbe:
httpGet:
path: /healthz
port: 80
initialDelaySeconds: 5
periodSeconds: 3
timeoutSeconds: 2
successThreshold: 1
failureThreshold: 3
In this example, the liveness probe will restart the container if /healthz returns a non-200 status three consecutive times after the initial delay. The readiness probe will mark the Pod as not ready if the health check fails, causing the Service to stop sending traffic to it.
Apply and watch the probes in action:
kubectl apply -f baseline-pod.yaml
kubectl describe pod baseline-pod -n pod-lab | grep -A5 'Liveness\|Readiness'
# Liveness: http-get http://:80/healthz delay=10s timeout=2s period=5s #success=1 #failure=3
# Readiness: http-get http://:80/healthz delay=5s timeout=2s period=3s #success=1 #failure=3
Verification and Diagnostics
When a Pod is misbehaving, systematic verification and diagnostics are more effective than guesswork. Start with high-level status, then drill into events, logs, and container internals.
High-Level Status Check
Get the Pod status and recent events:
kubectl get pod <pod-name> -n pod-lab -o wide
kubectl describe pod <pod-name> -n pod-lab
The describe output includes a section like:
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 2m default-scheduler Successfully assigned pod-lab/baseline-pod to node-2
Normal Pulling 2m kubelet Pulling image "nginx:1.25-alpine"
Normal Pulled 119s kubelet Successfully pulled image "nginx:1.25-alpine" in 1.2s
Normal Created 119s kubelet Created container nginx
Normal Started 119s kubelet Started container nginx
If the Pod is stuck in Pending, look for FailedScheduling events indicating resource shortages or taints. If it's CrashLoopBackOff, check the Last State and exit code.
Check Container Logs
For a crashing container, get the previous instance's logs:
kubectl logs <pod-name> -n pod-lab --previous
If the container writes logs to files instead of stdout/stderr, use kubectl exec to inspect them:
kubectl exec -it <pod-name> -n pod-lab -- cat /var/log/app.log
Debug with an Ephemeral Container
For advanced diagnostics on a running Pod that lacks debugging tools, use ephemeral containers (Kubernetes v1.23+):
kubectl debug -it <pod-name> -n pod-lab --image=busybox --target=<container-name>
This attaches a new container to the Pod's network and PID namespace (if sharing is enabled), allowing you to run nslookup, curl, or strace without restarting the original container.
Example:
kubectl debug -it baseline-pod -n pod-lab --image=busybox --target=nginx
# Inside the ephemeral container:
/ # wget -qO- http://localhost/healthz
<!DOCTYPE html>...
Failure Modes and Recovery
Pod failures fall into predictable categories: scheduling failures, image pull errors, crash loops, probe failures, and eviction. Understanding each failure mode enables quick recovery and prevention.
Scheduling Failures
If a Pod remains Pending, check for taints on nodes and whether the Pod tolerates them. List taints:
kubectl get nodes -o json | jq '.items[].spec.taints'
# [{"effect":"NoSchedule","key":"node-role.kubernetes.io/control-plane"}]
If a node is tainted with NoSchedule, your Pod must have a matching toleration. Add one to the Pod spec:
tolerations:
- key: "node-role.kubernetes.io/control-plane"
operator: "Exists"
effect: "NoSchedule"
Also verify that resource requests fit on any node. Use kubectl describe nodes to see allocated resources.
Image Pull Errors
If the Pod status is ImagePullBackOff or ErrImagePull, check the event message:
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 10m default-scheduler Successfully assigned...
Warning Failed 10m kubelet Failed to pull image "nginx:1.25-alpine": rpc error: code = NotFound desc = failed to pull and unpack image
Common causes: incorrect image tag, missing imagePullSecret for a private registry, or registry unavailability. Fix the image reference or add a secret:
kubectl create secret docker-registry regcred \
--docker-server=myregistry.example.com \
--docker-username=myuser \
--docker-password=mypassword \
[email protected] \
-n pod-lab
Then reference it in the Pod spec:
imagePullSecrets:
- name: regcred
CrashLoopBackOff
A CrashLoopBackOff means the container starts then exits with a non-zero code. Inspect the exit code and logs:
kubectl get pod <pod-name> -n pod-lab -o jsonpath='{.status.containerStatuses[0].lastState.terminated.exitCode}'
# 1
kubectl logs <pod-name> -n pod-lab --previous
Common causes: application misconfiguration, missing dependencies, or a failed startup script. Fix the underlying issue and apply the updated manifest.
Probe Failure Recovery
If a readiness probe fails, the Pod status will show Running but READY 0/1. Traffic via a Service will be stopped. Check probe configuration and endpoint health:
kubectl get endpoints <service-name> -n pod-lab
# NAME ENDPOINTS AGE
# my-service 192.168.1.12:80 5m
If no endpoints are listed, the readiness probe is failing. Adjust the probe path, port, or thresholds.
Eviction and Pod Disruption
Node pressure or preemption can evict Pods. Use a PodDisruptionBudget (PDB) to limit voluntary disruptions during maintenance. Example PDB for a Deployment:
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: myapp-pdb
namespace: pod-lab
spec:
minAvailable: 2
selector:
matchLabels:
app: myapp
This ensures at least two replicas remain available during voluntary disruptions like kubectl drain.
Operations Checklist
Use this checklist before and after any advanced Pod operation to ensure you have covered observation, safety, and verification steps.
Before Making Changes
- [ ] Capture current Pod YAML with
kubectl get pod <name> -o yaml > backup.yaml. - [ ] Record the cluster version and node OS/runtime (
kubectl version,kubectl get nodes -o wide). - [ ] Verify the Pod's current status, QoS class, and resource requests/limits.
- [ ] Check for any PDB affecting the Pod's controller.
- [ ] Confirm the target namespace and context (
kubectl config current-context). - [ ] If modifying a Deployment, record the current rollout revision (
kubectl rollout history deployment/<name>).
During the Change
- [ ] Apply one change at a time to isolate effects.
- [ ] Use
--dry-run=serverto validate manifest syntax and admission control before applying:
kubectl apply -f pod-change.yaml --dry-run=server
- [ ] Trigger the change and immediately watch the rollout status:
kubectl apply -f pod-change.yaml
kubectl get pods -n pod-lab -w
After the Change
- [ ] Verify the Pod reaches
Readystate within expected time. - [ ] Check events for warnings (
kubectl describe pod <name>). - [ ] Run application-level smoke tests (e.g.,
curlthrough a Service). - [ ] Confirm no unintended evictions or restarts occurred (
kubectl get pod <name> -o jsonpath='{.status.containerStatuses[0].restartCount}'). - [ ] Update documentation with the change and rollback plan (e.g.,
kubectl apply -f backup.yaml).
Conclusion
Advanced Pod management requires moving beyond basic creation to understand the full lifecycle and the control knobs Kubernetes provides. By capturing environment inventory, following a safe configuration path, systematically verifying and diagnosing issues, and preparing for failure modes, you can operate Pods with confidence in production.
Start with one concept from this article—for example, adding resource limits to a critical Pod or configuring readiness probes—and validate it in a test namespace. Observe the effects, document the expected behavior, and then expand to broader workloads. The commands and manifests shown here are your starting point for building robust, self-healing applications on Kubernetes.
The next time a Pod fails, you will have a structured process to find the root cause quickly and recover without guesswork. Keep this checklist handy, and revisit it after every incident to refine your operational playbook.