Kubernetes ReplicaSet with Examples

Tested on Rocky Linux 10.2 (Red Quartz) workstation
Package kubectl 1.36.3
Applies to Any host with kubectl configured; any Kubernetes cluster
Cert prep CKA · CKAD
Lab environment Multi-node kubeadm cluster with containerd — install Kubernetes with kubeadm
Privilege Normal user (no sudo required on the workstation)
Scope ReplicaSet YAML, selector and template matching, verification, self-healing, scaling, Pod selection, comparison with Deployment and ReplicationController, Deployment ownership, deletion, and common problems. Does not cover rolling-update tuning, HPA, StatefulSet, DaemonSet, Job, probes, or scheduling depth.
Related guides Choose a Kubernetes workload resource
Kubernetes API resources

This walkthrough uses one ReplicaSet named web in the rs-demo namespace. You will create three nginx Pods, delete one to watch the controller replace it, scale the replica count, and see how label selectors connect the ReplicaSet to its Pods.


What Is a Kubernetes ReplicaSet?

A ReplicaSet maintains a specified number of Pods whose labels match its selector. When a matching Pod is deleted or fails, the controller creates a replacement from spec.template until the desired count is restored.

  • A ReplicaSet identifies potential managed Pods through spec.selector
  • Deployments normally create and manage ReplicaSets for you
  • Create a ReplicaSet directly mainly for learning or specialised cases

For normal stateless applications, apply a Deployment instead. It owns ReplicaSets and adds rollout and rollback. The resource flow looks like this:

Deployment → ReplicaSet → Pods


Create and Understand a ReplicaSet

ReplicaSet manifest

Save this manifest as web-replicaset.yaml:

yaml
apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: web
  namespace: rs-demo
  labels:
    app: web
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: nginx
          image: nginx:1.27-alpine

Create the namespace first:

bash
kubectl create namespace rs-demo

Sample output:

output
namespace/rs-demo created

Validate the manifest against the API server:

bash
kubectl apply --dry-run=server -f web-replicaset.yaml

Sample output:

output
replicaset.apps/web created (server dry run)

Apply the ReplicaSet:

bash
kubectl apply -f web-replicaset.yaml

Sample output:

output
replicaset.apps/web created

Important YAML fields

Field Purpose
spec.replicas Desired Pod count; defaults to one if omitted
spec.selector Required, immutable selector for potential managed Pods
spec.template Pod template used whenever new replicas are needed
spec.template.spec.restartPolicy Must be Always or omitted, which defaults to Always

A ReplicaSet Pod template cannot use OnFailure or Never. The lab manifest is valid because it omits the field.

Set spec.replicas to 0 to remove all managed Pods while keeping the ReplicaSet object.

Selector and template matching

These rules are strict:

  • spec.selector.matchLabels must match labels under spec.template.metadata.labels
  • Additional labels on the Pod template are allowed
  • A mismatch causes the API server to reject the manifest

A ReplicaSet selector is immutable after creation. To use a different selector, create a new ReplicaSet. ReplicaSet selectors are required, must match the Pod-template labels, and are immutable in apps/v1.

Copy the working manifest and change only the template label to test a mismatch:

bash
cp web-replicaset.yaml bad-replicaset.yaml

Edit bad-replicaset.yaml: set metadata.name to bad-rs and change the Pod template labels:

yaml
template:
  metadata:
    labels:
      app: frontend

A server dry-run surfaces the error before you apply:

bash
kubectl apply --dry-run=server -f bad-replicaset.yaml

Sample output:

output
The ReplicaSet "bad-rs" is invalid: spec.template.metadata.labels: Invalid value: {"app":"frontend"}: `selector` does not match template `labels`

ReplicaSet also supports matchExpressions for set-based selectors. See Kubernetes labels and selectors for expression syntax.


Verify the ReplicaSet and Its Pods

Wait until three Pods are Ready before you trust the status columns:

bash
kubectl wait --for=jsonpath='{.status.readyReplicas}'=3 replicaset/web -n rs-demo --timeout=60s

Sample output:

output
replicaset.apps/web condition met

List ReplicaSets. The short resource name is rs:

bash
kubectl get rs -n rs-demo

Sample output:

output
NAME   DESIRED   CURRENT   READY   AGE
web    3         3         3       6s
Column Meaning
DESIRED Desired number from spec.replicas
CURRENT Most recently observed number of non-terminating Pods controlled by the ReplicaSet
READY Non-terminating controlled Pods whose Ready condition is true
AGE Time since the ReplicaSet object was created

