Pod: The Smallest Unit in Kubernetes
Newcomers often think the smallest unit Kubernetes manages is the container. Not quite — it's the Pod. Getting the pod right from the start saves you confusion later, because almost everything in Kubernetes (Deployment, Service, Job...) ultimately reduces to "how to run a pod".
What is a Pod
A Pod is a group of one or more containers running together, sharing:
- The same network namespace — the containers in a pod share one IP address and call each other over
localhost. - Possibly a shared volume — accessing the same storage area.
- The same lifecycle — scheduled together onto one node, born and gone together.
┌──────────── POD (1 IP: 10.244.0.5) ────────────┐
│ │
│ [main container] [helper container (sidecar)] │
│ │ same localhost, same volume │ │
│ └──────────────┬────────────────┘ │
└────────────────────────┼────────────────────────┘
▼
runs on one node
Most pods have just one container — that's the common case. But the "multiple containers in one pod" model is useful for the sidecar pattern: for example one container runs the app, a helper container ships logs or acts as a proxy, and the two need to share localhost and disk. The key point: containers in the same pod are as intimate as two processes on the same machine.
A pod is ephemeral. It's not "durable". When a pod dies or a node fails, that pod does not come back to life — a new pod (different IP, different name) replaces it if someone manages it. This idea leads straight to Deployment in Article 4.
Write a pod in YAML
The kubectl run command in Article 2 is handy for quick tests, but the right way to work with Kubernetes is to declare it in YAML then apply. YAML is the source of truth, can be stored in Git, and is repeatable.
apiVersion: v1
kind: Pod
metadata:
name: nginx-pod
labels:
app: web
spec:
containers:
- name: nginx
image: nginx:1.27-alpine
ports:
- containerPort: 80
These four fields appear in every Kubernetes object, so get familiar early:
apiVersion— the API version of the object's kind (Pod belongs to the core groupv1).kind— the object kind (Pod).metadata— name, labels (tags for labeling and selecting — Article 6), namespace...spec— describes the desired state. For a pod: which container to run, which image, which port to open.
Notice we don't say "create a container" — we describe "this pod consists of an nginx container listening on port 80". The declarative mindset (Article 0) is present in every manifest.
apply and check state
kubectl apply -f pod.yaml
pod/nginx-pod created
kubectl get pod nginx-pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE
nginx-pod 1/1 Running 0 12s 10.244.0.5 minikube <none>
READY 1/1 (1 of 1 container ready), STATUS Running, internal IP 10.244.0.5. For more detail — and especially what happened — use describe:
kubectl describe pod nginx-pod
The Events section at the bottom is gold when troubleshooting; it recounts exactly the flow from Article 1:
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 12s default-scheduler Successfully assigned default/nginx-pod to minikube
Normal Pulling 11s kubelet Pulling image "nginx:1.27-alpine"
Normal Pulled 1s kubelet Successfully pulled image ... in 9.648s
Normal Created 1s kubelet Container created
Normal Started 1s kubelet Container started
Read top to bottom: the scheduler assigns the pod to node minikube, then the kubelet pulls the image, creates and starts the container. Exactly what Article 1 described, now seen with your own eyes. When a pod is stuck in Pending or ImagePullBackOff, this is the first place you look.
Read logs and exec into the pod
View the container's logs (like docker logs):
kubectl logs nginx-pod
Run a command inside the pod (like docker exec):
kubectl exec nginx-pod -- nginx -v
nginx version: nginx/1.27.5
Add -it for an interactive shell: kubectl exec -it nginx-pod -- sh. The logs and exec pair are the two you'll use most when troubleshooting (we dig in at Article 13).
Reaching the pod: port-forward
The pod has IP 10.244.0.5, but that's an internal cluster IP — your machine can't call it directly. The quick way to test during dev is port-forward:
kubectl port-forward pod/nginx-pod 8080:80
Now curl http://localhost:8080 on your machine is forwarded into port 80 of the pod:
<title>Welcome to nginx!</title>
port-forward is only suitable for testing/troubleshooting. The right way to expose a service externally in a stable way is a Service (Article 5) and Ingress (Article 9).
Why nobody runs bare pods in production
This is the most important lesson of the article. Delete the pod:
kubectl delete pod nginx-pod
kubectl get pods
pod "nginx-pod" deleted from default namespace
No resources found in default namespace.
The pod is gone — and nothing rebuilds it. No one is there holding "this pod must always exist". If this were production and the pod died because the node failed, your application would simply vanish. A pod on its own has no self-healing, no scaling, no rollout.
That's why in practice almost no one creates a Pod directly. Instead we use a higher-level object that manages pods for us — the most common being the Deployment. It's a controller (remember the control loop in Articles 0–1) that ensures "there are always exactly N pods running", rebuilds them when they die, and also handles version updates.
Wrap-up
The Pod is the smallest unit Kubernetes manages: a group of containers sharing IP/network, volume and lifecycle — most have just one container, sometimes with a sidecar. Declare a pod in YAML with the four fields apiVersion/kind/metadata/spec, then kubectl apply. Use get/describe (look at Events), logs, exec, port-forward to observe and troubleshoot. The crucial point: a pod is ephemeral and not self-healing — delete it and it's gone for good — so production doesn't run bare pods but lets a Deployment manage them.
Article 4 meets the Deployment and ReplicaSet: how to keep N copies always alive, scale up/down, and update versions without disruption.