Right, so you’ve got your DaemonSet deployed. It’s happily running its little pod on every node, doing whatever thankless infrastructure task you assigned it. But now you need to change its spec. Maybe you’re updating the container image to patch a vulnerability, or perhaps you’re adding a new volume mount. This is where the updateStrategy rears its head, and you need to understand it because, trust me, the default behavior will bite you when you least expect it.

The updateStrategy field in your DaemonSet spec is your control panel for how this rolling update happens. It’s not like a Deployment where you can just throw a bunch of pods at the cluster and hope for the best. This is a more surgical operation; we’re touching every single node, often ones running critical workloads. We need to be deliberate.

The Two Flavors of Update Strategy

You have two choices here, and one of them is basically a trap for the unwary.

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: my-daemonset
spec:
  updateStrategy:
    type: OnDelete
  # ... rest of the spec

This is the OnDelete strategy. It’s the old-school, manual, “I’ll update when I’m good and ready” approach. When you update the DaemonSet’s spec (e.g., kubectl apply -f new-daemonset.yaml), absolutely nothing happens to the existing pods. They just sit there, running the old version, blissfully unaware of your new manifest. A new pod will only be created with the new spec when the old pod is deleted. This means you, or your CI/CD pipeline, have to manually go and delete each pod one-by-one to trigger the rollout.

It’s clunky, but it exists for a reason: maximum control. You might use this for something like a network CNI plugin where you absolutely cannot afford for two nodes to be updating at the same time. You want to manually cordon and drain a node, delete the pod, let the new one come up, verify everything is working, and then move to the next one. It’s a pain, but sometimes a necessary pain.

apiVersion: apps/v1
kind: DaemonSet
name: my-daemonset
spec:
  updateStrategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1
  # ... rest of the spec

This is the RollingUpdate strategy. It’s what you’ll use 95% of the time. It tells the DaemonSet controller to automatically roll out the new pods without you needing to manually delete the old ones. The key lever you pull here is maxUnavailable. This specifies the maximum number of pods that can be unavailable during the update process. It can be a number (e.g., 1) or a percentage (e.g., 10%).

Think about what happens: the controller needs to update a pod on a node. To do that, it has to kill the old one first. During the brief window between the old pod terminating and the new one becoming Ready, that node is “unavailable” from the DaemonSet’s perspective. maxUnavailable: 1 means the controller will only ever work on one node at a time. It’s the safest, most conservative approach.

Why maxUnavailable is Your Best Friend and Worst Enemy

Setting maxUnavailable: 100% is basically the nuclear option. It tells the controller to blow away every single pod on every single node all at once and then start recreating them. Your cluster will have a very bad, no-good, terrible few seconds. Don’t do this unless you enjoy chaos and pageable alerts.

The percentage-based value is clever but can be tricky. maxUnavailable: 50% on a 3-node cluster is fine (it rounds down to 1). On a 100-node cluster, that’s 50 nodes updating simultaneously. Is your infrastructure ready to handle 50 of its nodes having a critical pod restarted at the exact same moment? For most daemons (log collectors, node exporters), probably not. This is why I almost always recommend using an absolute number (1) instead of a percentage. It’s predictable. It’s safe. It prevents you from accidentally DDoSing your own cluster during a routine update.

The Node Conundrum: Taints, Tolerations, and PodDisruptionBudgets

Here’s the part the manual often glosses over. The DaemonSet controller is smart, but it has to work within the constraints you’ve given it.

If a node is cordoned or has a taint that the new pod spec cannot tolerate, the controller will skip it during the rollout. It won’t touch the existing pod. This is usually what you want. You don’t want it trying to deploy a pod that’s immediately going to be evicted.

But you also need to think about PodDisruptionBudgets (PDBs). If you have a PDB that requires at least 80% of your my-daemonset pods to be available, the DaemonSet controller will honor it. It will calculate whether deleting a pod would violate the PDB and will pause the rollout if it would. This is brilliant for protecting your cluster’s stability but can also completely deadlock your rollout if you’ve set the PDB too strictly. If you require 100% availability, your update will never, ever proceed. The controller can’t kill a pod to start a new one without breaking your rule. It’s a classic “computer says no” situation. Always ensure your PDB minAvailable value is less than 100% of your desired number of pods.