CURRENT is not simply every Pod that happens to match the selector. Ownership also matters, and Pods already controlled by another controller are not acquired.

During deletion or scale-down, terminating Pods can briefly coexist with replacements, so the total number of Pod objects may exceed spec.replicas. Kubernetes 1.36 can report these separately through .status.terminatingReplicas. That field is beta and enabled by default in current Kubernetes.

List the Pods the selector matched:

bash
kubectl get pods -l app=web -n rs-demo

Sample output:

output
NAME        READY   STATUS    RESTARTS   AGE
web-5s9n8   1/1     Running   0          6s
web-g84pf   1/1     Running   0          6s
web-smwgk   1/1     Running   0          6s

kubectl describe shows the selector, template, and controller events:

bash
kubectl describe replicaset web -n rs-demo

Sample Events excerpt:

output
Normal  SuccessfulCreate  7s    replicaset-controller  Created pod: web-smwgk
  Normal  SuccessfulCreate  7s    replicaset-controller  Created pod: web-g84pf
  Normal  SuccessfulCreate  7s    replicaset-controller  Created pod: web-5s9n8

Each Pod stores an owner reference back to the ReplicaSet:

bash
kubectl get pods -l app=web -n rs-demo -o custom-columns='POD:.metadata.name,OWNER:.metadata.ownerReferences[0].kind,OWNER-NAME:.metadata.ownerReferences[0].name'

Sample output:

output
POD         OWNER        OWNER-NAME
web-5s9n8   ReplicaSet   web
web-g84pf   ReplicaSet   web
web-smwgk   ReplicaSet   web

Test ReplicaSet Reconciliation and Scaling

Delete a managed Pod

Delete one managed Pod by name without hard-coding the generated suffix:

bash
POD_NAME=$(kubectl get pod -n rs-demo -l app=web -o jsonpath='{.items[0].metadata.name}')

kubectl delete pod "$POD_NAME" -n rs-demo

Sample output:

output
pod "web-5s9n8" deleted

Watch until a new Pod with a different generated suffix reaches Running, then press Ctrl+C:

bash
kubectl get pods -l app=web -n rs-demo --watch

The ReplicaSet creates and deletes Pods to restore its desired replica count. Generated names and suffixes vary between clusters.

Scale up and down

Scale up imperatively:

bash
kubectl scale replicaset web --replicas=5 -n rs-demo

Sample output:

output
replicaset.apps/web scaled

Wait for five Ready Pods before you read 5/5:

bash
kubectl wait --for=jsonpath='{.status.readyReplicas}'=5 replicaset/web -n rs-demo --timeout=60s
bash
kubectl get rs web -n rs-demo

Sample output:

output
NAME   DESIRED   CURRENT   READY   AGE
web    5         5         5       16s

You can also change spec.replicas in web-replicaset.yaml and run kubectl apply -f web-replicaset.yaml for a declarative update.

kubectl scale changes the live ReplicaSet but does not update web-replicaset.yaml. The file still declares replicas: 3; applying it later restores that declared count. kubectl scale supports ReplicaSets and modifies their scale subresource.

Scale to zero

Set replicas to zero when you want to stop all managed Pods but keep the ReplicaSet object:

bash
kubectl scale rs web --replicas=0 -n rs-demo

Sample output:

output
replicaset.apps/web scaled

The ReplicaSet remains; Pods terminate:

bash
kubectl get rs web -n rs-demo

Sample output:

output
NAME   DESIRED   CURRENT   READY   AGE
web    0         0         0       18s

Scale back up before the next sections:

bash
kubectl scale rs web --replicas=3 -n rs-demo

Sample output:

output
replicaset.apps/web scaled
bash
kubectl wait --for=jsonpath='{.status.readyReplicas}'=3 replicaset/web -n rs-demo --timeout=60s
bash
kubectl get pods -l app=web -n rs-demo

Sample output:

output
NAME        READY   STATUS    RESTARTS   AGE
web-8bv92   1/1     Running   0          11s
web-bn59v   1/1     Running   0          11s
web-snqsd   1/1     Running   0          11s

How ReplicaSet Selection and Adoption Work

A ReplicaSet uses its selector to find potential Pods in the same namespace. It controls Pods that already reference it as controller and can adopt matching Pods that do not have another controlling owner. It does not take Pods away from another controller.

Once a matching Pod is controlled by the ReplicaSet, it is counted toward the ReplicaSet's observed replica status. ReplicaSets can acquire matching Pods without a controller owner reference, while owner references prevent controllers from interfering with dependents they do not control.

