Kubernetes Lesson 9 – ReplicaSets | Dataplexa
Kubernetes Fundamentals · Lesson 9
Hands-On Lesson — Open your cluster and follow along

ReplicaSets

One Pod is great. One Pod in production is terrifying. What happens when it crashes? What happens when the node it lives on dies? This lesson is about ReplicaSets — the object that keeps a fixed number of your Pods alive at all times, no matter what goes wrong. We'll create one, break it on purpose, and watch Kubernetes heal itself in real time.

🖥️
Get your cluster ready

Use Play with Kubernetes at labs.play-with-k8s.com or run minikube start. Confirm you're good with kubectl get nodes — you should see at least one node in Ready state.

The Problem With a Single Pod

In Lesson 8 you created a Pod directly. It worked. But think about what happens next:

💥
The node running your Pod crashes at 2 AM. Your Pod is gone. It never comes back. Your service is down until someone manually creates a new Pod.
📈
Traffic doubles. One Pod is getting hammered. There's nothing to spread the load across — it's just that one container handling everything until it falls over.
🐛
A memory leak causes your container to crash. It restarts thanks to the kubelet's health check — but if it crashes in a loop it enters CrashLoopBackOff and stays there.

A single Pod has no safety net. A ReplicaSet is that safety net.

What Is a ReplicaSet?

A ReplicaSet is an object that says: "I want exactly N copies of this Pod running at all times." Kubernetes reads that number, counts the actual running Pods, and continuously makes reality match what you declared.

Too few Pods? It creates more. Too many? It deletes some. A Pod crashes? It creates a replacement straight away — automatically, without anyone having to do anything.

This automatic fixing of broken state is called self-healing — and it's one of Kubernetes' most powerful features.

Self-Healing in Action — ReplicaSet With 3 Replicas
Before — All 3 healthy
payment-api-pod-1
Running
payment-api-pod-2
Running
payment-api-pod-3
Running
💥
Pod 2 crashes
ReplicaSet notices
creates replacement
After — Back to 3
payment-api-pod-1
Running
payment-api-pod-2
Dead
payment-api-pod-3
Running
payment-api-pod-4
New ✨
The ReplicaSet doesn't "restart" the crashed Pod — it creates a completely new one. The dead Pod stays dead (Kubernetes keeps it for a while so you can read its logs). The new Pod gets a new name and a new IP address. The count goes back to 3.

How Does a ReplicaSet Know Which Pods Are "Its" Pods?

This trips up a lot of beginners. The ReplicaSet doesn't own Pods by name — names change every time a new Pod is created. It finds its Pods using label selectors.

You tell the ReplicaSet: "count any Pod that has the label app: payment-api." The ReplicaSet counts those Pods. If the count is below 3, it creates a new Pod — and stamps it with that same label so it gets counted next time.

This label-based approach is actually clever. Because it means if you manually create a Pod with the same label, the ReplicaSet will count it — and delete one of its own Pods to keep the total at 3. More on this in the hands-on section below.

Writing Your First ReplicaSet YAML

The scenario: Your payment API is running as a single Pod. A production incident last week proved you need at least 3 copies running at all times for redundancy. Your lead says to turn this into a ReplicaSet today. Here's the YAML.

apiVersion: apps/v1              # ReplicaSets use the apps/v1 API group — not the core v1
kind: ReplicaSet                 # The object type
metadata:
  name: payment-api-rs           # Name of the ReplicaSet itself
  labels:
    app: payment-api             # Label on the ReplicaSet object (not the Pods)
spec:
  replicas: 3                    # We want exactly 3 Pods running at all times
  selector:                      # How this ReplicaSet finds its Pods — via labels
    matchLabels:
      app: payment-api           # Count any Pod that has this label
  template:                      # The blueprint for every Pod this ReplicaSet creates
    metadata:
      labels:
        app: payment-api         # MUST match the selector above — or Kubernetes rejects it
    spec:
      containers:
        - name: payment-api      # Container name inside each Pod
          image: nginx:1.25      # Image to use — nginx as our stand-in
          ports:
            - containerPort: 80  # Port the container listens on
          resources:
            requests:
              memory: "128Mi"    # Minimum memory needed to schedule this Pod
              cpu: "250m"        # 0.25 of one CPU core
            limits:
              memory: "256Mi"    # Kill the container if it exceeds this
              cpu: "500m"        # Throttle the container if it exceeds this
The three sections that need to match perfectly
spec.replicas: 3 — this is the number you declare. Kubernetes will always try to make the running count equal this. Change it to 5 and Kubernetes creates 2 more. Change it to 1 and it deletes 2.
spec.selector.matchLabels — this is how the ReplicaSet counts its Pods. It looks for any Pod with app: payment-api. This is the link between the ReplicaSet and the Pods it manages.
spec.template.metadata.labels — the label stamped on every Pod this ReplicaSet creates. It must include the same label as the selector — otherwise the ReplicaSet would create Pods it can never find, and Kubernetes will reject the YAML.

