Kubernetes Interview Troubleshooting Lab

Deploy and debug a Kubernetes app to ace interview scenarios.

Introduction

30 Second Summary

Kubernetes interviews test one thing above all else: can you debug a broken cluster under pressure? Muscle memory from actually fixing failures is what separates confident answers from fumbling.

In this project, you will deploy a web application on a local Kubernetes cluster. Then you will deliberately break it in the exact ways interviewers ask about, and fix each failure using production-grade debugging commands.

What You'll Build

A multi-replica nginx application running on your local Kubernetes cluster, complete with configuration injection, health probes, service networking, and five deliberate failure scenarios you will diagnose and fix.

By the end of this project, you'll have:

  • A multi-component application deployed locally with Deployments, ConfigMaps, Services, and health probes.
  • Hands-on experience diagnosing and fixing the top 5 failure modes interviewers test, including CrashLoopBackOff, bad rollouts, and selector mismatches.
  • A personal reference of kubectl commands and YAML patterns for rapid interview recall.
  • Secret Mission: Add resource requests and limits, trigger an OOMKilled pod, and diagnose it using exit codes.

Are there any prerequisites?

This project requires Docker Desktop installed on Windows. You do not need prior Kubernetes experience. Step 1 walks you through enabling Kubernetes inside Docker Desktop.

Before We Start

Before We Start...

Enable Kubernetes and Verify Your Cluster

Every Kubernetes interview question assumes you can spin up a cluster and navigate it confidently. Before you deploy anything, you need a working cluster and a connection to its API.

Docker Desktop ships with a built-in single-node Kubernetes cluster. Enabling it gives you a full control plane and worker node on your machine with zero extra installs.

In this step, get ready to:
  • Enable Kubernetes inside Docker Desktop settings.
  • Verify kubectl can reach the cluster.
  • Create an isolated namespace for your lab.
Enable Kubernetes in Docker Desktop

What does Docker Desktop Kubernetes give you?

Docker Desktop runs a single-node Kubernetes cluster inside containers on your machine. This gives you a full control plane (API server, etcd, scheduler, controller manager) and a worker node (kubelet, kube-proxy) all in one.

You get the same kubectl experience as a multi-node production cluster, without needing cloud resources or extra tools.

  • Press the Windows key to open your search bar.
  • Type Docker Desktop and press Enter to open it.
  • Click the gear icon in the top-right corner to open Settings.
  • Select the Kubernetes tab in the left sidebar.
  • Check the box labelled Enable Kubernetes.
  • Click Apply & Restart.

Docker Desktop will take a minute or two to download and start the Kubernetes components. Wait until you see a green light beside Kubernetes running in the Settings panel.

Don't see the Kubernetes tab?

The Kubernetes tab is not available when Docker Desktop is in Windows container mode. Right-click the Docker icon in your system tray and select Switch to Linux containers if you see that option.

If the green light does not appear after a few minutes, try restarting Docker Desktop entirely. Help me troubleshoot my Docker Desktop Kubernetes setup.

Verify kubectl and cluster connectivity
  • Press the Windows key, type PowerShell, and press Enter to open a PowerShell window.
  • Point kubectl at your Docker Desktop cluster by running this command:
kubectl config use-context docker-desktop

You should see Switched to context "docker-desktop". in the output.

  • Confirm the API server is reachable by running:
kubectl cluster-info

You should see a line showing the Kubernetes control plane address, something like https://127.0.0.1:6443.

  • Check that your node is in a healthy state by running:
kubectl get nodes

You should see one node named docker-desktop with a status of Ready.

What is the API server?

The API server is the front door to all cluster operations. Every kubectl command you run is translated into an API call that hits this server.

If kubectl cluster-info returns an address, your local machine can talk to the cluster. Everything else in this project builds on that connection.

Seeing 'connection refused' or 'kubectl not recognized'?

If PowerShell does not recognize kubectl, close and reopen PowerShell. Docker Desktop adds kubectl to your PATH automatically, but a fresh terminal session is needed to pick it up.

If you see a connection error, make sure the green Kubernetes running light is showing in Docker Desktop Settings. The cluster may still be starting up.

Help me fix my kubectl connection to Docker Desktop Kubernetes.

Create a project namespace

What is a namespace?

Namespaces are logical boundaries for resources inside a Kubernetes cluster. They keep your lab isolated from system components running in the default and kube-system namespaces.

