Run kubectl run nginx --image=nginx:1.27 and Kubernetes creates exactly one Pod named nginx. Delete it with kubectl delete pod nginx and it is gone for good — nothing notices, nothing replaces it. A Pod is the smallest deployable unit in Kubernetes: one or more containers that share a network namespace and a set of volumes, scheduled together onto a single node.

That single Pod is a dead end in production. If the container inside it crashes, the kubelet restarts the container in place. If the node reboots, or someone deletes the Pod, nothing recreates it. A bare Pod has no memory of the fact that it is supposed to exist.

What a Deployment adds on top of a Pod

A Deployment is a Kubernetes object that says how many copies of a Pod should exist and how to roll out changes to them. You stop managing Pods directly and describe the desired state instead; a control loop keeps making that state true. It is the same reconciliation model behind everything else in Kubernetes.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web-app
  template:
    metadata:
      labels:
        app: web-app
    spec:
      containers:
        - name: web-app
          image: nginx:1.27
          ports:
            - containerPort: 80
1
2
kubectl apply -f web-app-deployment.yaml
kubectl get pods
1
2
3
4
NAME                       READY   STATUS    RESTARTS   AGE
web-app-7d9f8c7b86-4kxqz   1/1     Running   0          8s
web-app-7d9f8c7b86-9wj2p   1/1     Running   0          8s
web-app-7d9f8c7b86-x2p6t   1/1     Running   0          8s

Three Pods, not one, and none of them are named web-app. That naming pattern (web-app-7d9f8c7b86-4kxqz) is the first sign that something else is managing them.

replicas: 3 is the same instruction you give with docker compose up --scale web=3 in Docker Compose. The difference is that here a controller keeps enforcing the number long after the command exits.

How a Deployment actually creates Pods: the ReplicaSet in between

A Deployment does not create Pods directly. It creates a ReplicaSet, and the ReplicaSet creates the Pods. You can see the whole chain:

1
2
3
kubectl get deployment web-app
kubectl get replicaset -l app=web-app
kubectl get pods -l app=web-app
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
NAME      READY   UP-TO-DATE   AVAILABLE   AGE
web-app   3/3     3            3           2m

NAME                 DESIRED   CURRENT   READY   AGE
web-app-7d9f8c7b86   3         3         3       2m

NAME                       READY   STATUS    RESTARTS   AGE
web-app-7d9f8c7b86-4kxqz   1/1     Running   0          2m
web-app-7d9f8c7b86-9wj2p   1/1     Running   0          2m
web-app-7d9f8c7b86-x2p6t   1/1     Running   0          2m

The ReplicaSet’s name is the Deployment’s name plus a hash of the Pod template (7d9f8c7b86); each Pod’s name is the ReplicaSet’s name plus a random suffix. kubectl describe pod web-app-7d9f8c7b86-4kxqz shows the relationship directly, in the Controlled By field: ReplicaSet/web-app-7d9f8c7b86.

Delete one of those Pods and the count drops to 2. Within seconds a replacement appears. That is the ReplicaSet noticing the gap and creating a new Pod from the same template, not the Deployment acting directly. The ReplicaSet does the watching. The Deployment exists for the one thing a ReplicaSet cannot do on its own: replace a Pod template gradually instead of all at once.

What a rollout does to the ReplicaSets

Change the image and apply it:

1
2
kubectl set image deployment/web-app web-app=nginx:1.27.1
kubectl rollout status deployment/web-app
1
2
3
Waiting for deployment "web-app" rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for deployment "web-app" rollout to finish: 2 out of 3 new replicas have been updated...
deployment "web-app" successfully rolled out

Behind that status line, the Deployment created a second ReplicaSet for the new Pod template and scaled it up while scaling the old one down. With the default RollingUpdate strategy on 3 replicas, that works out to one Pod at a time:

1
kubectl get replicaset -l app=web-app
1
2
3
NAME                 DESIRED   CURRENT   READY   AGE
web-app-7d9f8c7b86   0         0         0       6m
web-app-6b8f6d9c4d   3         3         3       40s

The old ReplicaSet is not deleted. It stays at zero replicas, keeping the previous Pod template on record. That is what makes rollback fast:

1
kubectl rollout undo deployment/web-app

Kubernetes scales the old ReplicaSet back up and the new one down — no rebuild, no re-pull of an old image if it is still cached on the node. kubectl rollout history deployment/web-app --revision=1 still shows exactly which image that revision ran.

When a bare Pod is the right call

A bare Pod is not a mistake in every context, only in most of them:

  1. A one-off diagnostic container you will delete in five minutes: kubectl run debug --image=busybox -it --rm -- sh.
  2. A Job or CronJob, which creates Pods through its own controller because the work has to run to completion rather than stay up.
  3. Inspecting or learning the Pod spec itself, before wrapping it in something that manages it.

Anything meant to stay running through a crash, a node failure or an update goes through a Deployment. Or a StatefulSet, for Pods that need a stable identity and their own storage — a different problem, and a different object. Write the Deployment manifest by default and drop to a bare Pod only for the three cases above.

Pod, ReplicaSet, and Deployment side by side

PodReplicaSetDeployment
What it managesContainersPodsReplicaSets
Replaces a deleted instanceNoYesYes, via its ReplicaSet
Scales to N replicasNoYesYes
Rolling updatesNoNoYes
Rollback to a previous versionNoNoYes
You create it directlyRarelyAlmost neverNormally

Check whether your own Pods have a Deployment behind them

Run this against whatever is already in your cluster:

1
kubectl get deployment,replicaset,pod -l app=<your-label>

If only Pod rows come back, those are bare Pods. That is the gap to close before the next node drain takes the app down with them.