kubectl rollout restart on a StatefulSet or DaemonSet

kubectl rollout restart writes the current time into the pod template's kubectl.kubernetes.io/restartedAt annotation. The template changed, so the controller replaces the pods under its update strategy, as it would after a new image. A StatefulSet replaces one pod at a time, from the highest ordinal down, and waits for each new pod to be Running and Ready. It replaces only some pods, or none, when its updateStrategy is OnDelete, when rollingUpdate.partition is above 0, or when a pod is not Ready. On GKE 1.36 a not-Ready pod stopped our restart before any pod was replaced, and a new pod that never got Ready left it at one pod of three. A DaemonSet replaces one node's pod at a time (maxUnavailable: 1, maxSurge: 0 by default). A PodDisruptionBudget does not hold back a restart; it limits evictions, which is why kubectl drain hangs on Cannot evict pod as it would violate the pod's disruption budget.

By Ostgate · Updated

The test

We ran this on 11 November 2026 on a GKE Autopilot cluster, version 1.36.4-gke.2377000, with kubectl 1.35. The StatefulSet web has three replicas of busybox:1.37, each requesting 50m CPU and 64Mi of memory. Its readiness probe fails while the pod carries the label seo-notready, so a pod can be made not Ready with kubectl label. The container exits on SIGTERM, so a pod stops in about a second. The DaemonSet agent runs the same container on every node; the cluster had 13 to 15 nodes at the time.

What kubectl rollout restart changes

kubectl -n NAMESPACE rollout restart statefulset/web
kubectl -n NAMESPACE get statefulset web -o jsonpath='{.spec.template.metadata.annotations}'
statefulset.apps/web restarted
{"kubectl.kubernetes.io/restartedAt":"2026-11-11T14:41:03+01:00"}

kubectl sets the annotation to time.Now() in RFC 3339, so it carries the local time of the machine you ran it from. That is the whole command: kubectl deletes no pod itself. The same code refuses a paused Deployment with can't restart paused deployment (run rollout resume first). The kubectl reference says only “Resource rollout will be restarted” (kubectl rollout restart), so what happens next depends on the controller's update strategy.

Read the strategy first:

kubectl -n NAMESPACE get statefulset web -o jsonpath='{.spec.updateStrategy}{"\n"}{.spec.podManagementPolicy}{"\n"}'
{"rollingUpdate":{"partition":0},"type":"RollingUpdate"}
OrderedReady

RollingUpdate: every pod, highest ordinal first

With the defaults above, the restart replaced all three pods in 10 seconds. The StatefulSet's events:

13:41:03Z   SuccessfulDelete   Delete Pod web-2 in StatefulSet web successful
13:41:06Z   SuccessfulCreate   Create Pod web-2 in StatefulSet web successful
13:41:07Z   SuccessfulDelete   Delete Pod web-1 in StatefulSet web successful
13:41:09Z   SuccessfulCreate   Create Pod web-1 in StatefulSet web successful
13:41:10Z   SuccessfulDelete   Delete Pod web-0 in StatefulSet web successful
13:41:12Z   SuccessfulCreate   Create Pod web-0 in StatefulSet web successful
kubectl -n NAMESPACE rollout status statefulset/web
Waiting for partitioned roll out to finish: 0 out of 3 new pods have been updated...
Waiting for 1 pods to be ready...
Waiting for partitioned roll out to finish: 1 out of 3 new pods have been updated...
Waiting for 1 pods to be ready...
Waiting for partitioned roll out to finish: 2 out of 3 new pods have been updated...
Waiting for 1 pods to be ready...
partitioned roll out complete: 3 new pods have been updated...

kubectl says “partitioned” for every StatefulSet, because the API fills in partition: 0. The order is the documented one: the controller “will delete and recreate each Pod in the StatefulSet. It will proceed in the same order as Pod termination (from the largest ordinal to the smallest), updating each Pod one at a time”, and it “waits until an updated Pod is Running and Ready prior to updating its predecessor” (StatefulSets). Each old pod is deleted before its replacement is created, so a StatefulSet with one replica has no running pod while the new one starts.