When you are done, you can delete the entire namespace and everything inside it disappears. This makes cleanup effortless.

  • Create a namespace called k8s-lab by running:
kubectl create namespace k8s-lab

You should see namespace/k8s-lab created in the output.

  • Set k8s-lab as your default namespace so every command targets it automatically:
kubectl config set-context --current --namespace=k8s-lab

You should see Context "docker-desktop" modified. in the output.

  • Verify the namespace is active and set as your current context by running these two commands:
kubectl get namespace k8s-lab
kubectl config view --minify

The first command should show k8s-lab with a status of Active. The second command should show namespace: k8s-lab under your current context.

Namespace not showing as active?

If kubectl get namespace k8s-lab returns an error, make sure you typed the name exactly as k8s-lab (with a hyphen, not an underscore).

If the namespace exists but kubectl config view --minify does not show it, re-run the set-context command above. Help me set my default namespace in kubectl.

Your cluster is running and your lab namespace is ready. Next up, you will deploy your first application using Deployments, ReplicaSets, and Pods.

Deploy Your Application

Your cluster is online and your namespace is ready. Now it is time to put something on it.

In Kubernetes, you never create individual containers (called Pods) by hand. Instead, you declare a Deployment that manages everything for you. The Deployment creates a ReplicaSet, and the ReplicaSet creates the Pods.

In this step, get ready to:
  • Create a Deployment manifest and apply it to your cluster.
  • Explore the Deployment, ReplicaSet, and Pod hierarchy.
  • Scale the Deployment up and back down.
Create a Deployment manifest and apply it

A Deployment manifest is a YAML file that tells Kubernetes what you want running. You describe the desired state (image, replica count, labels) and Kubernetes makes it happen.

  • Navigate to your Desktop by running this command in PowerShell:
cd ~/Desktop
  • Create a project folder and move into it by running these commands:
mkdir k8s-lab-files
cd k8s-lab-files
  • Create the deployment.yaml file by running:
New-Item -ItemType File -Name deployment.yaml
  • Open deployment.yaml in Notepad by running:
notepad deployment.yaml
  • Paste the following YAML into the file:
# Deployment manifest for a web application
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
  namespace: k8s-lab
  labels:
    app: web-app
spec:
  # Run 3 identical copies of the container
  replicas: 3
  # Match pods with the label app: web-app
  selector:
    matchLabels:
      app: web-app
  # Rolling update strategy for zero-downtime deployments
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
  template:
    metadata:
      labels:
        app: web-app
    spec:
      containers:
        - name: nginx
          image: nginx:1.30.2
          ports:
            - containerPort: 80

What does this manifest do?

The metadata section names your Deployment web-app and places it in the k8s-lab namespace.

The spec.replicas: 3 field tells Kubernetes to maintain exactly 3 running copies of this container at all times. The selector.matchLabels ties the Deployment to any Pod carrying the label app: web-app.

The strategy section controls how updates roll out. maxSurge: 1 allows one extra Pod during an update. maxUnavailable: 1 allows one Pod to be down at a time.

The template section is the Pod blueprint. It runs the nginx:1.30.2 image and exposes port 80.

  • Save the file in Notepad with Ctrl+S and close it.

✔️ Awesome, I've got everything!

Great. Make sure you have saved deployment.yaml before continuing.

ⓧ I'd like to double check the full code

Here is the complete deployment.yaml file. Make sure yours matches exactly (comments are optional):

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
  namespace: k8s-lab
  labels:
    app: web-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web-app
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
  template:
    metadata:
      labels:
        app: web-app
    spec:
      containers:
        - name: nginx
          image: nginx:1.30.2
          ports:
            - containerPort: 80
  • Apply the manifest to your cluster by running:
kubectl apply -f deployment.yaml
  • Confirm the Deployment was created by running:
kubectl get deployment web-app

You should see web-app listed with 3/3 under the READY column. It may take a few seconds for all 3 replicas to start.

Seeing 0/3 READY or an error?

  • Check that your YAML indentation uses spaces, not tabs. YAML is indentation-sensitive.
  • Verify the file is saved in the same directory where you ran the command. Run ls deployment.yaml to confirm it exists.
  • If you see an ImagePullBackOff, confirm you typed nginx:1.30.2 exactly. A typo in the image name causes a pull failure.

Help me debug why my Deployment shows 0/3 READY after applying my deployment.yaml manifest.

Explore the Deployment, ReplicaSet, and Pod hierarchy