Deploying and Watching It Start

The scenario: You saved the YAML above as payment-rs.yaml. Let's apply it and watch all three Pods spin up at the same time.

# Create the ReplicaSet
kubectl apply -f payment-rs.yaml

# Watch all three Pods come to life — -w streams live updates
kubectl get pods -w

# See the ReplicaSet itself
kubectl get replicaset

# Short form also works
kubectl get rs
What just happened?

Pod names are auto-generated — payment-api-rs-x2p9k, payment-api-rs-mn7ql, payment-api-rs-k8sj2. The ReplicaSet name is the prefix, and Kubernetes appends a random suffix. You'll never know these names in advance — you don't need to.

All three started simultaneously — the Scheduler found three nodes (or scheduled multiple Pods on one node if you only have one) and the kubelet on each pulled the image and started the container at the same time.

DESIRED / CURRENT / READY on the ReplicaSet output — DESIRED is what you declared (3), CURRENT is how many Pods exist right now (3), READY is how many have passed their readiness check (3). When all three match, you're healthy.

Breaking It On Purpose — Watch Self-Healing Happen

This is the most satisfying moment in any Kubernetes demo. We're going to delete one of the Pods manually — simulating a crash — and watch the ReplicaSet create a replacement in real time.

The scenario: It's 11 PM. An engineer accidentally deletes the wrong Pod in production. Before they even have time to panic, Kubernetes has already created a fresh one. Here's what that looks like from the terminal.

# First — open a second terminal and run this to watch Pods live
kubectl get pods -w

# In your first terminal — grab the name of one of your Pods
kubectl get pods

# Delete one Pod by name (replace with your actual Pod name)
kubectl delete pod payment-api-rs-x2p9k

# Watch what happens in the second terminal...
# The deleted Pod shows Terminating, then a brand new Pod appears
What just happened?

The moment payment-api-rs-x2p9k started Terminating, the ReplicaSet Controller noticed: "I need 3, I now only have 2, I need to create 1 more." A new Pod — payment-api-rs-7qr8n — appeared in Pending within milliseconds.

Total time from delete to replacement running: about 5 seconds. That's the gap users might experience if traffic isn't spread across multiple Pods and doesn't have retries. With 3 Pods and a Service in front, users feel nothing at all — requests just go to the other two while the third is replaced.

The new Pod has a completely new name and a new IP address. The ReplicaSet doesn't care — it matches by label, and this new Pod has app: payment-api stamped on it, so it gets counted.

Scaling Up and Down

The scenario: Black Friday is tomorrow. Your traffic is forecast to triple. You need to go from 3 replicas to 8 immediately. Here are two ways to do it.

# Option 1 — scale using kubectl directly (fastest, good for emergencies)
# This updates the replicas field in etcd without touching your YAML file
kubectl scale replicaset payment-api-rs --replicas=8

# Check the result
kubectl get pods
kubectl get rs

# Option 2 — edit the YAML file to change replicas: 3 to replicas: 8
# then re-apply it (better for teams — keeps the file as the source of truth)
kubectl apply -f payment-rs.yaml

# Scale back down after Black Friday
kubectl scale replicaset payment-api-rs --replicas=3
What just happened?

Five new Pods appeared almost instantly. The ReplicaSet compared "I want 8, I have 3" and created 5 more in one go. The Scheduler placed them across available nodes simultaneously.

Important note about kubectl scale vs editing YAML: kubectl scale changes the live state in etcd but doesn't update your YAML file. If a colleague then runs kubectl apply -f payment-rs.yaml, it will revert back to 3 because the file still says 3. In teams, always update the file and apply it — so the file remains the single source of truth.

The Label Experiment — How ReplicaSets Really Count

Here's a fun experiment that proves exactly how label-based counting works. We'll create a standalone Pod with the same label as our ReplicaSet — and watch what happens.

The scenario: You're at 3 replicas. You manually create a fourth Pod with the same label. The ReplicaSet now counts 4 Pods matching its selector — but it only wants 3. So it deletes one. Let's try it.

# Make sure you're back at 3 replicas first
kubectl scale replicaset payment-api-rs --replicas=3

# Create a standalone Pod with the SAME label as the ReplicaSet selector
kubectl run intruder-pod --image=nginx:1.25 --labels="app=payment-api"

# Watch what happens — the ReplicaSet sees 4 Pods, wants 3, deletes one
kubectl get pods -w
What just happened — and why this matters

The moment intruder-pod started running with label app: payment-api, the ReplicaSet counted 4 matching Pods. It wanted 3. So it immediately deleted one of its own Pods — payment-api-rs-k8sj2 was terminated.

This is why labels matter enormously. If you accidentally give a test Pod the same label as a production ReplicaSet's selector, Kubernetes will destroy one of your production Pods immediately. This is a real mistake that has caused real incidents. Always be careful with labels on Pods you create manually.