Only one pod restarted: partition

kubectl -n NAMESPACE patch statefulset web --type merge \
  -p '{"spec":{"updateStrategy":{"rollingUpdate":{"partition":2}}}}'
kubectl -n NAMESPACE rollout restart statefulset/web
kubectl -n NAMESPACE rollout status statefulset/web
Waiting for partitioned roll out to finish: 0 out of 1 new pods have been updated...
Waiting for 1 pods to be ready...
partitioned roll out complete: 1 new pods have been updated...
NAME    CREATED                REVISION
web-0   2026-11-11T13:41:12Z   web-b5477fcb6
web-1   2026-11-11T13:41:09Z   web-b5477fcb6
web-2   2026-11-11T13:41:24Z   web-5cbb9d779f

Only web-2 got the new revision, and rollout status calls that complete. When we deleted web-0, it came back on the old revision: “All Pods with an ordinal that is less than the partition will not be updated, and, even if they are deleted, they will be recreated at the previous version” (StatefulSets). A partition left behind after a canary is a common reason a restart touches one pod. Set it back to 0 and the rest follow; ours finished 6 seconds later:

kubectl -n NAMESPACE patch statefulset web --type merge \
  -p '{"spec":{"updateStrategy":{"rollingUpdate":{"partition":0}}}}'

No pod restarted: OnDelete

With updateStrategy: {type: OnDelete} the restart printed statefulset.apps/web restarted and nothing else happened. 45 seconds later all three pods were still on the old revision, and rollout status refused with exit code 1:

error: rollout status is only available for RollingUpdate strategy type

That is the documented behaviour: the controller “will not automatically update the Pods in a StatefulSet. Users must manually delete Pods to cause the controller to create new Pods” (StatefulSets). Delete them one at a time, waiting for each to be Ready:

kubectl -n NAMESPACE delete pod web-1

Our web-1 came back 2 seconds later on the new revision; web-0 and web-2 stayed old until we deleted them or switched the strategy back to RollingUpdate.

A pod that isn't Ready stalls the rollout

An old pod not Ready: nothing restarts

We made web-0 not Ready, then restarted. web-2 and web-1 were Ready, yet the controller replaced no pod at all:

Waiting for 1 pods to be ready...
error: timed out waiting for the condition

After 90 seconds all three pods still had their old creation times and revision. Under the default OrderedReady policy, “Before a scaling operation is applied to a Pod, all of its predecessors must be Running and Ready” (StatefulSets), and our update waited the same way. When web-0 turned Ready again, the waiting rollout ran by itself and replaced all three pods within 8 seconds. Find the pod it waits for:

kubectl -n NAMESPACE get pods -l app=web -o wide
kubectl -n NAMESPACE describe pod web-0

A new pod never gets Ready: one pod restarts

This is the usual “only restarted one pod” report. We changed the template so that new pods fail their readiness probe. web-2 was replaced and stayed READY false, with Readiness probe failed events; web-1 and web-0 were never touched. A rollout restart on top made no difference.

NAME    CREATED                REVISION         READY
web-0   2026-11-11T13:46:01Z   web-77c59d8cbc   true
web-1   2026-11-11T13:45:59Z   web-77c59d8cbc   true
web-2   2026-11-11T13:46:13Z   web-587994888b   false

Fixing the template is not enough. We reverted it, and 60 seconds later web-2 was still the broken pod. The docs describe this as a broken state under OrderedReady: “StatefulSet will stop the rollout and wait”, and “After reverting the template, you must also delete any Pods that StatefulSet had already attempted to run with the bad configuration” (Forced rollback). Deleting it finished the rollout in 8 seconds:

kubectl -n NAMESPACE delete pod web-2

podManagementPolicy Parallel and maxUnavailable