A Deployment does not run containers directly. It creates a ReplicaSet, and the ReplicaSet creates the Pods. Understanding this three-layer hierarchy is one of the most common Kubernetes interview questions.

  • View the Deployment by running:
kubectl get deployments
  • View the ReplicaSet the Deployment created by running:
kubectl get replicasets

Notice the ReplicaSet name includes a hash suffix (something like web-app-5d9f8b7c6d). This is the pod-template-hash that Kubernetes generates automatically.

  • View the 3 Pods the ReplicaSet created by running:
kubectl get pods

You should see 3 pods with names starting with web-app-, all showing Running status and 1/1 READY.

  • Get detailed information about the Deployment by running:
kubectl describe deployment web-app

What is the pod-template-hash?

Each unique Pod template gets its own ReplicaSet, and Kubernetes names it by appending a hash of the template spec. When you update the image or configuration later, a new ReplicaSet is created with a new hash.

This is exactly how rolling updates work. The old ReplicaSet scales down while the new one scales up. Interviewers love asking about this mechanism.

Pods stuck in Pending?

  • Run kubectl get nodes and confirm your node shows Ready status. If it shows NotReady, restart Docker Desktop and wait for Kubernetes to come back online.
  • Run kubectl describe pod <pod-name> and check the Events section at the bottom for scheduling errors.

Why are my pods stuck in Pending status on Docker Desktop Kubernetes?

Scale the Deployment and observe

Kubernetes is declarative. You tell it the desired state and it figures out how to get there. Scaling is a perfect example of this principle in action.

  • Scale the Deployment to 5 replicas by running:
kubectl scale deployment web-app --replicas=5
  • Watch the new Pods come online by running:
kubectl get pods -w

You should see 5 pods listed, with the 2 new ones transitioning from ContainerCreating to Running. Press Ctrl+C to stop watching once all 5 show Running.

  • Scale back down to 3 replicas by running:
kubectl scale deployment web-app --replicas=3
  • Confirm the scale-down completed by running:
kubectl get pods

You should see exactly 3 pods in Running status. Kubernetes terminated the 2 extra Pods to match your declared desired state.

Why is declarative scaling powerful?

You said "I want 5" and Kubernetes created 2 more. You said "I want 3" and Kubernetes removed 2. You never told it which pods to create or kill.

The Deployment controller constantly compares the current state to the desired state and reconciles the difference. This reconciliation loop is the core pattern behind all Kubernetes controllers.

Your application is running with 3 healthy replicas. Next up, you will inject custom configuration and add health probes to make the Deployment production-ready.

Add Configuration and Health Probes

Your Deployment is running with 3 healthy replicas. Now it's time to make it production-ready by injecting external configuration and adding health monitoring.

Right now, your nginx pods use their default configuration baked into the image. Kubernetes also has no way to detect if the app inside a container is healthy or frozen. ConfigMaps solve the first problem by injecting configuration files without rebuilding the image. Health probes solve the second by giving Kubernetes automatic restart and traffic-routing signals.

In this step, get ready to:
  • Create a ConfigMap to inject custom nginx configuration into your pods.
  • Add readiness and liveness probes to detect unhealthy containers.
  • Verify that configuration and health checks are working inside the cluster.
Create a ConfigMap and mount it into the Deployment

A ConfigMap lets you store configuration data separately from your container image. You mount it as a file inside the container at runtime.

  • Create a new file called configmap.yaml in the same folder as your deployment.yaml file.
  • Paste the following content into configmap.yaml:
# ConfigMap that provides a custom nginx server configuration
apiVersion: v1
kind: ConfigMap
metadata:
  name: web-config
  namespace: k8s-lab
data:
  # This key becomes the filename when mounted as a volume
  default.conf: |
    server {
        listen 80;
        server_name localhost;

        location / {
            root /usr/share/nginx/html;
            index index.html index.htm;
        }

        location /health {
            access_log off;
            return 200 "healthy\n";
            add_header Content-Type text/plain;
        }
    }

What does this ConfigMap do?

The data section holds key-value pairs. Here, the key default.conf becomes the filename when Kubernetes mounts this ConfigMap as a volume inside your container.

The nginx configuration defines a standard web server on port 80 plus a dedicated /health endpoint that returns a 200 status. This gives your health probes something lightweight to hit without serving actual HTML.

  • Save configmap.yaml.

✔️ Awesome, I've got everything!