Changing a managed Pod's labels so it no longer matches the selector causes the ReplicaSet to release that Pod and create a replacement to restore the desired count. The isolated Pod continues running without that ReplicaSet as its controller until you delete it or another matching controller adopts it. This is important because isolating a Pod can temporarily leave the original Pod plus the replacement running.

Avoid overlapping selectors. Controllers do not steal Pods already owned by another controller, but matching unowned Pods can be adopted and overlapping controller instructions can behave unpredictably.


ReplicaSet and Deployment

ReplicaSet vs Deployment

ReplicaSet Deployment
Maintains matching Pod replicas Manages ReplicaSets and Pods
Replaces deleted Pods Replaces Pods through ReplicaSets
No rollout history Supports rollout history
No built-in rollback workflow Supports rollback
Usually managed indirectly Preferred for stateless applications

Changing a standalone ReplicaSet's spec.template does not replace existing Pods. Those Pods keep their original specification. Any Pod created later because of deletion or scale-up uses the new template, so one ReplicaSet can temporarily contain Pods created from different template versions. Use a Deployment for controlled replacement.

ReplicaSets maintain replica count but do not perform rolling updates directly.

For Pod versus Deployment trade-offs at the workload level, see Pod versus Deployment. For the full Deployment apply and rollout path, see Deployments and rolling updates.

How Deployments use ReplicaSets

When you apply a Deployment, Kubernetes follows this pattern:

  1. The Deployment creates a ReplicaSet from the Pod template
  2. The ReplicaSet creates the requested Pods
  3. A template change creates a new ReplicaSet with a new pod-template-hash
  4. With the default RollingUpdate strategy, the new ReplicaSet scales up while the previous ReplicaSet scales down
  5. A Recreate Deployment instead terminates the old Pods before creating Pods from the new template

Deployments support both RollingUpdate and Recreate. Older ReplicaSets may remain for rollout history.

Do not manually scale or edit a ReplicaSet owned by a Deployment. Change the Deployment instead; the Deployment controller is responsible for reconciling its ReplicaSets. A Deployment stores its revisions through the ReplicaSets it controls and uses them for rollout history and rollback.

The output below comes from a two-replica Deployment in a separate rs-demo2 namespace. Create it to compare Deployment-owned ReplicaSet names with a standalone ReplicaSet:

bash
kubectl create namespace rs-demo2

Save this manifest as web-deploy.yaml:

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
  namespace: rs-demo2
  labels:
    app: web
spec:
  replicas: 2
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: nginx
          image: nginx:1.27-alpine

Apply the Deployment and wait for rollout:

Label selectors used with kubectl get -l filter the metadata labels of each requested object. Pod-template labels do not automatically become Deployment metadata labels.

bash
kubectl apply -f web-deploy.yaml

Sample output:

output
deployment.apps/web created
bash
kubectl rollout status deployment/web -n rs-demo2

Sample output:

output
deployment "web" successfully rolled out

List what the Deployment created:

bash
kubectl get deploy,rs,pod -n rs-demo2 -l app=web

Sample output:

output
NAME                  READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/web   2/2     2            2           8s

NAME                             DESIRED   CURRENT   READY   AGE
replicaset.apps/web-7b8c57c6d6   2         2         2       8s

NAME                       READY   STATUS    RESTARTS   AGE
pod/web-7b8c57c6d6-nnjcb   1/1     Running   0          8s
pod/web-7b8c57c6d6-rtplr   1/1     Running   0          8s

Generated hashes and suffixes vary. You edit the Deployment object; it reconciles ReplicaSets beneath it. Rollout pause, resume, and rollback live in the rolling updates guide.


ReplicaSet vs ReplicationController

Feature ReplicaSet ReplicationController
API apps/v1 Legacy core v1
Equality selectors Supported Supported
Set-based selectors Supported Not supported
Recommended for new use Yes, normally through Deployment No

Mention ReplicationController only when you maintain older manifests. New workloads should use ReplicaSet, almost always through a Deployment.


Delete a ReplicaSet

Delete the ReplicaSet and Pods

Foreground deletion removes managed Pods before the ReplicaSet object disappears:

bash
kubectl delete replicaset web -n rs-demo --cascade=foreground

Sample output:

output
replicaset.apps "web" deleted

Verify both the controller and its Pods are gone:

bash
kubectl get rs,pod -n rs-demo -l app=web

Sample output:

output
No resources found in rs-demo namespace.

Recreate the ReplicaSet when you need it again:

bash
kubectl apply -f web-replicaset.yaml
bash
kubectl wait --for=jsonpath='{.status.readyReplicas}'=3 replicaset/web -n rs-demo --timeout=60s

Orphan the Pods

Before orphan deletion, wait until all three recreated Pods are Ready.

Orphan deletion removes the ReplicaSet object while leaving its Pods running:

bash
kubectl delete replicaset web -n rs-demo --cascade=orphan

Sample output:

output
replicaset.apps "web" deleted

The Pods keep running without a controller:

bash
kubectl get rs -n rs-demo

Sample output:

output
No resources found in rs-demo namespace.
bash
kubectl get pods -l app=web -n rs-demo

Sample output:

output
NAME        READY   STATUS    RESTARTS   AGE
web-5m494   1/1     Running   0          7s
web-tlcwr   1/1     Running   0          8s
web-wmk9g   1/1     Running   0          7s

Orphan deletion leaves dependent Pods running without their ReplicaSet controller. A new ReplicaSet with the same selector can later adopt them. Use orphan deletion only when you understand that consequence.


Troubleshoot Common ReplicaSet Problems

Start with describe and Pod status:

bash
kubectl describe replicaset <name> -n <namespace>
kubectl get pods -l app=web -n <namespace>
Symptom Likely cause First check
Apply is rejected Selector and template labels differ, or another manifest field is invalid Server-side dry run
CURRENT below DESIRED Pod creation blocked by quota, admission, permissions, or API errors ReplicaSet Events
CURRENT equals DESIRED, but READY is lower Pods Pending, pulling images, crashing, or failing readiness Pod status, Events, and logs
Extra or unexpected Pods counted Broad selector or adoption of matching unowned Pods Labels and owner references
New image appears only on replacement Pods ReplicaSet template changed without a Deployment rollout Compare images and creation times

For deeper Pod phase and image-pull diagnosis, see Kubernetes Pods and Pod Lifecycle.


When to Use a ReplicaSet Directly

Create a ReplicaSet directly mainly when:

  • you are learning how controllers maintain Pod replicas
  • you need to understand the Deployment → ReplicaSet → Pod chain
  • a rare workflow deliberately skips Deployment rollout features

For normal long-running stateless applications, apply a Deployment and let it manage ReplicaSets for you.


What's Next


References


Summary

A Kubernetes ReplicaSet keeps a desired number of Pods running whose labels match spec.selector. You declare spec.replicas, a required immutable selector, and a Pod template in one apps/v1 manifest, then verify DESIRED, CURRENT, and READY with kubectl get rs after Pods become Ready. Deleting a managed Pod triggers the controller to create a replacement with a new name and UID.

The selector must match spec.template.metadata.labels; a server dry-run catches mismatches before they reach the cluster. kubectl scale changes the live replica count but does not update your manifest file. Changing spec.template on a standalone ReplicaSet does not roll out existing Pods; use a Deployment for controlled replacement. Deployments normally own ReplicaSets and add rollout history and rollback on top of replica reconciliation.


Frequently Asked Questions

1. What does a Kubernetes ReplicaSet do?

A ReplicaSet keeps a specified number of Pods running whose labels match its selector. When matching Pods are deleted or fail, the controller creates replacements from spec.template until the desired count is restored.

2. Should I create a ReplicaSet or a Deployment?

Use a Deployment for normal long-running stateless applications. A Deployment creates and manages ReplicaSets and adds rollout history and rollback. Create a ReplicaSet directly mainly for learning or rare workflows that do not need Deployment rollout features.

3. Why is my ReplicaSet not creating Pods?

If CURRENT is below DESIRED, inspect ReplicaSet Events for quota, admission, permission, or Pod-creation errors. If CURRENT matches DESIRED but READY is lower, inspect the Pods for scheduling, image-pull, container-start, or readiness failures.

4. What happens when I delete a ReplicaSet?

By default kubectl delete removes the ReplicaSet and its managed Pods. Use --cascade=orphan to delete the ReplicaSet object while leaving its Pods running without a controller.

5. Does updating a ReplicaSet Pod template roll out new Pods automatically?

No. Changing spec.template on an existing ReplicaSet does not replace running Pods. Existing Pods keep the old spec; Pods created later after deletion or scale-up use the new template, so one ReplicaSet can temporarily mix template versions. Use a Deployment for controlled replacement.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, networking, and security, delivering robust and efficient solutions for diverse projects.

  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security
  • Cloud Computing
  • Kubernetes
  • Linux
  • Ansible (software)