Inspecting Your ReplicaSet

The scenario: Something feels off. Pods seem to be restarting more than expected. You want to see the full picture of your ReplicaSet — events, Pod status, selector rules.

# Full details on the ReplicaSet — shows selector, Pod template, events
kubectl describe replicaset payment-api-rs

# See which Pods belong to this ReplicaSet using label filtering
kubectl get pods -l app=payment-api

# The -l flag means "filter by label" — this is exactly how the ReplicaSet counts Pods
# Try changing the label value and see how the results change
kubectl get pods -l app=payment-api,environment=staging
What just happened?

The Events section shows the history of this ReplicaSet — every Pod it ever created, and every Pod it ever deleted. You can see the entire story of what happened during the label experiment above: it deleted k8sj2 and created 7qr8n.

kubectl get pods -l app=payment-api — the -l flag filters Pods by label, which is exactly the same logic the ReplicaSet uses internally to count. This is a powerful command for debugging any label-related issues.

An Honest Word — You Rarely Use ReplicaSets Directly

Everything you've just done with a ReplicaSet is genuinely useful to understand. But in day-to-day production work, you'll rarely create a ReplicaSet directly.

Instead you use a Deployment — which creates and manages a ReplicaSet for you automatically. The reason is simple: ReplicaSets alone have no understanding of updates. If you change the container image in your ReplicaSet YAML and re-apply it, the existing Pods don't change — they keep running the old image until they're individually deleted and replaced. That's not how you want to manage a production service.

A Deployment wraps a ReplicaSet and adds rolling updates, rollback support, and update history on top. We cover Deployments thoroughly in Lesson 10. Everything you learned here carries over completely — a Deployment is just a smarter controller on top of a ReplicaSet.

ReplicaSet Deployment
Keeps N replicas running ✓ Yes ✓ Yes
Self-healing ✓ Yes ✓ Yes
Rolling updates ✗ No ✓ Yes
Rollback support ✗ No ✓ Yes
Use in production Only if you have a very specific reason Always — this is the standard

Cleaning Up

# Delete the ReplicaSet — this also deletes all the Pods it manages
kubectl delete replicaset payment-api-rs

# Or delete using the YAML file
kubectl delete -f payment-rs.yaml

# Clean up the intruder Pod if it's still around
kubectl delete pod intruder-pod

# Verify everything is gone
kubectl get pods
kubectl get rs
What just happened?

When you delete a ReplicaSet, Kubernetes deletes all the Pods it owns at the same time — it's a cascading delete. This is different from deleting individual Pods: delete one Pod and the ReplicaSet recreates it. Delete the ReplicaSet itself and all its Pods go with it. If you ever need to delete a ReplicaSet but keep the Pods running (unusual but possible), use kubectl delete rs payment-api-rs --cascade=orphan.

Why we spent a whole lesson on something you won't use directly

When something goes wrong with a Deployment in production — Pods stuck terminating, unexpected deletions, weird rolling update behaviour — understanding what the underlying ReplicaSet is doing is the key to diagnosing it. Every Deployment creates a ReplicaSet, and when you run kubectl describe deployment or look at events, you're seeing ReplicaSet behaviour underneath.

Also — the label experiment above is genuinely something that causes production incidents. Knowing how label selectors work, and why accidentally matching labels can cause Pods to get deleted, is the kind of knowledge that separates good engineers from great ones.

👨‍💻 Keep practising — try these yourself
1
Create the ReplicaSet with 3 replicas. Then edit the YAML to change replicas: 3 to replicas: 5 and re-apply with kubectl apply -f. Watch 2 new Pods appear.
2
Open two terminals side by side. In the first, run kubectl get pods -w. In the second, delete a Pod. Watch the replacement appear in the first terminal in real time.
3
Run kubectl get pods -l app=payment-api and compare the output to kubectl get pods. Notice anything different? Try changing the label value to something that doesn't match and see what happens.

Practice Questions

Type from memory — don't scroll up.

1. A ReplicaSet doesn't track its Pods by name. What does it use instead to find and count the Pods it manages?



2. Your ReplicaSet has replicas: 3. You manually delete 2 Pods. How many Pods will be running 30 seconds later?



3. ReplicaSets are great for keeping Pods running but have no support for rolling updates or rollbacks. Which object should you use instead for production deployments?



Knowledge Check

Pick the best answer.

1. One of your 3 ReplicaSet Pods crashes and exits. What does the ReplicaSet do?


2. Your ReplicaSet has replicas: 3 and selector app: payment-api. You manually create a new Pod with the label app: payment-api. What happens?


3. You run kubectl delete replicaset payment-api-rs. What happens to the 3 Pods it was managing?


Up Next · Lesson 10

Deployments

The object you'll use every single day in production. Deployments give you rolling updates, rollback in one command, and update history — all built on top of the ReplicaSet you just mastered.