Great. Make sure you've saved the file before moving on.

ⓧ I'd like to double check the full code

apiVersion: v1
kind: ConfigMap
metadata:
  name: web-config
  namespace: k8s-lab
data:
  default.conf: |
    server {
        listen 80;
        server_name localhost;

        location / {
            root /usr/share/nginx/html;
            index index.html index.htm;
        }

        location /health {
            access_log off;
            return 200 "healthy\n";
            add_header Content-Type text/plain;
        }
    }

Now you need to tell your Deployment to mount this ConfigMap as a volume so the nginx container can read it.

  • Open deployment.yaml.
  • Find the line - containerPort: 80. Add the following volumeMounts section directly below ports, at the same indentation level as ports:
          volumeMounts:
            - name: config-volume
              mountPath: /etc/nginx/conf.d
  • At the bottom of the file, add a volumes section at the same indentation level as containers:
      volumes:
        - name: config-volume
          configMap:
            name: web-config

What do volumeMounts and volumes do?

The volumes section declares a volume backed by the web-config ConfigMap. The volumeMounts section tells the container where to mount that volume.

Kubernetes takes the default.conf key from the ConfigMap and writes it as a file at /etc/nginx/conf.d/default.conf inside each pod. Nginx reads from that directory automatically.

  • Save deployment.yaml.
  • Apply both files by running these commands:
kubectl apply -f configmap.yaml
kubectl apply -f deployment.yaml
  • Run kubectl get pods to confirm all 3 pods restart and reach Running status with 1/1 READY.

Pods stuck in ContainerCreating or CrashLoopBackOff?

Make sure the name in volumeMounts matches the name in volumes exactly. Both must be config-volume.

Check that configmap.yaml was applied before deployment.yaml. If the ConfigMap does not exist, the pod cannot mount it.

If you're stuck, help me debug why my pod can't mount the ConfigMap volume.

Add liveness and readiness probes

Probes tell Kubernetes how to check if your container is alive and ready to receive traffic. Without them, Kubernetes only knows whether the container process is running.

What is the difference between readiness and liveness probes?

A readiness probe controls whether a pod receives traffic. If it fails, Kubernetes removes the pod from Service endpoints but does NOT restart it.

A liveness probe controls whether a pod gets restarted. If it fails, the kubelet kills and restarts the container. This catches situations where the process is running but the app is frozen or deadlocked.

  • In deployment.yaml, find the volumeMounts section you just added. Add the following readiness and liveness probes directly above volumeMounts, at the same indentation level:
          # Checks if the pod is ready to receive traffic
          readinessProbe:
            httpGet:
              path: /
              port: 80
            initialDelaySeconds: 5
            periodSeconds: 5
          # Checks if the pod is alive and should keep running
          livenessProbe:
            httpGet:
              path: /
              port: 80
            initialDelaySeconds: 10
            periodSeconds: 10

What do these probe settings mean?

  • httpGet tells Kubernetes to make an HTTP GET request to the specified path and port.
  • initialDelaySeconds is how long Kubernetes waits after the container starts before running the first check.
  • periodSeconds is how often the check runs after the initial delay.

A 200 response means the probe passes. Any other status code means failure.

  • Save deployment.yaml and apply the update by running:
kubectl apply -f deployment.yaml
  • Wait about 15 seconds for the pods to roll out with the new probe configuration.
  • Verify the probes are registered by running:
kubectl get pods
  • All 3 pods should show Running with 1/1 READY. The READY column confirms the readiness probe is passing.

Pods showing 0/1 READY?

If pods stay at 0/1, the readiness probe is failing. Check your YAML indentation. The readinessProbe and livenessProbe keys must be at the same level as ports and volumeMounts (10 spaces of indentation).

If you're stuck, help me figure out why my readiness probe is failing.

✔️ Awesome, I've got everything!

Great. Make sure you've saved deployment.yaml and applied it.

ⓧ I'd like to double check the full code

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
  namespace: k8s-lab
  labels:
    app: web-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web-app
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
  template:
    metadata:
      labels:
        app: web-app
    spec:
      containers:
        - name: nginx
          image: nginx:1.30.2
          ports:
            - containerPort: 80
          readinessProbe:
            httpGet:
              path: /
              port: 80
            initialDelaySeconds: 5
            periodSeconds: 5
          livenessProbe:
            httpGet:
              path: /
              port: 80
            initialDelaySeconds: 10
            periodSeconds: 10
          volumeMounts:
            - name: config-volume
              mountPath: /etc/nginx/conf.d
      volumes:
        - name: config-volume
          configMap:
            name: web-config