Faster StatefulSet rollouts need two settings. podManagementPolicy: Parallel can only be set when the StatefulSet is created, and with it “when .spec.updateStrategy.rollingUpdate.maxUnavailable is greater than 1, the StatefulSet controller terminates and creates up to maxUnavailable Pods simultaneously”. maxUnavailable is “Beta since Kubernetes v1.37; enabled by default” (StatefulSets). On our GKE 1.36.4 cluster the field didn't stick: kubectl patch answered statefulset.apps/web patched, and reading the strategy back showed no maxUnavailable. We did not test Parallel.

kubectl rollout restart daemonset

kubectl -n NAMESPACE rollout restart daemonset/agent
kubectl -n NAMESPACE rollout status daemonset/agent
Waiting for daemon set "agent" rollout to finish: 0 out of 14 new pods have been updated...
Waiting for daemon set "agent" rollout to finish: 1 out of 14 new pods have been updated...
…
Waiting for daemon set "agent" rollout to finish: 13 of 14 updated pods are available...
daemon set "agent" successfully rolled out

Autopilot accepted the DaemonSet. Its strategy read back as {"rollingUpdate":{"maxSurge":0,"maxUnavailable":1},"type":"RollingUpdate"}, and the restart replaced 14 pods one node at a time in 30 seconds. Raising maxUnavailable replaces more nodes' pods at once. With OnDelete, “new DaemonSet pods will only be created when you manually delete old DaemonSet pods” (Perform a rolling update on a DaemonSet); we did not run that case.

maxSurge starts the new pod before the old one stops, and “can not be 0 if MaxUnavailable is 0”. The API reference warns that “the resources consumed by the daemonset on any given node can double” (DaemonSet API). That stopped our rollout. With maxUnavailable: 0 and maxSurge: 1 it reached 6 out of 13 new pods have been updated and stayed there: the new pod for one full node was Pending with 0/15 nodes are available: 1 Insufficient memory, 14 node(s) didn't satisfy plugin(s) [NodeAffinity], because a DaemonSet pod is pinned to its node and the old pod still held its memory. Setting maxUnavailable: 1, maxSurge: 0 finished the rollout in 18 seconds.

PodDisruptionBudgets and drains

A PodDisruptionBudget limits evictions, and a rollout restart evicts nothing: the controller deletes and creates pods itself. Pods replaced by a rolling update “do count against the disruption budget, but workload resources (such as Deployment and StatefulSet) are not limited by PDBs when doing rolling upgrades” (Disruptions). kubectl drain, GKE node upgrades and anything else that calls the Eviction API wait for the budget.

OperationWaits for the PDB?Source
kubectl rollout restart, a new image, any rolling update No Our runs below; Disruptions
kubectl delete pod, deleting a Deployment No: “deleting deployments or pods bypasses Pod Disruption Budgets” Disruptions
An Eviction through the API Yes, refused with HTTP 429 Our run below
kubectl drain Yes, retries every 5 seconds with no time limit by default kubectl source; Safely drain a node
GKE node upgrade (surge, the only kind on Autopilot) Yes, for up to one hour, then evicts anyway GKE docs

A restart goes through a budget that allows 0

We gave a three-replica Deployment a budget that allows no disruption at all, the case the docs describe as “requiring zero voluntary evictions” (Specifying a Disruption Budget):

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: web
spec:
  minAvailable: 3
  selector:
    matchLabels:
      app: web
kubectl -n NAMESPACE get pdb web
kubectl -n NAMESPACE rollout restart deployment/web
kubectl -n NAMESPACE rollout status deployment/web
NAME   MIN AVAILABLE   MAX UNAVAILABLE   ALLOWED DISRUPTIONS   AGE
web    3               N/A               0                     11s
deployment.apps/web restarted
Waiting for deployment "web" rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for deployment "web" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "web" rollout to finish: 1 old replicas are pending termination...
deployment "web" successfully rolled out

