How do you run a single application reliably in a cluster without manually logging into machines? Pods solve the problem of treating your application as a disposable unit that can be scheduled, restarted, and scaled. Understanding Pods is the first concrete step to deploying any workload in Kubernetes, and the CKAD exam will test your ability to create, inspect, and delete them under time pressure.
Jump to a section
A simple way to picture Pods and Basic Workload Management
Ever packed lunch for a road trip with a friend? You each bring your own snacks, but you share the same fold-out table in the boot of the car. That folding table is your shared space for eating sandwiches, but your personal snack bag stays in your own backpack. Now, what if you wanted to heat up a samosa? You'd ask your friend who has the boot space to plug in the portable heater near the table. Your heater needs its own power cord, and you only want it running while you're eating, not for the whole journey. That boot setup - the table, the heater power cord, and the window of time you're using them - that's a Pod. In Kubernetes, a Pod is the smallest deployable unit. It's a shared execution environment that holds one or more containers (your snack bags). Containers in the same Pod share the same network and storage, like sharing that fold-out table. They can talk to each other easily using 'localhost', just like you can hand a sandwich to your friend sitting opposite. The Pod also defines how the container runs: what resources it needs, which ports it opens (like the heater's power cord plugging into the car's outlet), and when it should restart if it fails. Crucially, a Pod has a short, temporary life - it's born, does its job, and it might die if it finishes or crashes. That life cycle is exactly what you need to manage when you schedule a Pod to run or update it. You don't try to run containers in isolation; you always think in terms of Pods.
Let's start with the most basic question: what is a container? A container is a lightweight, standalone, executable package of software that includes everything needed to run an application: code, runtime, system tools, libraries, and settings. Think of a container like a shipping container for your app - it seals everything inside so it runs the same way on any system. But in Kubernetes, you never run a container directly on a node (a worker machine in the cluster). Instead, you wrap it inside a Pod.
A Pod is the smallest and simplest Kubernetes object. It represents a single instance of a running process in your cluster. A Pod can contain one or more containers. The most common pattern is one container per Pod - this is called a single-container Pod. For example, if you run a simple web server, you create a Pod that wraps just that web server container. But you can also have multi-container Pods, where two or more containers work closely together, like a web server and a helper container that syncs files from a remote source. Both containers in the same Pod share the same network namespace - meaning they share the same IP address and port space, and can communicate over localhost. They also share the same storage volumes, so they can read and write common data.
Why does Kubernetes use Pods instead of just running containers? The key reason is resource sharing and lifecycle management. A Pod is the atomic unit of scheduling - when you tell Kubernetes to run a Pod, the scheduler finds a node with enough CPU and memory to fit the entire Pod. If the Pod has two containers, both containers must land on the same node. This is crucial because containers in a Pod are tightly coupled: they share the same network and can share storage. Without Pods, you'd have to manually ensure your helper container lands on the same machine as your web server, which is error-prone at scale.
Now, let's talk about the Pod lifecycle. A Pod has a status field that tells you where it is in its life:
Pending: The Pod has been accepted by the Kubernetes system but one or more of the container images have not been created yet. This includes time waiting for scheduling and downloading images over the network.
Running: The Pod has been bound to a node, and all of its containers have been created. At least one container is still running, or is in the process of starting or restarting.
Succeeded: All containers in the Pod have terminated successfully and will not be restarted. This is the normal end state for a batch job or a one-off task.
Failed: All containers in the Pod have terminated, and at least one container has terminated in failure - meaning it exited with a non-zero status code.
Unknown: For some reason the state of the Pod could not be obtained, typically due to an error in communicating with the node hosting the Pod.
To manage Pods, you write declarative YAML files. A simple Pod definition looks like this:
apiVersion: v1 kind: Pod metadata: name: my-first-pod spec: containers: - name: nginx-container image: nginx:latest
This tells Kubernetes: 'I want a Pod named my-first-pod that runs one container using the nginx:latest image from Docker Hub.' When you apply this file using kubectl apply -f pod.yaml, the API server (the control plane component that validates and processes requests) creates the Pod object. The scheduler then assigns it to a node, and the kubelet (the agent running on each node) pulls the image and starts the container.
Once a Pod is running, you can inspect it using kubectl get pods to see its status, kubectl describe pod <name> for detailed events and conditions, and kubectl logs <name> to see the container's stdout output. When you're done, you delete the Pod with kubectl delete pod <name>. Deleting a Pod sends a termination signal to the containers, allowing them to shut down gracefully.
A critical exam concept is that Pods are ephemeral - they are designed to be disposable. If a node fails, the Pods on it are not automatically recreated unless they are managed by a higher-level controller like a Deployment (which you'll learn later). In basic workload management, you create and delete individual Pods manually. This is useful for debugging, testing, running one-off tasks, or understanding the fundamentals before moving to controllers.
Define the Pod specification in YAML
Create a file (e.g., mypod.yaml) with the required fields: apiVersion (v1), kind (Pod), metadata.name, and spec.containers with at least a name and image. Optionally add command, args, ports, resources, and volumes. This declarative file tells Kubernetes exactly what you want.
Apply the Pod to the cluster
Use kubectl apply -f mypod.yaml to send the YAML to the API server. The API server validates the file and creates the Pod object in the cluster's etcd database. If there's a syntax error, kubectl will show an error message.
Check the Pod status
Run kubectl get pods to see a list of Pods with their status (Pending, Running, etc.). To dig deeper, use kubectl describe pod <name> to see events, conditions, node assignment, and container state. This step is crucial for troubleshooting delays like image pull failures.
Inspect container logs
If the Pod is running but not behaving as expected, run kubectl logs <pod-name> to see the stdout and stderr output from the container. For multi-container Pods, you need to specify the container with -c <container-name>. Logs help identify code errors or configuration issues.
Modify or delete the Pod
To change most Pod fields (e.g., container image, command), you must first delete the existing Pod with kubectl delete pod <name>, make changes to the YAML file, then re-apply. For a simple image update, you can use kubectl set image pod/<name> <container-name>=<new-image> if the Pod supports it (though deletion is safer). Deleting a Pod triggers a graceful shutdown with a timeout.
Verify the Pod is gone
After deletion, run kubectl get pods with a label selector or by name to confirm the Pod no longer exists. If you used --force --grace-period=0, the Pod may disappear instantly. This step ensures you don't leave orphaned resources consuming cluster capacity.
Imagine you are a platform engineer at a small e-commerce company called 'ShopSquirrel'. Your team has built a simple Python script that converts product descriptions from English to French using a small open-source model. This script needs to run every 12 hours because the product catalogue updates overnight. Your boss says: 'Just make it run on the Kubernetes cluster we already have.' You have zero budget for fancy job schedulers.
You start by packaging the Python script into a container image. You write a Dockerfile that copies the script and its dependencies, then push it to your company's private container registry. Now you need to run this on Kubernetes. Since this is a one-off task (not a long-running web server), you decide to use a bare Pod. You create a YAML file called translator-pod.yaml:
apiVersion: v1 kind: Pod metadata: name: product-translator spec: containers: - name: translator image: shopsquirrel/translator:v1 command: ["python"] args: ["translate.py", "--input", "/data/products.json", "--output", "/data/products-fr.json"] restartPolicy: Never
Notice the restartPolicy: Never - this tells Kubernetes not to automatically restart the container when it finishes. That's because your script should run exactly once and exit.
You apply the file: kubectl apply -f translator-pod.yaml. Immediately you check the status: kubectl get pods product-translator --watch. The Pod goes from Pending (waiting for scheduling) to ContainerCreating (kubelet is pulling the image) to Running (the script is executing). After 30 seconds, the status changes to Completed (Succeeded). You check the logs: kubectl logs product-translator. You see 'Translation complete. Output written to /data/products-fr.json'. Perfect.
But then you realise the output needs to persist, because the container's filesystem is wiped when the Pod is deleted. So you modify the YAML to attach a volume from a network filesystem. You add:
spec: volumes: - name: data-volume persistentVolumeClaim: claimName: shared-product-data containers: - name: translator volumeMounts: - name: data-volume mountPath: /data
Now the output file lives on shared storage even after the Pod dies. You delete the old Pod with kubectl delete pod product-translator and re-apply the new YAML.
Later, your script crashes because the input file format changed. The Pod shows status CrashLoopBackOff - meaning the container repeatedly starts and crashes, and Kubernetes is waiting longer each time before restarting (since restartPolicy defaults to Always). You fix the code, rebuild the container image with tag v2, update the YAML to use shopsquirrel/translator:v2, delete the old Pod, and re-apply. The new Pod starts successfully.
In real-world practice, you also need to handle:
Resource limits: setting CPU and memory requests/limits in the Pod spec so it doesn't hog the node.
Liveness and readiness probes: but that's for more advanced topics.
Cleaning up: always delete Pods you no longer need, or they clutter the cluster and waste resources.
This scenario shows the everyday pattern: write YAML, apply, check status, inspect logs, delete, and iterate. For CKAD, you must be able to do this quickly without reference material.
The CKAD exam loves testing your ability to create, inspect, and modify Pods under time pressure. Expect the following question types:
Given a YAML snippet with a syntax error (e.g., wrong indentation, missing field), fix it and apply the Pod.
Create a Pod from scratch using only imperative commands (kubectl run) without writing a YAML file.
Inspect a running Pod: find its IP address, node name, container image, or current status.
Modify a Pod's specification - but note: you cannot edit most fields of a running Pod directly (with few exceptions like container image). You must delete and re-create the Pod.
Debug a Pod that is stuck in Pending or CrashLoopBackOff: identify the issue from its events or logs.
The traps they set include:
RestartPolicy confusion: The default restartPolicy for a Pod is Always, which means if your container exits successfully, Kubernetes will restart it. This is fine for a web server but catastrophic for a batch job that should run once. If the exam asks for a one-off task, you must set restartPolicy: Never or OnFailure.
ImagePullPolicy defaults: If you don't specify imagePullPolicy, it defaults to Always if the image tag is 'latest', or IfNotPresent otherwise. This matters because if you push a new version of the image but keep the tag 'latest', old Pods already running will not pull the new image unless you delete and recreate the Pod or set imagePullPolicy: Always.
The difference between 'kubectl run' and 'kubectl create deployment': 'kubectl run podname --image=nginx' creates a Pod. 'kubectl create deployment' creates a Deployment (a higher-level controller that manages Pods). The exam tests bare Pods directly, so know the distinction.
Resource requests vs limits: When a Pod is pending because it cannot be scheduled, the most common cause is insufficient CPU or memory in the cluster. The exam might ask you to troubleshoot by examining 'kubectl describe pod' output showing '0/1 nodes are available: insufficient cpu'. You need to reduce the request or add more nodes.
The concept of 'grace period': When you delete a Pod, kubectl sends a SIGTERM and waits for a default grace period of 30 seconds before sending SIGKILL. The exam may test how to override this with --grace-period=0 --force.
Multi-container Pods: They may ask you to create a Pod with two containers that share a volume. You must specify both containers under spec.containers, and a shared volume under spec.volumes with matching volumeMounts.
Key definitions to memorise for the exam:
Pod status: Pending, Running, Succeeded, Failed, Unknown.
Container states: Waiting (with reason like ContainerCreating or CrashLoopBackOff), Running, Terminated.
restartPolicy options: Always (default), OnFailure, Never.
imagePullPolicy options: Always, IfNotPresent, Never.
Memorise the structure of a Pod YAML: apiVersion, kind, metadata, spec.containers[].name, image, imagePullPolicy, ports, resources, command, args, volumeMounts, and spec.volumes.
The exam is open-book but time is tight - you should be able to write a basic Pod YAML from memory in under two minutes.
A Pod is the smallest deployable unit in Kubernetes, wrapping one or more containers that share network and storage resources.
You cannot run a container directly on a Kubernetes cluster; every container must be inside a Pod.
The restartPolicy on a Pod controls automatic restarts: Always (default, restarts on any exit), OnFailure (restarts only on non-zero exit), Never (no restarts).
Most Pod spec fields are immutable after creation, so you must delete and recreate the Pod to change them.
kubectl run creates a Pod when you use the --restart=Never flag; otherwise it may create a Deployment.
A Pod's lifecycle status progresses through Pending, Running, Succeeded or Failed, but intermediate states like CrashLoopBackOff are not a formal Pod status but a container reason.
You troubleshoot Pod failures using kubectl describe pod for events and kubectl logs for container output.
Always set imagePullPolicy and resource requests/limits in production Pods to avoid unexpected behaviour.
These come up on the exam all the time. Here's how to tell them apart.
Pod
Smallest deployable unit in Kubernetes; a wrapper for one or more containers.
Manually created and deleted; no self-healing if the Pod crashes or node fails.
Used for one-off tasks, debugging, or when you need direct control over a single instance.
Deployment
A higher-level controller that manages ReplicaSets, which in turn manage Pods.
Automatically replaces failed Pods, rolls out updates, and scales replicas.
Used for long-running stateless applications like web servers where resilience is required.
Single-container Pod
Contains exactly one container; the most common pattern.
Simpler to manage and debug; fewer inter-container dependencies.
Logs and resources are tied to one process; no need for shared volumes between containers.
Multi-container Pod
Contains two or more containers that share network and storage.
Used for tightly coupled tasks like log collection, data synchronisation, or proxy sidecars.
Containers communicate over localhost; must be co-scheduled on the same node.
restartPolicy: Always
Default policy; Kubernetes restarts container regardless of exit code.
Used for long-running services like web servers that should always be up.
Pod status remains Running even after container exit and restart.
restartPolicy: Never
Container runs once and never restarts; exit code 0 leads to Pod status Succeeded.
Used for batch jobs, one-time tasks, or testing scripts.
Failed exit code sets Pod status to Failed; manual intervention needed to retry.
kubectl run podname --image=nginx
Creates a single Pod directly with no controller overhead.
Pod is not automatically replaced if it crashes or the node fails.
Best for quick testing, debugging, or temporary workloads.
kubectl create deployment mydep --image=nginx
Creates a Deployment object that manages ReplicaSets and Pods.
Automatically maintains desired replica count, handles rollouts and rollbacks.
Production pattern for stateless applications requiring resilience.
Mistake
A Pod and a container are the same thing, just different terminology.
Correct
A Pod is a wrapper around one or more containers. The Pod provides shared network and storage, and the container is the running process inside it. You cannot have a container without a Pod in Kubernetes.
Many beginners come from Docker, where you run containers directly. In Kubernetes, the Pod abstraction is fundamental, and the exam tests your understanding that the Pod is the atomic unit, not the container.
Mistake
You can edit any field in a running Pod using kubectl edit.
Correct
You can only edit a few fields (like container image) on a running Pod. Most fields (like command, args, ports) require you to delete and recreate the Pod. The exam frequently tests this with a trick question.
In imperative workflows, people expect to modify running resources directly. Kubernetes enforces immutability for Pod spec to ensure consistency, which feels unnatural at first.
Mistake
If a container in a Pod crashes, the Pod status becomes 'Failed' immediately.
Correct
If restartPolicy is Always (the default), Kubernetes will restart the container, so the Pod stays in 'Running' or 'CrashLoopBackOff' status. The Pod only becomes 'Failed' if restartPolicy is Never or OnFailure and the container exits with non-zero.
Beginners conflate container status with Pod status. The Pod status is a higher-level aggregation that considers the restart policy.
Mistake
A Pod with a successful exit (code 0) disappears automatically.
Correct
A Pod persists in the cluster even after its containers exit, with status 'Succeeded' or 'Completed'. You must manually delete it with kubectl delete pod. The Pod only disappears if it was created by a Job controller and the TTL controller cleans it up.
New users expect ephemeral behaviour, but Kubernetes retains the Pod object for inspection purposes, which can confuse when resources are not freed.
Mistake
kubectl run always creates a Pod.
Correct
kubectl run can create a Pod or a Deployment depending on the arguments and Kubernetes version. In newer versions, kubectl run podname --image=nginx creates a Pod, but if you omit --restart=Never or use generators, it might create a Deployment. Always use --restart=Never to guarantee a Pod.
The default behaviour of kubectl run changed over Kubernetes versions, and exam environments may use specific versions. This mismatch causes mistakes during time-pressured tests.
Reveal each answer, then mark whether you got it right. Score 60%+ to unlock the next chapter.
Yes, a Pod can contain multiple containers that share the same network namespace (they can talk via localhost) and share storage volumes. This is useful for tightly coupled helper containers like log shippers or sidecars, but for most workloads, a single container per Pod is best practice.
Pod status is a high-level aggregate of all containers, shown as Pending, Running, Succeeded, Failed, or Unknown. Container status is more detailed: Waiting (with a reason like ContainerCreating or CrashLoopBackOff), Running, or Terminated (with exit code).
The Pod has been accepted but not yet scheduled or the container image is not yet pulled. Use kubectl describe pod to see events. Common causes: no node with enough CPU/memory, insufficient persistent volume, or a node selector that matches no nodes.
Set restartPolicy: Never or OnFailure in your Pod spec so Kubernetes does not restart the container after it finishes. The Pod will show status 'Succeeded' or 'Completed' when the container exits with code 0.
Yes, you can use 'kubectl set image pod/<pod-name> <container-name>=<new-image>', or edit the Pod with 'kubectl edit pod <pod-name>' and change the image field. However, the Pod will restart to pick up the new image. For most other spec changes, you must delete and recreate.
Kubernetes sends a SIGTERM signal to the main process in each container and waits for a default grace period of 30 seconds. If the container doesn't exit, it sends SIGKILL. The Pod object is then removed from etcd and the cluster.
You've finished Pods and Basic Workload Management. Continue through the CKAD study guide to build a complete picture of the exam.
Done with this chapter?