Verify ConfigMap injection and endpoints

Time to confirm that the ConfigMap content is actually inside the container and that all pods are registered as healthy endpoints.

  • First, grab a pod name by running:
kubectl get pods
  • Copy one of the pod names from the output (it looks like web-app- followed by random characters).
  • Check the mounted ConfigMap content by running:
kubectl exec -it [[POD_NAME="one of your pod names from kubectl get pods"]] -- cat /etc/nginx/conf.d/default.conf
  • You should see the full nginx server block from your ConfigMap printed in the terminal, including the /health endpoint.
  • Verify that all 3 pods are passing their readiness probes by running:
kubectl get endpoints
  • You should see 3 IP addresses listed. Each IP represents a pod that is passing its readiness probe and is eligible to receive traffic.

Why do endpoints matter?

When you create a Service later, it uses these endpoints to route traffic to healthy pods. If a pod's readiness probe fails, its IP disappears from the endpoints list and the Service stops sending traffic to it.

This is one of the most common interview debugging scenarios. If a Service returns no response, the first thing to check is whether kubectl get endpoints shows any IPs.

Not seeing the nginx config in the exec output?

If cat returns "No such file or directory", the volume is not mounted correctly. Run kubectl describe pod and look at the Mounts section to see where volumes are attached.

If you need help, help me understand why my ConfigMap file is not appearing in the container.

Your pods are now production-grade. They have externalized configuration, a dedicated health endpoint, and probes that tell Kubernetes exactly when to restart or remove them from traffic. Next up, you'll expose this Deployment to the network with Services.

Set Up Services and Networking

Your pods are running and healthy, but they are isolated. Each pod has its own IP address that changes every time it restarts.

In this step, you will create a Service to give your pods a single, stable address that never changes. You will then test connectivity from inside the cluster and expose the app to your browser.

In this step, get ready to:
  • Create a ClusterIP Service to route traffic to your pods.
  • Test internal connectivity with a temporary pod.
  • Upgrade to NodePort and access the app from your browser.
Create a ClusterIP Service

A ClusterIP Service gives your pods a single virtual IP inside the cluster. It load-balances traffic across all pods whose labels match the Service's selector.

  • Create a new file called service.yaml in the same folder as your deployment.yaml.
  • Paste the following manifest into service.yaml:
apiVersion: v1
kind: Service
metadata:
  name: web-svc
  namespace: k8s-lab
spec:
  # ClusterIP is the default Service type - internal only
  type: ClusterIP
  selector:
    # Must match the labels on your pods
    app: web-app
  ports:
    - port: 80
      targetPort: 80
      protocol: TCP

What does this manifest do?

The selector field is the glue between the Service and your pods. It matches against the app: web-app label you set on your Deployment's pod template.

The port is what other things inside the cluster use to reach the Service. The targetPort is the port on the actual container that receives traffic. If labels do not match, the Service has zero endpoints and traffic goes nowhere.

  • Apply the Service by running this command in PowerShell:
kubectl apply -f service.yaml
  • Save service.yaml.
  • Verify the Service was created by running:
kubectl get svc web-svc

You should see web-svc listed with type ClusterIP and a cluster IP address assigned.

Service not showing up?

  • Make sure you saved service.yaml before running the apply command.
  • Check that namespace: k8s-lab is in your metadata section. If it is missing, the Service lands in the default namespace instead.

Still stuck? Help me troubleshoot why my Kubernetes Service is not appearing in the k8s-lab namespace.

✔️ Awesome, I've got everything!

Great. Double check you have saved service.yaml before moving on.

ⓧ I'd like to double check the full code

apiVersion: v1
kind: Service
metadata:
  name: web-svc
  namespace: k8s-lab
spec:
  type: ClusterIP
  selector:
    app: web-app
  ports:
    - port: 80
      targetPort: 80
      protocol: TCP
Test internal connectivity

Before exposing the Service externally, confirm that pods inside the cluster can reach it using just its name.

  • Test the Service by spinning up a temporary pod and making a request:
kubectl run test-pod --image=busybox --rm -it --restart=Never -- wget -O- http://web-svc:80 --timeout=5

You should see the HTML content of the nginx welcome page printed in your terminal, followed by a message that the pod was deleted.

What does this command do?

