Understanding Pods

Learn about Pods - the smallest deployable unit in Kubernetes. Create your first pod, understand its lifecycle, and view logs.

8 min read

Understanding Pods

In the Previous Tutorial, we set up Minikube and got our cluster running. Now it's time to meet the most fundamental concept in Kubernetes — the Pod.

A Pod is the smallest thing you can deploy in Kubernetes. Not a container — a Pod. Every container runs inside a Pod. Get comfortable with Pods because everything else in Kubernetes builds on top of them.

What is a Pod?

Hold on, I thought we were running containers. What's a Pod?

Great question! A Pod is a wrapper around one or more containers. Think of it like a cozy little apartment for your container to live in. The apartment provides:

  • Shared network: All containers in a Pod share the same IP address and port space. They can chat with each other via localhost — like roommates yelling across the hallway.
  • Shared storage: Containers in a Pod can share volumes (like a shared fridge).
  • Shared lifecycle: Containers in a Pod start and stop together. If the Pod goes down, all containers inside go down with it.

Why Not Just Containers?

Why does Kubernetes add this extra layer? Can't it just manage containers directly?

It could, but some applications need tightly coupled processes. Imagine a web server that needs a log shipper running alongside it, sharing the same files. Putting them in the same Pod makes total sense — they're like best friends who do everything together.

But most of the time? One container per Pod. The multi-container pattern is the exception, not the rule. So don't overthink it.

Create Your First Pod

Alright, enough theory. Let's deploy something! We'll spin up an nginx web server. First, make sure your Minikube cluster is running:

minikube status

All good? Let's go!

Method 1: Imperative Command

The quick-and-dirty way — great for testing, terrible for production:

kubectl run nginx --image=nginx:latest

Check if it's running:

kubectl get pods

Output:

NAME    READY   STATUS    RESTARTS   AGE
nginx   1/1     Running   0          30s

Your first Pod is alive! How cool is that?

Method 2: Declarative YAML (Recommended)

In the real world, Kubernetes uses YAML files. They're version-controlled, reviewable, and repeatable. Think of them as recipes — you write the recipe once, and anyone can follow it.

Create nginx-pod.yaml:

apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
  labels:
    app: nginx
spec:
  containers:
  - name: nginx
    image: nginx:latest
    ports:
    - containerPort: 80

That looks like a lot of boilerplate for just one container!

Yeah, YAML can be verbose. But let's break it down — it's simpler than it looks:

FieldDescription
apiVersion: v1Pods are part of the core API
kind: PodWe're creating a Pod
metadata.nameThe Pod's name (must be unique in the namespace)
metadata.labelsKey-value pairs for organizing and selecting Pods
spec.containersList of containers in this Pod
containers[].nameContainer name (unique within the Pod)
containers[].imageDocker image to use
containers[].portsPorts the container exposes

Apply it:

kubectl apply -f nginx-pod.yaml

Output:

pod/nginx-pod created

Inspect the Pod

Now let's put on our detective hat and see what's going on with our Pod.

Get Basic Info

kubectl get pods

Add -o wide for more details:

kubectl get pods -o wide

Output:

NAME        READY   STATUS    RESTARTS   AGE   IP           NODE       
nginx-pod   1/1     Running   0          1m    10.244.0.5   minikube   

Get Full Details

kubectl describe pod nginx-pod

This is the big one. It shows everything: events, conditions, container status, volumes, and more. When things go wrong (and they will), describe is your best friend. Treat it like the doctor's report for your Pod.

Key sections to look at:

Name:             nginx-pod
Namespace:        default
Status:           Running
IP:               10.244.0.5
Containers:
  nginx:
    Image:          nginx:latest
    State:          Running
    Ready:          True
Events:
  Type    Reason     Message
  ----    ------     -------
  Normal  Scheduled  Successfully assigned default/nginx-pod to minikube
  Normal  Pulled     Container image "nginx:latest" already present
  Normal  Created    Created container nginx
  Normal  Started    Started container nginx

View as YAML

See the full Pod specification with current status:

kubectl get pod nginx-pod -o yaml

Pod Lifecycle

Pods go through phases, kind of like us on a Monday morning:

PhaseDescription
PendingPod accepted but not yet running. Could be downloading the image or waiting for a node. (Still in bed.)
RunningPod bound to a node, all containers created, at least one running. (At work, doing stuff.)
SucceededAll containers terminated successfully (exit code 0). Won't restart. (Job done, going home.)
FailedAll containers terminated, at least one failed (non-zero exit). (Something went wrong...)
UnknownPod state cannot be determined. Usually a communication issue. (Lost signal. Hello? Anyone?)

Watch a Pod's status change in real-time:

kubectl get pods --watch

View Logs

Want to see what's happening inside the container? Logs are your window:

kubectl logs nginx-pod

Want to watch logs in real-time (like tail -f)? Add the -f flag:

kubectl logs -f nginx-pod

For Pods with multiple containers, specify which one:

kubectl logs nginx-pod -c nginx

Execute Commands in a Pod

Need to debug something inside the container? You can actually get a shell inside it — like SSH-ing into a server:

kubectl exec -it nginx-pod -- /bin/bash

What does -it mean?

-i stands for interactive (keeps stdin open) and -t allocates a terminal. Together they give you a usable shell. Without them, it'd be like trying to have a conversation through a mailbox.

# Inside the container
cat /etc/nginx/nginx.conf
curl localhost
exit

Run a single command without entering the shell:

kubectl exec nginx-pod -- cat /usr/share/nginx/html/index.html

Access the Pod

Pods have internal IP addresses, but they're not accessible from outside the cluster by default.

So how do I see my app in the browser?

For quick testing, use port forwarding. It's like creating a secret tunnel from your laptop to the Pod:

kubectl port-forward nginx-pod 8080:80

Now open http://localhost:8080 in your browser. Boom — you'll see the nginx welcome page! 🎉

Press Ctrl+C to stop port forwarding.

Pod Networking

Every Pod gets its own IP address. Containers within the same Pod share that IP — they communicate via localhost on different ports.

┌────────────────────────────────────┐
│            Pod (10.244.0.5)        │
│  ┌────────────┐  ┌────────────┐    │
│  │ Container A│  │ Container B│    │
│  │  :80       │  │  :8080     │    │
│  └────────────┘  └────────────┘    │
│         │              │           │
│         └──── localhost ───┘       │
└────────────────────────────────────┘

Container A can reach Container B at localhost:8080. From outside the Pod, you'd use 10.244.0.5:8080.

Delete a Pod

kubectl delete pod nginx-pod

Or delete using the YAML file:

kubectl delete -f nginx-pod.yaml

Wait, is the Pod gone forever?

Yep! Pods are like mayflies — they live, they die, they don't come back. When you delete a Pod, it's gone. Poof. That's exactly why you don't usually create Pods directly in production — you use Deployments (coming up in the next tutorial) to manage them and auto-resurrect them when they die.

Pod Restart Policies

Control what happens when a container exits:

apiVersion: v1
kind: Pod
metadata:
  name: restart-demo
spec:
  restartPolicy: Always  # Always | OnFailure | Never
  containers:
  - name: busybox
    image: busybox
    command: ["sh", "-c", "echo Hello && sleep 10"]
PolicyDescription
AlwaysAlways restart (default). Used for long-running services.
OnFailureRestart only if exit code is non-zero. Good for Jobs.
NeverDon't restart. Container runs once.

Multi-Container Pods

Remember how I said most Pods have just one container? Well, sometimes you need containers that work together like a buddy cop duo. Here's a common pattern:

Sidecar Pattern

A helper container that enhances the main container — like a sidekick:

apiVersion: v1
kind: Pod
metadata:
  name: web-with-logging
spec:
  containers:
  - name: web
    image: nginx
    volumeMounts:
    - name: logs
      mountPath: /var/log/nginx
  - name: log-shipper
    image: busybox
    command: ["sh", "-c", "tail -f /logs/access.log"]
    volumeMounts:
    - name: logs
      mountPath: /logs
  volumes:
  - name: logs
    emptyDir: {}

Both containers share the logs volume. The web server writes logs, the sidecar ships them somewhere. Teamwork makes the dream work!

Clean Up

Delete any Pods you created:

kubectl delete pod nginx --ignore-not-found
kubectl delete pod nginx-pod --ignore-not-found

What's Next?

Pods are the building blocks of Kubernetes — but in the real world, you never create them directly. Why? Because if a Pod dies, nobody brings it back. It's like hiring an employee with no backup plan.

In the next tutorial, you'll learn about Deployments — the smart manager that keeps your Pods running, handles scaling, and performs rolling updates without downtime. Let's go!