The short answer: restart all pods of a Deployment with a rolling restart, then wait until it finishes.
shell
1kubectl rollout restart deployment/<deployment_name> -n <namespace>2kubectl rollout status deployment/<deployment_name> -n <namespace>The same command works for statefulset/<name> and daemonset/<name>. The rest of this guide explains what happens under the hood, when other methods make sense, and how to avoid dropped requests during a restart.
Why there is no kubectl restart pod
Docker can stop and start the same container. Kubernetes works differently. A pod is a disposable object: once it's gone, it's gone. Its controller (a ReplicaSet, StatefulSet or DaemonSet) creates a new one with a new name, a new IP and a new UID.
So "restarting a pod" in Kubernetes always means one of two things:
- Replacing the pod. The controller deletes old pods and creates new ones. This is what
rollout restart,delete podand scaling do. - Restarting a container inside the pod. The kubelet does this on its own when a container exits, according to the pod's
restartPolicy. The pod keeps its name and IP, and the RESTARTS counter goes up.
That's also why you can't "restart" a pod you created with kubectl run or a bare pod manifest. Nothing owns it, so nothing recreates it. More on that below.
The same goes for kubectl restart service, which doesn't exist either. A Service is only a stable address in front of pods. To "restart a service", restart the Deployment or StatefulSet behind it.
When you actually need a restart
Most changes don't need a manual restart at all. If you change the image, env vars, resources or anything else in the pod template of a Deployment, Kubernetes starts a rolling update by itself.
A manual restart is useful when the pod template stays the same but the running pods are stale:
- A ConfigMap or Secret used as env vars changed. Env vars are read once when the container starts. Running pods keep the old value until they are replaced.
- The app caches something at startup: certificates, DNS results, database connections, feature flags.
- A process hangs in a way your liveness probe doesn't catch, or leaks memory slowly.
- You reuse an image tag (for example
latest) withimagePullPolicy: Alwaysand want pods to pull the new image.
A container that was OOMKilled or crashed does not need a manual restart. The kubelet restarts it automatically. If it keeps crashing, a restart won't help, see the section on CrashLoopBackOff and OOMKilled.
Set up a test deployment (optional)
If you want to try every command safely first, spin up a local cluster with kind and apply this manifest. It's a small nginx Deployment with a readiness probe, a preStop hook (explained in Method 1) and a Service.
shell
1kind create cluster --name restart-pods2kubectl create namespace demoyaml
1apiVersion: apps/v12kind: Deployment3metadata:4 name: web5 namespace: demo6spec:7 replicas: 38 selector:9 matchLabels:10 app: web11 template:12 metadata:13 labels:14 app: web15 spec:16 containers:17 - name: nginx18 image: nginx:1.3019 ports:20 - containerPort: 8021 readinessProbe:22 httpGet:23 path: /24 port: 8025 periodSeconds: 226 lifecycle:27 preStop:28 sleep:29 seconds: 530---31apiVersion: v132kind: Service33metadata:34 name: web35 namespace: demo36spec:37 selector:38 app: web39 ports:40 - port: 80Save it as web.yaml, apply it and wait for the pods:
shell
1kubectl apply -f web.yaml2kubectl rollout status deployment/web -n demo3kubectl get pods -n demo -l app=webshell
1NAME READY STATUS RESTARTS AGE2web-595478c459-4qhqk 1/1 Running 0 8s3web-595478c459-4w5s7 1/1 Running 0 8s4web-595478c459-g7h4f 1/1 Running 0 8sIn the examples below, replace web and demo with your own names.
Method 1: kubectl rollout restart (recommended)
Restart a Deployment
shell
1kubectl rollout restart deployment/web -n demoshell
1deployment.apps/web restartedkubectl adds an annotation with the current time to the pod template:
shell
1kubectl get deployment web -n demo -o jsonpath='{.spec.template.metadata.annotations}'shell
1{"kubectl.kubernetes.io/restartedAt":"2026-11-02T14:13:42+01:00"}The template changed, so the Deployment creates a new ReplicaSet and moves pods to it according to its RollingUpdate strategy. By default that's maxSurge: 25% and maxUnavailable: 25%.
Kubernetes starts new pods and terminates old ones only as fast as new pods pass the readiness probe. Pods are replaced in batches, not strictly one at a time, and with the default settings at least 75% of replicas stay available during the restart.
Watch the restart with rollout status
shell
1kubectl rollout status deployment/web -n demo --timeout=120sshell
1Waiting for deployment "web" rollout to finish: 1 out of 3 new replicas have been updated...2Waiting for deployment "web" rollout to finish: 2 out of 3 new replicas have been updated...3Waiting for deployment "web" rollout to finish: 1 old replicas are pending termination...4deployment "web" successfully rolled outIf new pods never become ready, rollout status exits with error: timed out waiting for the condition and a non-zero code, so you can use it in CI. The good news: a broken rollout doesn't kill your healthy pods. In our test, a typo in the image tag left one new pod in ErrImagePull while all three old pods kept serving traffic.
Restart StatefulSets and DaemonSets
shell
1kubectl rollout restart statefulset/<name> -n <namespace>2kubectl rollout restart daemonset/<name> -n <namespace>They behave differently from Deployments:
- StatefulSet replaces pods one by one in reverse order (
db-2, thendb-1, thendb-0). The old pod is terminated before the new one with the same name starts, so each replica is briefly down. Your app has to tolerate that, which is normal for clustered databases. - DaemonSet defaults to
maxUnavailable: 1andmaxSurge: 0. On each node the old pod stops before the new one starts, so that node runs without the agent for a few seconds.
Restart all pods in a namespace
Leave out the name to restart all Deployments in a namespace, or filter them with a label selector:
shell
1kubectl rollout restart deployment -n <namespace>2kubectl rollout restart deployment -n <namespace> -l app=apiTo restart every pod that has a controller, list all three workload types in one command:
shell
1kubectl rollout restart deployment,statefulset,daemonset -n <namespace>shell
1deployment.apps/web restarted2statefulset.apps/db restarted3daemonset.apps/agent restartedUse these commands with care: every workload in the namespace rolls at the same time. Standalone pods and Job pods are not touched. If you trigger two restarts of the same object within a second, kubectl refuses with if restart has already been triggered within the past second, please wait before attempting to trigger another.
A paused Deployment can't be restarted
shell
1error: deployments.apps "web" can't restart paused deployment (run rollout resume first)Resume it first:
shell
1kubectl rollout resume deployment/web -n demoRoll back with rollout undo
Each restart or template change creates a new revision. To go back to the previous one, or to a specific revision:
shell
1kubectl rollout history deployment/web -n demo2kubectl rollout undo deployment/web -n demo3kubectl rollout undo deployment/web -n demo --to-revision=<revision>Two things to know.
Deployments keep only the last 10 revisions by default (revisionHistoryLimit: 10), and asking for an older one fails with unable to find specified revision.
If you manage the Deployment with kubectl apply, Helm or GitOps, rollout undo warns that the last-applied configuration is not updated. Treat undo as an emergency button and fix the manifest in Git right after.
Make the restart really zero-downtime
Most guides call rollout restart zero-downtime. That's only true if your pods shut down gracefully.
We ran a request every 200 ms against the test Service during restarts. Without the preStop hook, 3 restarts dropped 1, 4 and 0 requests with Connection refused.
The reason is a race. When a pod gets terminated, the kubelet sends SIGTERM to the container. At the same time, the pod is removed from the Service endpoints. nginx exits on SIGTERM in milliseconds, but kube-proxy on the nodes may still route a few requests to the old IP.
The fix is a short pause before SIGTERM, so the endpoints update first:
yaml
1lifecycle:2 preStop:3 sleep:4 seconds: 5With this hook, 3 restarts in a row dropped 0 requests.
The native sleep action is enabled by default since Kubernetes 1.30. On older clusters use exec with ["sleep", "5"], which needs a sleep binary in the image. Keep terminationGracePeriodSeconds (30 seconds by default) longer than the sleep plus your app's own shutdown time.
Two more conditions for no downtime: a readiness probe, so traffic only goes to pods that are ready, and more than one replica if your app takes long to start.
Method 2: delete pods
Delete one pod
If a pod belongs to a controller, deleting it makes the controller create a replacement with a new name:
shell
1kubectl delete pod <pod_name> -n <namespace>Check the owner first. If the output is empty, the pod is standalone and will be gone for good:
shell
1kubectl get pod <pod_name> -n <namespace> -o jsonpath='{.metadata.ownerReferences[0].kind}'shell
1ReplicaSetDelete pods by label
The label selector uses =, not ::
shell
1kubectl delete pod -l app=web -n demoshell
1pod "web-68946f84f4-qhvzv" deleted from demo namespace2pod "web-68946f84f4-qj8tw" deleted from demo namespace3pod "web-68946f84f4-x6dzz" deleted from demo namespaceWith a colon, the API server rejects the selector:
shell
1Error from server (BadRequest): Unable to find "/v1, Resource=pods" that match label selector "app:web", field selector "": unable to parse requirement: <nil>: Invalid value: "app:web": name part must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric characterDeleting by label terminates all matching pods at once. The ReplicaSet creates replacements right away, but they still need to start and pass the readiness probe.
Without the preStop hook, every run of our test dropped 2 requests. With the hook, the old pods kept serving for 5 more seconds and we saw no errors, but on a slower app that window will not be enough. For a running service, prefer rollout restart.
You may also see advice to delete the ReplicaSet. It works the same way: all its pods are deleted together and the Deployment creates a new ReplicaSet. It has no advantage over rollout restart.
Method 3: scale to zero and back
First record the current number of replicas, then scale down and up again:
shell
1kubectl get deployment <deployment_name> -n <namespace>2kubectl scale deployment/<deployment_name> -n <namespace> --replicas=03kubectl scale deployment/<deployment_name> -n <namespace> --replicas=<replica_count>This always causes downtime. At zero replicas the Service has no endpoints at all. In our test, every request failed between the two commands and until the first new pod became ready.
Note the flag: --replicas=3, without a space after the dashes. -- replicas=3 ends flag parsing, and kubectl fails with error: required flag(s) "replicas" not set.
Use this method only when you need a full stop, for example when two versions of the app must never run at the same time. If a HorizontalPodAutoscaler manages the Deployment, it can fight with your manual scaling.
Method 4: change an env var or a pod template annotation
Any change to the pod template starts a rolling update, so you can trigger a restart by setting a dummy env var:
shell
1kubectl set env deployment/<deployment_name> -n <namespace> RESTARTED_AT="$(date +%s)"This is how people restarted Deployments before rollout restart existed. The downside: the variable stays in the template, and removing it later with RESTARTED_AT- triggers one more rollout.
A cleaner variant is a pod template annotation. This is exactly what rollout restart does, and it's handy in scripts or in Helm charts (for example, an annotation with a checksum of the ConfigMap, so pods roll when the config changes):
shell
1kubectl patch deployment <deployment_name> -n <namespace> -p '{"spec":{"template":{"metadata":{"annotations":{"example.com/restarted-at":"'$(date +%s)'"}}}}}'The annotation has to be in spec.template.metadata. kubectl annotate deployment ... changes the Deployment's own metadata, and in our test it did not restart anything.
Method 5: restart a standalone pod
A pod created without a controller has nothing to recreate it. You have two options.
Recreate the pod from its own spec. The name stays the same, but it's a new pod with a new UID and IP:
shell
1kubectl get pod <pod_name> -n <namespace> -o yaml | kubectl replace --force -f -shell
1pod "debug" deleted from demo namespace2pod/debug replacedRestart only the container by stopping its main process. The kubelet starts it again in the same pod, with the same name and IP:
shell
1kubectl exec <pod_name> -n <namespace> -- sh -c 'kill 1'shell
1NAME READY STATUS RESTARTS AGE IP2web-77bcdd9f96-7ms2f 1/1 Running 1 (8s ago) 58s 10.244.0.109This needs a shell in the image, and the main process has to react to SIGTERM. Many images run a process as PID 1 that ignores signals it has no handler for. It also works for pods managed by a controller, when you want to restart one container without rescheduling the pod.
restartPolicy, CrashLoopBackOff and OOMKilled
restartPolicy
restartPolicy is set per pod and tells the kubelet what to do when a container exits:
Always(default, and the only option for Deployments, StatefulSets and DaemonSets): restart after any exit.OnFailure: restart only on a non-zero exit code. Common for Jobs.Never: don't restart. The pod stays inErrororCompleted.
In our test, a container exiting with code 1 under Never stayed at 0 restarts, while the same container under OnFailure had restarted twice within 40 seconds.
CrashLoopBackOff
If a container keeps exiting, the kubelet restarts it with an exponential delay: 10s, 20s, 40s and so on, capped at 5 minutes. The delay resets after the container runs for 10 minutes without problems. While the kubelet waits, kubectl get pods shows CrashLoopBackOff:
shell
1NAME READY STATUS RESTARTS AGE2oom 0/1 CrashLoopBackOff 5 (2m31s ago) 5m47sCrashLoopBackOff is not an error by itself. It's the kubelet saying "this container keeps dying, I'm slowing down".
Restarting the pod or the Deployment resets nothing important: the new pod crashes the same way. Find the cause instead:
shell
1kubectl logs <pod_name> -n <namespace> --previous2kubectl describe pod <pod_name> -n <namespace>--previous shows the logs of the container that crashed, not the one that is starting now. describe shows the exit code and reason under Last State, and events such as Back-off restarting failed container.
OOMKilled
When a container uses more memory than its limit, the kernel kills it. The reason is OOMKilled with exit code 137:
shell
1kubectl get pod <pod_name> -n <namespace> -o jsonpath='{.status.containerStatuses[0].lastState.terminated.reason}'shell
1OOMKilledThe kubelet restarts the container automatically, so a manual restart adds nothing. If it keeps happening, raise the memory limit or fix the leak. Changing resources in a Deployment starts a rolling update by itself:
shell
1kubectl set resources deployment/<deployment_name> -n <namespace> --limits=memory=256Mi --requests=memory=128MiIn a GitOps setup, change the manifest in Git instead of running set resources by hand.
Which method to use
kubectl rollout restart: the default for Deployments, StatefulSets and DaemonSets. Rolling, can be watched withrollout statusand rolled back withrollout undo.kubectl delete pod: one stuck pod of a Deployment. Avoid deleting all pods by label on a live service.- Scale to 0 and back: only when you need a full stop. Always causes downtime.
- Env var or template annotation: scripts and Helm charts, for example rolling pods when a ConfigMap changes.
replace --forceorkill 1: standalone pods and single containers.
If you clean up the test cluster afterwards:
shell
1kind delete cluster --name restart-podsWrapping up
Kubernetes doesn't restart pods, it replaces them. kubectl rollout restart does that the controlled way, and with a readiness probe and a short preStop sleep it really doesn't drop requests. The other methods have their place, but each comes with a trade-off you should know before you run it on production.
If pods keep needing manual restarts, the cause usually sits in the cluster setup or in the app's resource limits and probes, and our Kubernetes consulting team can help find it. For another hands-on Kubernetes walkthrough, see our guide on deploying the ELK stack on Kubernetes.