This command creates a temporary busybox pod that runs wget against your Service name. The --rm flag deletes the pod automatically after it finishes.

Kubernetes DNS automatically resolves web-svc to the Service's ClusterIP. Inside the same namespace, just the service name works. This is how microservices discover each other without hardcoding IP addresses.

Seeing a connection timeout?

  • Run kubectl get endpoints web-svc to check if any pod IPs appear. If the list is empty, your Service selector does not match the pod labels.
  • Confirm your pods are still Running with kubectl get pods. If pods restarted, the readiness probes need a few seconds to pass before endpoints populate.

Need more help? Help me debug why wget to web-svc times out in my Kubernetes cluster.

Upgrade to NodePort and access from your browser

ClusterIP only works inside the cluster. To reach your app from your browser, you need to upgrade the Service to NodePort.

  • Patch the Service type to NodePort by running:
kubectl patch svc web-svc -p '{"spec":{"type":"NodePort"}}'
  • Find the assigned NodePort by running:
kubectl get svc web-svc

Look at the PORT(S) column. You will see something like 80:31234/TCP. The number after the colon (in the range 30000-32767) is your NodePort.

  • Open your browser.
  • Navigate to http://localhost: followed by the NodePort number from the output above.

You should see the nginx welcome page in your browser.

How do Service types build on each other?

Think of the Service types as a hierarchy. ClusterIP is internal only. NodePort builds on ClusterIP by opening a port on every node in your cluster. LoadBalancer builds on NodePort by provisioning an external load balancer (cloud environments only).

For interviews, remember the chain: ClusterIP < NodePort < LoadBalancer. Each layer adds more accessibility. On Docker Desktop, your single node is localhost, so the NodePort is reachable at localhost:<port>.

  • Confirm the Service has all three pod IPs as endpoints by running:
kubectl get endpoints web-svc

You should see three IP addresses listed, one for each of your running pods.

Browser showing a connection refused error?

  • Make sure you are using http:// not https:// in your browser URL. There is no TLS certificate configured.
  • Double check the port number matches the NodePort shown in the kubectl get svc output. It changes each time you recreate the Service.
  • Run kubectl get pods to confirm all pods are in Running state with 1/1 READY.

Still not working? Help me access my Kubernetes NodePort Service from my browser on Docker Desktop.

Your app is now accessible from your browser through a stable Service. Next up, you will perform rolling updates, intentionally break things, and practice the exact troubleshooting commands interviewers test.

Rolling Updates, Rollbacks, and Troubleshooting

Your app is deployed, scaled, and accessible from the browser. In production, the next challenge is updating it without downtime and recovering when things break.

In this step, you will perform rolling updates, trigger deliberate failures, and fix each one using the same kubectl commands you would use in a live incident. These are the exact scenarios interviewers use to test Kubernetes troubleshooting skills.

In this step, get ready to:
  • Perform a rolling update and rollback.
  • Diagnose a CrashLoopBackOff from a missing ConfigMap.
  • Fix a service selector mismatch.
Perform a rolling update and rollback

A rolling update replaces Pods gradually by creating a new ReplicaSet and scaling the old one down. If the new version is broken, a rollback reverts to the previous ReplicaSet instantly.

  • Update the nginx image to 1.31.1 by running this command:
kubectl set image deployment/web-app nginx=nginx:1.31.1
  • Watch the rollout progress by running:
kubectl rollout status deployment/web-app

You should see a message confirming the rollout completed successfully.

  • View the revision history by running:
kubectl rollout history deployment/web-app

You should see at least two revisions listed. Each revision represents a unique pod template.

What is a rolling update?

When you change the pod template (like the image version), the Deployment controller creates a new ReplicaSet and gradually shifts pods over. The maxSurge field controls how many extra pods can exist during the transition. The maxUnavailable field controls how many can be down.

Your deployment uses maxSurge: 1 and maxUnavailable: 1. This means Kubernetes adds one new pod before removing one old pod, keeping the app available throughout.

Now trigger a bad update to see what happens when an image does not exist.

  • Set the image to a tag that does not exist by running:
kubectl set image deployment/web-app nginx=nginx:doesnotexist
  • Watch the rollout attempt with a timeout by running:
kubectl rollout status deployment/web-app --timeout=30s

This command will wait up to 30 seconds, then report that the rollout did not complete.

  • Check the pod states by running:
kubectl get pods

