01 - Kubernetes Core Concept - Pod
KubernetesPodReplicaSetDeploymentKubectlDevOpsTroubleshooting Beginner 7 min read

01 - Kubernetes Core Concept - Pod

Understand core Kubernetes building blocks Pod with real-world examples, YAML manifests, and hands-on troubleshooting scenarios.

Kubernetes Core Concepts — Pod

Before you can deploy real applications on Kubernetes, you need a solid understanding of the three most fundamental building blocks: Pods, ReplicaSets, and Deployments. These three objects are layered on top of each other — Deployments manage ReplicaSets, and ReplicaSets manage Pods. Understanding each layer individually will make you a much more effective Kubernetes engineer and will directly help you debug problems faster.

This guide covers what each resource is, why it exists, where it is used in real environments, a full YAML manifest breakdown, common real-world troubleshooting scenarios, and the exact kubectl commands used to resolve them.


1.1 What Is a Pod?

A Pod is the smallest and most basic deployable unit in Kubernetes. It is important to understand that Kubernetes does not run containers directly — it runs Pods, and each Pod contains one or more containers that share the same network namespace and storage.

Think of a Pod as a logical host. Just like how a virtual machine can run multiple processes, a Pod can run multiple containers that:

  • Share the same IP address and port space
  • Can communicate with each other via localhost
  • Can share mounted volumes (storage)
  • Are always scheduled together on the same node

In practice, most Pods run a single container. Multi-container Pods are used for specific patterns like sidecar logging, service mesh proxies (like Envoy in Istio), or init containers that do setup work before the main container starts.

┌─────────────────────────────────────────────┐
│                    POD                      │
│  IP: 10.244.1.15                            │
│                                             │
│  ┌──────────────┐   ┌──────────────────┐   │
│  │  Main App    │   │  Sidecar Logger  │   │
│  │  Container   │   │  Container       │   │
│  │  :8080       │   │  (reads logs)    │   │
│  └──────────────┘   └──────────────────┘   │
│                                             │
│  Shared Volume: /var/log/app                │
└─────────────────────────────────────────────┘

1.2 Why Is a Pod Used?

Kubernetes introduces the Pod abstraction for several important reasons:

ReasonExplanation
Co-locationTightly coupled containers (e.g., app + log shipper) need to run on the same node and share resources
Shared networkingContainers within a Pod communicate over localhost — no service discovery needed between them
Atomic schedulingKubernetes schedules an entire Pod, not individual containers — ensures all containers land on the same node
Lifecycle managementAll containers in a Pod start, stop, and restart together
Resource groupingCPU and memory limits can be set per container within the Pod

1.3 Where Are Pods Used?

Pods appear in virtually every Kubernetes workload:

  • Web application containers — your Node.js, Python Flask, or Java Spring Boot app
  • Database instances — a PostgreSQL or MySQL container (usually with a PersistentVolume)
  • Batch jobs — one-off data processing containers (via Job or CronJob resources)
  • System components — kube-dns, kube-proxy, metrics-server all run as Pods in the kube-system namespace
  • Sidecar patterns — Envoy proxy (Istio), Fluentd log shipper, Vault agent running alongside the main app

1.4 Pod YAML Manifest — Full Breakdown

apiVersion: v1          # Core API group for Pods
kind: Pod               # Resource type
metadata:
  name: nginx-pod                    # Unique name within the namespace
  namespace: default                 # Namespace this pod belongs to
  labels:
    app: nginx                       # Label for selectors and grouping
    env: production
    version: "1.25"
  annotations:
    description: "Frontend nginx pod for TechWithDB"
spec:
  containers:
    - name: nginx                    # Container name (must be unique within the pod)
      image: nginx:1.25              # Docker image with tag (always pin the tag!)
      ports:
        - containerPort: 80          # Port the container listens on (informational)
      resources:
        requests:                    # Minimum resources guaranteed to the container
          memory: "64Mi"
          cpu: "100m"                # 100 millicores = 0.1 CPU core
        limits:                      # Maximum resources the container can use
          memory: "128Mi"
          cpu: "250m"
      env:                           # Environment variables
        - name: NGINX_HOST
          value: "techwithdb.com"
        - name: LOG_LEVEL
          value: "info"
      livenessProbe:                 # Kubernetes restarts container if this fails
        httpGet:
          path: /healthz
          port: 80
        initialDelaySeconds: 10      # Wait 10s before first check
        periodSeconds: 15            # Check every 15s
      readinessProbe:                # Pod removed from service endpoints if this fails
        httpGet:
          path: /ready
          port: 80
        initialDelaySeconds: 5
        periodSeconds: 10
      volumeMounts:
        - name: config-volume
          mountPath: /etc/nginx/conf.d
  volumes:
    - name: config-volume            # Named volume — referenced by volumeMounts above
      configMap:
        name: nginx-config           # A ConfigMap that holds nginx config files
  restartPolicy: Always              # Always | OnFailure | Never
  nodeSelector:
    disk: ssd                        # Only schedule on nodes with this label

1.5 Common Pod Phases and Status

When you run kubectl get pods, the STATUS column can show various states. Here is what each means:

StatusMeaning
PendingPod accepted by cluster but not yet scheduled or image not yet pulled
RunningPod bound to a node and at least one container is running
SucceededAll containers exited with status 0 (common for Jobs)
FailedAll containers have stopped and at least one exited with non-zero status
CrashLoopBackOffContainer is crashing repeatedly — Kubernetes keeps restarting it with increasing delay
ImagePullBackOffKubernetes cannot pull the container image
OOMKilledContainer exceeded its memory limit and was killed by the OS
TerminatingPod is being deleted
ContainerCreatingContainer is being created (image pulling or volume mounting in progress)
Init:0/1Init container has not completed yet

1.6 Real-World Troubleshooting - Pod

Scenario 1: Pod is stuck in CrashLoopBackOff

Situation: You deploy an application pod and notice it shows CrashLoopBackOff. This is one of the most common issues you will face.

$ kubectl get pods
NAME                    READY   STATUS             RESTARTS   AGE
myapp-pod               0/1     CrashLoopBackOff   5          4m

Step 1 — Check pod events to understand why it crashed:

kubectl describe pod myapp-pod

Look at the Events section at the bottom of the output:

Events:
  Type     Reason     Age                  From               Message
  ----     ------     ----                 ----               -------
  Normal   Scheduled  5m                   default-scheduler  Successfully assigned default/myapp-pod to node1
  Normal   Pulled     5m                   kubelet            Successfully pulled image "myapp:latest"
  Normal   Created    5m                   kubelet            Created container myapp
  Normal   Started    5m                   kubelet            Started container myapp
  Warning  BackOff    3m (x5 over 4m)      kubelet            Back-off restarting failed container

Step 2 — Check the container logs:

# Current logs
kubectl logs myapp-pod

# Logs from the PREVIOUS crashed instance (most useful for CrashLoopBackOff)
kubectl logs myapp-pod --previous

Example log output revealing the issue:

Error: Cannot connect to database at db-service:5432
Connection refused: ECONNREFUSED
Application exiting with code 1

Root Cause: The application exits immediately if it cannot connect to the database on startup. The database service does not exist yet.

Step 3 — Fix the issue:

Option A — Create the missing database service first Option B — Add a startup retry mechanism in the application Option C — Add an init container to wait for the DB to be ready:

initContainers:
  - name: wait-for-db
    image: busybox
    command: ['sh', '-c', 'until nc -z db-service 5432; do echo waiting for db; sleep 3; done']

Scenario 2: Pod stuck in Pending state

$ kubectl get pods
NAME         READY   STATUS    RESTARTS   AGE
nginx-pod    0/1     Pending   0          8m

Step 1 — Describe the pod:

kubectl describe pod nginx-pod

Events section shows:

Warning  FailedScheduling  8m   default-scheduler
  0/1 nodes are available: 1 Insufficient memory. preemption: 0/1 nodes are available: 1 No preemption victims found for incoming pod.

Root Cause: The node does not have enough memory to satisfy the pod’s resource request.

Fix options:

# Option 1: Check current node resource usage
kubectl describe node minikube | grep -A 10 "Allocated resources"

# Option 2: Reduce the memory request in the pod manifest
# Change requests.memory from "512Mi" to "128Mi"

# Option 3: Check if there are resource quotas applied to the namespace
kubectl get resourcequota -n default
kubectl describe resourcequota -n default

Scenario 3: ImagePullBackOff

$ kubectl get pods
NAME        READY   STATUS             RESTARTS   AGE
bad-pod     0/1     ImagePullBackOff   0          2m
kubectl describe pod bad-pod

Events:

Warning  Failed  90s  kubelet  Failed to pull image "myapp:v2.5-typo":
  rpc error: code = Unknown desc = Error response from daemon:
  manifest for myapp:v2.5-typo not found: manifest unknown

Root Cause: The image tag v2.5-typo does not exist in the registry.

Fix:

# Edit the pod to fix the image tag
kubectl edit pod bad-pod
# Change image: myapp:v2.5-typo  to  image: myapp:v2.5

# Or delete and recreate with corrected YAML
kubectl delete pod bad-pod
kubectl apply -f corrected-pod.yaml

Note: You cannot update the image of a running pod directly in most cases — pods are largely immutable. This is exactly why Deployments (covered below) are the preferred way to manage pods.


Scenario 4: OOMKilled — Out of Memory

$ kubectl get pods
NAME        READY   STATUS      RESTARTS   AGE
myapp-pod   0/1     OOMKilled   3          10m
kubectl describe pod myapp-pod | grep -A 5 "Last State"

Output:

Last State:  Terminated
  Reason:    OOMKilled
  Exit Code: 137
  Started:   Mon, 20 Jan 2026 10:00:00 +0530
  Finished:  Mon, 20 Jan 2026 10:00:45 +0530

Root Cause: The container exceeded its memory limit (limits.memory) and was killed by the Linux OOM (Out Of Memory) killer.

Fix:

# Increase the memory limit in your manifest
resources:
  requests:
    memory: "256Mi"
  limits:
    memory: "512Mi"    # was 128Mi — application needs more memory
kubectl apply -f pod.yaml