# Kubelet Won't Start After Upgrading to Kubernetes 1.35? It's cgroup v1

URL: https://www.devopsness.com/blog/kubelet-cgroup-v1-kubernetes-1-35
Author: Kiril Urbonas
Published: 2026-10-28
Updated: 2026-10-28
Categories: Infrastructure
Tags: Kubernetes, Linux, Cgroups, Troubleshooting, Kubelet

Summary: Since Kubernetes 1.35 the kubelet refuses to start on cgroup v1 nodes by default. How to check every node in one command, the safe migration, and the temporary override with an expiry date.

---
# Kubelet Won't Start After Upgrading to Kubernetes 1.35? It's cgroup v1

If a node went `NotReady` after upgrading to Kubernetes 1.35 or later and the kubelet won't stay up, check the cgroup version first. Since v1.35, the kubelet setting `failCgroupV1` defaults to `true`, so the kubelet won't start on a cgroup v1 node.

For kubeadm clusters the failure arrives even earlier: the preflight check now errors during `kubeadm init`, `join` and `upgrade` when it detects cgroup v1 with kubelet 1.35 or later.

This is not a bug, and it's not going away. The Kubernetes project moved cgroup v1 support into maintenance mode in v1.31, and the official blog post from October 6, 2026 says the v1 fallback is scheduled for removal in v1.38.

## Confirm it in ten seconds

On the node:

```bash
$ stat -fc %T /sys/fs/cgroup/
cgroup2fs          # cgroup v2: fine
tmpfs              # cgroup v1 (or hybrid): this is your problem

$ journalctl -u kubelet --since "15 min ago" | grep -i cgroup
```

To check a whole fleet before upgrading, run the same `stat` through your configuration tool. Without SSH access, a debug Pod on the node works one node at a time:

```bash
# Every host in an Ansible inventory
$ ansible k8s_nodes -m command -a "stat -fc %T /sys/fs/cgroup/"

# One node, no SSH: the debug Pod mounts the host filesystem at /host
$ kubectl debug node/worker-3 -it --image=busybox:1.36 -- \
    chroot /host stat -fc %T /sys/fs/cgroup/
```

The debug Pods stay behind afterward, so delete them when you're done.

## Why your node is still on cgroup v1

cgroup v2 has been stable in Kubernetes since v1.25, and most current distributions boot with it by default. Nodes end up on v1 for a few predictable reasons:

| Cause | How to spot it | Fix |
|---|---|---|
| Older OS image | Long-lived node image or an older LTS release | Move to a current image that defaults to v2 |
| Kernel boot parameter forcing v1 | `systemd.unified_cgroup_hierarchy=0` in `/proc/cmdline` | Remove the parameter, reboot |
| Kernel older than 5.8 | `uname -r` | Kubernetes documents 5.8 as the minimum for cgroup v2 |
| Old container runtime | `containerd --version` | containerd 1.4+ supports v2; 2.0+ adds automatic cgroup driver detection; CRI-O 1.20+ |

On managed node pools, the answer is almost always the node image. Roll a new node group on a current image, then drain and remove the old one.

## Migrate safely, one node pool at a time

The switch happens at boot, so treat it like any node replacement:

1. **Check the runtime and driver.** The kubelet and the container runtime must use the same cgroup driver. For kubeadm clusters the project recommends `systemd`. With containerd 2.0+ or CRI-O 1.28+, the kubelet detects the runtime's driver automatically.
2. **Check software that reads cgroups directly.** Monitoring agents and language runtimes that size memory and CPU from cgroup files need versions that understand v2. The official migration guidance lists compatible cAdvisor, Java, Node.js and `automaxprocs` versions; older JVMs are the classic case of an app suddenly misjudging its memory limit.
3. **Roll the nodes.** Cordon and drain one node, boot it on cgroup v2 (new image, or the boot parameter removed), confirm `cgroup2fs`, uncordon. Watch for evictions and OOMs before moving to the next. Respect your [PodDisruptionBudgets during drains](/blog/kubernetes-pod-disruption-budgets-node-drains).
4. **Upgrade Kubernetes last.** Once every Linux node reports `cgroup2fs`, the 1.35+ upgrade is a non-event.

What you get in return is real. Memory QoS, the PSI pressure metrics the kubelet now exposes, and container-aware OOM handling all require cgroup v2. Our post on [cgroup v2 limits](/blog/cgroup-v2-limits-what-constrains-containers) explains what actually constrains a container.

## The temporary override, and its expiry

If a node can't move yet, you can opt out in the kubelet configuration file:

```yaml
# /var/lib/kubelet/config.yaml
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
failCgroupV1: false
```

Restart the kubelet after editing. Treat this as a dated exception with an owner, not a fix: the v1 fallback is scheduled for removal in v1.38, and at that point the override stops being an option.

## Questions people ask

### Why does the kubelet fail to start after upgrading to Kubernetes 1.35?

Most often because the node still uses cgroup v1. From Kubernetes 1.35, the kubelet's `failCgroupV1` setting defaults to true, so it refuses to start on cgroup v1 nodes, and kubeadm preflight checks fail. Run `stat -fc %T /sys/fs/cgroup/`; anything other than `cgroup2fs` means you need to migrate.

### How do I check if a Linux node uses cgroup v2?

Run `stat -fc %T /sys/fs/cgroup/` on the node. It prints `cgroup2fs` on cgroup v2 and `tmpfs` on cgroup v1 or hybrid setups. You can also check `/proc/cmdline` for `systemd.unified_cgroup_hierarchy=0`, which forces the older v1 layout.

### When will Kubernetes remove cgroup v1 support completely?

The Kubernetes blog from October 2026 says the cgroup v1 fallback remains available in v1.36 and is scheduled for removal in v1.38, tracked in KEP-5573. Until then, `failCgroupV1: false` in the kubelet configuration lets a v1 node start, but it's meant only as a short-term bridge.

## The decision, concretely

- **Still below 1.35?** Migrate every Linux node to cgroup v2 first, then upgrade.
- **Already on 1.35 with a node down?** Set `failCgroupV1: false` to restore it, and file a ticket to migrate that node.
- **Managed node groups?** Replace the node image rather than editing boot parameters by hand.
- **Running JVM or older monitoring agents?** Upgrade them before switching, or you'll trade a kubelet error for OOMKills.

## The call we'd make

Run the `stat` check across every node this week, regardless of your current version. Nodes on cgroup v1 are a dated liability with a known removal version, and migrating them during a planned node rotation costs far less than discovering them during an emergency upgrade.

## Sources

- [Kubernetes blog: The shift to cgroup v2 in Kubernetes (October 6, 2026)](https://kubernetes.io/blog/2026/10/06/kubernetes-cgroups-v2-shift/)
- [KEP-5573: Remove cgroup v1 support](https://www.kubernetes.dev/resources/keps/5573/)
- [Kubernetes documentation: About cgroup v2](https://kubernetes.io/docs/concepts/architecture/cgroups/)