You should see some pods in ImagePullBackOff or ErrImagePull status. Kubernetes cannot pull an image that does not exist.

  • Recover by rolling back to the previous working revision:
kubectl rollout undo deployment/web-app
  • Verify the pods are healthy again by running:
kubectl get pods

All 3 pods should return to Running status with 1/1 READY.

How does rollback work?

The kubectl rollout undo command reverts the Deployment to the previous ReplicaSet. Kubernetes keeps old ReplicaSets around (scaled to zero) specifically so you can roll back instantly without redeploying.

This is one of the most commonly tested scenarios in interviews. The interviewer breaks a deployment with a bad image and asks you to recover it.

Pods still showing ImagePullBackOff after undo?

Wait 10-15 seconds and run kubectl get pods again. Kubernetes needs time to terminate the failing pods and spin up the reverted ones.

If pods remain stuck, check that Docker Desktop has internet access. The rollback still needs to pull the original nginx:1.30.2 image if it was evicted from the local cache.

help me debug why my pods are still in ImagePullBackOff after running rollout undo

Diagnose a missing ConfigMap (CrashLoopBackOff)

When a Deployment references a ConfigMap as a volume and that ConfigMap does not exist, Kubernetes cannot create the container at all. This produces a CrashLoopBackOff or CreateContainerConfigError status.

  • Delete the ConfigMap to simulate it going missing:
kubectl delete configmap web-config
  • Force a rollout restart so new pods try to mount the now-deleted ConfigMap:
kubectl rollout restart deployment/web-app
  • Wait about 10 seconds, then check pod status by running:
kubectl get pods

You should see pods in CrashLoopBackOff or CreateContainerConfigError status.

  • Diagnose the root cause by describing one of the failing pods. Copy a pod name from the output above and run:
kubectl describe pod [[POD_NAME="name of a failing pod from the output above"]]

Scroll down to the Events section. You should see a warning about the missing ConfigMap preventing the volume mount.

What does the Events section tell you?

The Events section at the bottom of kubectl describe pod output is your primary debugging tool. It shows the timeline of what Kubernetes tried to do and where it failed.

For missing ConfigMaps, you will typically see a message about the referenced ConfigMap not being found. This prevents the container from starting entirely.

Now fix the issue by restoring the ConfigMap and restarting the deployment.

  • Re-apply the ConfigMap from your file by running:
kubectl apply -f configmap.yaml
  • Restart the deployment so pods pick up the restored ConfigMap:
kubectl rollout restart deployment/web-app
  • Verify the pods recover by running:
kubectl get pods

All 3 pods should return to Running with 1/1 READY within 15-20 seconds.

Exit codes to remember for interviews

When a container crashes, the exit code tells you why. Exit code 1 means a general application error. Exit code 137 means the container was killed by OOM (out of memory). Exit code 139 means a segmentation fault.

Missing ConfigMaps and Secrets referenced as volumes prevent container creation entirely, so you may not even get an exit code. The Events section is your best diagnostic tool in that case.

Pods stuck in CrashLoopBackOff after re-applying ConfigMap?

Make sure you ran kubectl rollout restart deployment/web-app after applying the ConfigMap. Existing failing pods will not automatically retry the volume mount. The restart forces new pods that pick up the restored ConfigMap.

help me fix pods stuck in CrashLoopBackOff after restoring my ConfigMap

Diagnose a service selector mismatch

A Service routes traffic to pods by matching labels. If the selector does not match any pod labels, the Service has zero endpoints and all traffic times out. This is one of the most common interview debugging scenarios.

  • Break the service by patching its selector to a label that no pod has:
kubectl patch svc web-svc -p '{\"spec\":{\"selector\":{\"app\":\"wrong-label\"}}}'
  • Test the broken service by running a temporary pod that tries to reach it:
kubectl run test-pod --image=busybox --rm -it --restart=Never -- wget -O- http://web-svc:80 --timeout=5

This command will time out after 5 seconds. The Service cannot route traffic because it has no matching pods.

  • Confirm the Service has no endpoints by running:
kubectl get endpoints web-svc

You should see <none> in the ENDPOINTS column. This is the telltale sign of a selector mismatch.

Why check endpoints first?

When a Service is not routing traffic, kubectl get endpoints is the fastest diagnostic command. If it shows no IPs, the problem is the selector. If it shows IPs, the problem is elsewhere (pod health, port mismatch, network policy).