All three pods were replaced in 16 seconds while the budget stayed at ALLOWED DISRUPTIONS 0. The events show each new pod created before an old one was deleted, the Deployment's default surge. A StatefulSet has no surge and deletes first, and it went below the budget without waiting: in our first run it replaced all three pods in 7 seconds under the same minAvailable: 3.

An eviction is refused: Cannot evict pod

An eviction is the request kubectl drain sends for each pod on the node. We sent one for a single pod, which shows the same refusal without touching a node. Save the request as eviction.json:

{"apiVersion":"policy/v1","kind":"Eviction","metadata":{"name":"POD","namespace":"NAMESPACE"}}
kubectl create --raw /api/v1/namespaces/NAMESPACE/pods/POD/eviction -f eviction.json
Error from server (TooManyRequests): Cannot evict pod as it would violate the pod's disruption budget.

The response was HTTP 429, and its cause names the budget: The disruption budget web needs 3 healthy pods and has 3 currently. The pod kept running. The docs list 429 as “not currently allowed because of the configured PodDisruptionBudget” and 500 as a misconfiguration, “like if multiple PodDisruptionBudgets reference the same Pod” (API-initiated eviction).

kubectl drain treats a 429 as temporary. Its source prints the server's message in this form and tries again 5 seconds later:

error when evicting pods/"POD" -n "NAMESPACE" (will retry after 5s): Cannot evict pod as it would violate the pod's disruption budget.

Its --timeout is 0 by default, which means no limit, so a drain against a budget that allows 0 prints that line every 5 seconds until someone stops it. The docs put it the same way: “If you try to drain a Node where an unevictable Pod is running, the drain never completes” (Specifying a Disruption Budget). A budget below the replica count can also block a drain when one of its pods is not Ready, because “Any drains that would cause the number of healthy replicas to fall below the specified budget are blocked” (Safely drain a node). Find the budgets that allow nothing:

kubectl get pdb -A

The fix: let one pod go

Replace minAvailable: 3 with maxUnavailable: 1 in the manifest. A budget holds only one of the two fields, so replace the whole object rather than adding the second field. The docs suggest maxUnavailable 1 for replicated stateful apps because it “works with varying scale of application”; a lower minAvailable, such as 2 of 3, allows one eviction as well.

kubectl -n NAMESPACE replace -f pdb.yaml
kubectl -n NAMESPACE get pdb web
NAME   MIN AVAILABLE   MAX UNAVAILABLE   ALLOWED DISRUPTIONS   AGE
web    N/A             1                 1                     56s

The same eviction then answered 201 Created with "status":"Success", and the Deployment started a replacement pod. A second eviction sent in the same second was refused with needs 2 healthy pods and has 2 currently, because the replacement was not Ready yet. Ten seconds later it was Ready, the budget showed ALLOWED DISRUPTIONS 1 again, and the second eviction went through. A drain's retries get the same answers, so pods under this budget leave one at a time.

If a pod in the budget is stuck not Ready, the docs recommend unhealthyPodEvictionPolicy: AlwaysAllow: running pods that are not yet healthy “can be evicted regardless of whether the criteria in a PDB is met” (Unhealthy Pod Eviction Policy). We did not test that setting.

GKE node upgrades and Autopilot

For surge upgrades GKE will “Drain the existing node, respecting PodDisruptionBudget and GracefulTerminationPeriod settings for up to one hour”, and “After one hour, any remaining Pods are forcefully evicted so that the upgrade can proceed”. GKE uses surge upgrades on Autopilot clusters. A blue-green upgrade, an option on Standard clusters, ends by deleting the old pool: “Unlike eviction, deletion doesn't respect PDBs and forcibly deletes the Pods” (Node pool upgrade strategies). A budget that allows 0 holds each node's upgrade for up to an hour, and then its pods are evicted anyway.

On Autopilot, the annotation cluster-autoscaler.kubernetes.io/safe-to-evict=false on a pod goes further: such pods “are protected from eviction caused by node auto-upgrades or scale-down events for up to seven days” (Autopilot overview). We did not run a node upgrade or a drain: Autopilot nodes are Google's to manage.

How Ostgate does it

In a cluster's tab in Ostgate, Restart… on a Deployment, StatefulSet or DaemonSet replaces its pods under its rollout strategy, as kubectl rollout restart does. The confirmation names the account, project, cluster and namespace, counts the pods that go down at once, and says when an OnDelete StatefulSet would restart nothing. Ostgate asks the cluster first whether your account may restart the workload; when it may not, Restart… is disabled and says which permission is missing (Restart and delete). Delete Pod… deletes one pod, and its controller creates the replacement.

Symptoms and causes

You seeCauseFix
Only the highest-ordinal pod is new; rollout status says complete rollingUpdate.partition above 0 Set the partition to 0
restarted, but no pod changes; rollout status is only available for RollingUpdate strategy type updateStrategy: OnDelete Delete the pods one at a time, or switch to RollingUpdate
No pod changes; Waiting for 1 pods to be ready... An existing pod is not Ready Fix or delete that pod
One new pod stays not Ready, the others are old The new template never becomes Ready Fix the template, then delete the broken pod
maxUnavailable gone from a StatefulSet after patched Dropped on our GKE 1.36 cluster; beta from Kubernetes 1.37 Delete pods yourself, or check again on 1.37
DaemonSet rollout stops; a new pod Pending with Insufficient memory maxSurge needs room for two pods on a full node maxUnavailable: 1, maxSurge: 0
can't restart paused deployment The Deployment is paused kubectl rollout resume first
kubectl drain hangs; Cannot evict pod as it would violate the pod's disruption budget A PDB with ALLOWED DISRUPTIONS 0 maxUnavailable: 1 or a lower minAvailable; fix pods that are not Ready
A GKE node upgrade waits on one node for up to an hour A PDB allows no eviction from that node The same budget fix; after one hour GKE evicts anyway

Questions

How do I restart a rollout?

Run kubectl rollout restart deployment/NAME, statefulset/NAME or daemonset/NAME, then kubectl rollout status on the same object. The command sets the pod template annotation kubectl.kubernetes.io/restartedAt to the current time, and the controller replaces the pods under its update strategy. A paused Deployment needs kubectl rollout resume first.

How do I force a pod to restart?

Delete it: kubectl delete pod NAME. A Deployment, StatefulSet or DaemonSet creates a replacement; a StatefulSet pod comes back with the same name. Below a partition it comes back on the old revision, and under OnDelete deleting is the only way to move a pod to the new template. A pod without a controller is not recreated.

Why does kubectl rollout restart statefulset only restart one pod?

Either rollingUpdate.partition is set, so only ordinals at or above it are replaced, or the first new pod never became Ready, so the ordered rollout waits. In our test on GKE 1.36 a not-Ready web-0 stopped the restart before any pod was replaced, and a new web-2 that failed its readiness probe left web-1 and web-0 untouched until we deleted it. With OnDelete no pod is replaced at all.

Does a PodDisruptionBudget stop kubectl rollout restart?

No. With minAvailable: 3 on three pods and no disruptions allowed, our restart still replaced all three, one at a time. Rolling updates count against the budget but are not limited by it; the budget limits evictions, such as those from kubectl drain.

Why does kubectl drain hang with Cannot evict pod as it would violate the pod's disruption budget?

A PodDisruptionBudget on one of the node's pods allows 0 disruptions: minAvailable equal to the replica count, maxUnavailable: 0, or a budget whose pods are not all Ready. Each eviction gets HTTP 429, and kubectl drain retries every 5 seconds with no time limit unless you pass --timeout. Run kubectl get pdb -A and look for ALLOWED DISRUPTIONS 0, then set maxUnavailable: 1 or a lower minAvailable, or fix the pod that is not Ready. In our test the refused eviction went through as soon as the budget allowed 1.

Ostgate runs on Apple Silicon Macs with macOS 15 or later, with a 7-day trial and no sign-up. Setup is in the GKE docs, and GKE on a Mac lists what the GKE area does.