Interviewers love this question because it tests whether you jump straight to the right diagnostic tool or waste time checking unrelated things.

  • Fix the selector by patching it back to the correct label:
kubectl patch svc web-svc -p '{\"spec\":{\"selector\":{\"app\":\"web-app\"}}}'
  • Verify the endpoints repopulate by running:
kubectl get endpoints web-svc

You should see 3 pod IPs listed again.

  • Confirm traffic flows again by running the wget test one more time:
kubectl run test-pod --image=busybox --rm -it --restart=Never -- wget -O- http://web-svc:80 --timeout=5

This time you should see the nginx HTML response returned successfully.

  • Open your browser and navigate to http://localhost: followed by your NodePort number to confirm browser access still works.

The selector debugging pattern

When a Service shows no endpoints, always compare the Service selector with the pod labels. Run kubectl get svc web-svc -o yaml to see the selector, and kubectl get pods --show-labels to see what labels your pods actually have. A single typo breaks the match.

Wget test-pod failing with a different error?

If you see an error about the pod test-pod already existing, a previous test-pod did not clean up. Delete it manually by running kubectl delete pod test-pod and try again.

If wget returns a connection refused error instead of a timeout, your pods may have restarted during the ConfigMap fix. Run kubectl get pods to confirm all 3 are in Running state before retrying.

help me debug why my wget test against web-svc is failing

Secret mission

Resource Limits and OOMKilled Diagnosis

You have diagnosed bad images, missing ConfigMaps, and broken selectors. But there is one more failure mode that interviewers love to test: memory pressure. In this mission, you will add resource requests and limits to your Deployment, deliberately trigger an OOMKill, and diagnose it using exit codes and QoS classes.

Clean Up Your Resources

Clean Up Your Resources

This project runs entirely locally on your machine using Docker Desktop's built-in Kubernetes cluster. There are no cloud charges or external costs to worry about.

Decide whether to keep your resources running, pause them to come back later, or delete them entirely.

Resources you used:

  • A local Kubernetes cluster running inside Docker Desktop (no cost).
  • A namespace k8s-lab containing Deployments, Services, and ConfigMaps.
  • Local YAML manifest files (deployment.yaml, configmap.yaml, service.yaml).

Keep everything running

No action needed. Choose this if you are still actively building or plan to continue practising Kubernetes commands soon.

  • The Kubernetes cluster stays running inside Docker Desktop and uses some RAM and CPU on your machine.
  • There are no external charges. Everything is local.
  • Your namespace, Deployments, Services, and ConfigMaps remain available for further experimentation.

Pause - I'll come back to this later

Shut down the Kubernetes cluster to free up memory and CPU while keeping your files for next time.

  • Open Docker Desktop and navigate to the Kubernetes view.
  • Select Stop to disable the cluster. This stops and removes the Kubernetes containers.
  • Alternatively, quit Docker Desktop entirely to free all resources.
  • Your local YAML files remain on disk. When you are ready to resume, re-enable Kubernetes in Docker Desktop Settings and re-apply your manifests.

Delete - I don't want to use this again

Remove all project resources and start fresh. This deletes everything you created in this lab.

  • Delete the entire namespace (this removes all Deployments, Services, ConfigMaps, and Pods inside it) by running:
kubectl delete namespace k8s-lab
  • Reset your kubectl context back to the default namespace by running:
kubectl config set-context --current --namespace=default
  • Delete the local YAML manifest files by running:
Remove-Item deployment.yaml, configmap.yaml, service.yaml
  • Optionally disable Kubernetes entirely in Docker Desktop. Open Docker Desktop, go to Settings, select the Kubernetes tab, uncheck Enable Kubernetes, and click Apply.

Nice Work!

Nice Work!

You just built a full Kubernetes interview troubleshooting lab from scratch. You deployed a multi-replica application, wired up configuration and networking, and then deliberately broke it in the exact ways interviewers test.

You've learned how to:

  • Deploy a multi-replica nginx application using Deployments, ConfigMaps, health probes, and Services on a local Kubernetes cluster.
  • Perform rolling updates and rollbacks to manage application versions safely across ReplicaSets.
  • Diagnose and fix production failure scenarios including CrashLoopBackOff (bad image), missing ConfigMap, and service selector mismatch using kubectl describe and kubectl get endpoints.
  • Secret Mission: Configured resource requests and limits, triggered an OOMKilled pod, and diagnosed it using exit code 137 and kubectl describe pod output.

Ready to quiz yourself?