Kubernetes resource management is moving toward a simpler and more consistent Linux foundation: cgroup v2.
The transition matters because Kubernetes relies on Linux control groups to enforce CPU and memory limits, account for resource usage, and isolate workloads on shared nodes.
For developers, the migration can appear invisible. A Deployment still contains:
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "1"
memory: "512Mi"But underneath that configuration, the Linux resource-management model is changing.
That creates an important engineering question:
What should Kubernetes teams test when moving workloads from cgroup v1 to cgroup v2?
The answer is broader than checking whether Pods start.
A production migration should validate the complete path:
Kubernetes
|
v
Kubelet
|
v
Container Runtime
|
v
cgroup v2
|
v
Linux Kernel
|
v
ApplicationThe biggest risks are usually not Kubernetes manifests themselves. They are older runtimes, monitoring agents, security tools, native software, and custom scripts that make assumptions about the old cgroup interface.
Why cgroup v2 Matters
cgroup v1 uses separate hierarchies for different resource controllers.
cgroup v2 provides a unified hierarchy.
That architectural difference gives Linux and Kubernetes a more consistent way to manage resources.
Conceptually:
cgroup v1
CPU hierarchy
Memory hierarchy
Device hierarchy
Other controllers
|
v
Multiple resource treesversus:
cgroup v2
Unified hierarchy
|
+--> CPU
+--> Memory
+--> I/O
+--> Other controllersKubernetes has supported cgroup v2 as stable since Kubernetes 1.25. Kubernetes 1.35 deprecates cgroup v1, making the migration increasingly important for cluster operators. (kubernetes.io)
Kubernetes Still Uses the Same Resource Concepts
Moving to cgroup v2 does not eliminate Kubernetes resource requests and limits.
For example:
resources:
requests:
cpu: "500m"
memory: "512Mi"
limits:
cpu: "1"
memory: "1Gi"The meaning remains familiar:
Request
|
v
Scheduling decision
Limit
|
v
Runtime resource constraintThe underlying implementation changes.
That distinction is important because application teams generally do not need to rewrite every Deployment just because nodes move to cgroup v2.
The migration is primarily an infrastructure compatibility exercise.
The First Thing to Check: Your Nodes
Do not assume your entire cluster uses one cgroup version.
Clusters can contain node pools created at different times.
A simple Linux check is:
stat -fc %T /sys/fs/cgroup/A cgroup v2 system reports:
cgroup2fsYou can also inspect mounts:
mount | grep cgroupThe key distinction is:
Cluster version
!=
Node cgroup versionThis is particularly important in managed Kubernetes environments where existing node pools may retain older configuration.
The cgroup Driver Is Not the Same as the cgroup Version
Another common migration mistake is treating these as the same setting.
There are two separate concepts:
cgroup version
|
+--> v1
+--> v2and:
cgroup driver
|
+--> systemd
+--> cgroupfsFor modern Kubernetes systems using systemd, the recommended configuration is generally the systemd cgroup driver.
A kubelet configuration can look like:
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
cgroupDriver: systemdThe container runtime must be configured consistently.
A healthy node therefore has a coherent relationship:
systemd
|
+--> kubelet
|
+--> container runtime
|
v
cgroup v2Kubernetes recommends the systemd driver when systemd is the init system. (kubernetes.io)
Why Old Tools Can Break
The most important compatibility issue is software that reads the cgroup filesystem directly.
An application may contain code such as:
/sys/fs/cgroup/memory/...That path reflects the older cgroup v1 model.
cgroup v2 uses a unified hierarchy with different files and semantics.
The result can be:
Application
|
v
Assumes cgroup v1
|
v
Looks for old path
|
v
Path does not existThe application may crash.
Or worse, it may silently fall back to host-level resource information.
That can cause incorrect memory or CPU decisions.
Container Resource Detection Is Critical
Consider a Kubernetes node with:
Host memory = 64 GBand a Pod with:
limits:
memory: "1Gi"The application should understand that it has approximately a 1 GiB memory limit.
If it incorrectly believes the host has 64 GB available, it can allocate far more memory than Kubernetes intended.
That can produce:
Excessive allocation
|
v
Memory pressure
|
v
OOM kill
|
v
Pod restartThis is why runtime compatibility matters.
The application does not need to know that cgroup v2 exists, but the runtime may need to understand it.
Check .NET Runtime Behavior
For C# and .NET workloads, test container resource behavior explicitly.
A .NET application might run with:
resources:
requests:
cpu: "250m"
memory: "512Mi"
limits:
cpu: "1"
memory: "1Gi"Then test:
Normal workload
High allocation rate
CPU saturation
Memory pressure
Garbage collection
Pod restart
Horizontal scalingDo not rely solely on:
kubectl get podsA Pod being Running only proves that the process is alive.
It does not prove that resource limits are being interpreted correctly.
Java Has Similar Concerns
Java applications have historically required explicit container-awareness improvements.
Modern JDK versions have strong support for cgroup v2, but applications running older Java versions should be checked before migration.
A Java runtime may use container limits to determine:
Heap size
CPU count
Garbage collection configuration
Thread behaviorIf those values are calculated incorrectly, the application can behave badly under Kubernetes resource constraints.
This is particularly important for memory-heavy JVM applications.
Node.js Requires Runtime Awareness
Node.js applications can also depend on container resource detection.
For example:
Kubernetes memory limit
|
v
cgroup v2
|
v
libuv
|
v
Node.js runtime
|
v
V8 heap configurationIf the runtime does not correctly detect the container limit, V8 may use inappropriate memory assumptions.
This is one reason runtime upgrades should be part of cgroup migration planning.
A cluster upgrade can expose problems that were previously hidden by the older cgroup implementation.
Monitoring Is Often the Biggest Risk
Application developers may never inspect /sys/fs/cgroup.
Monitoring software often does.
A node may contain:
Metrics agent
APM agent
Security agent
Container runtime monitor
Log collector
Custom daemonThese components can use cgroup information to determine:
Which container owns this process?
How much CPU is it consuming?
How much memory is it using?
Which Pod generated this event?If the agent expects cgroup v1 paths, metrics can disappear or become incorrect after migration.
The dangerous scenario is not:
Monitoring crashesIt is:
Monitoring continues
but reports incorrect dataThat can hide production incidents.
Validate Your Observability Stack
Before migration, inventory:
Prometheus collectors
OpenTelemetry agents
APM agents
Node exporters
Cloud monitoring agents
Security monitoring
Custom exportersThen check whether each component supports cgroup v2.
Test:
CPU metrics
Memory metrics
Container identification
Pod identification
OOM events
Node pressure
Filesystem metricsCompare the output before and after migration.
For example:
Before:
Pod A memory = 420 MiB
After:
Pod A memory = 420 MiBIf the post-migration value suddenly becomes:
Host memory = 32 GiByou have found a resource-accounting problem.
Security Agents Need the Same Validation
Security tooling can also depend on cgroups.
Runtime security systems may associate processes with containers using cgroup information.
For example:
Linux process
|
v
cgroup identifier
|
v
Container
|
v
PodIf that relationship breaks, the security system may:
fail to attribute events
report incorrect container names
lose process visibility
generate false alerts
miss expected activity
Security validation should therefore be part of the migration checklist.
CPU Behavior Should Be Benchmarked
cgroup v2 changes the underlying CPU-control implementation.
cgroup v1 uses CPU shares.
cgroup v2 uses CPU weight.
Kubernetes has documented work to improve conversion between the two models so that existing Kubernetes resource specifications produce more consistent behavior during migration. (kubernetes.io)
For most workloads, this should not require application changes.
But CPU-sensitive systems should still be tested.
Examples include:
High-frequency APIs
CPU-bound workers
Batch processing
Video encoding
Compilation workloads
Latency-sensitive servicesRun workloads under controlled contention:
Pod A: CPU request = 1
Pod B: CPU request = 1
Pod C: CPU request = 2Then measure actual CPU allocation and application latency.
Memory Behavior Is Even More Important
Memory limits should be tested under pressure.
Suppose:
resources:
limits:
memory: "512Mi"A realistic test should deliberately push the application toward that boundary.
Measure:
Working set
RSS
Managed heap
GC activity
Allocation rate
OOM events
Restart count
Request latencyFor a .NET service, for example:
Traffic increases
|
v
Allocations increase
|
v
GC pressure increases
|
v
Memory approaches limit
|
v
Application remains stableYou want to verify the behavior before production.
cgroup v2 Enables Better Memory Management
One reason Kubernetes is moving toward cgroup v2 is that it provides stronger primitives for resource management.
Kubernetes can use cgroup v2 capabilities for features such as MemoryQoS.
The broader model is:
Pod
|
+--> memory.min
+--> memory.high
+--> memory.maxThese controls provide more nuanced memory behavior than simply allowing a process to consume memory until it is killed.
The exact behavior depends on Kubernetes configuration, kernel capabilities, and the workload.
The important point is that cgroup v2 gives Kubernetes more useful controls over memory pressure.
Pressure Stall Information
Another important capability is Pressure Stall Information, commonly called PSI.
Traditional CPU monitoring might show:
CPU utilization = 80%PSI asks a different question:
How much time are processes waiting because CPU resources are unavailable?
The distinction matters.
For example:
CPU utilization = 70%
CPU pressure = highcan be possible when contention causes workloads to spend significant time waiting.
This gives operators another signal for understanding node-level resource pressure.
Autoscaling Can Benefit From Better Signals
Resource management and autoscaling are related.
Suppose an application has:
CPU = 40%
Request latency = highCPU alone might suggest that no scaling is necessary.
But another metric could show:
CPU pressure = highor:
Requests waiting = highThis demonstrates why Kubernetes resource utilization should not be viewed in isolation.
A robust architecture can combine:
CPU
Memory
Application metrics
Pressure metrics
Queue depth
Latencywith the appropriate autoscaling mechanisms.
GKE's Migration Is Already Underway
Google Kubernetes Engine has been moving toward cgroup v2 for several releases.
GKE documentation states that new nodes use cgroup v2 by default from GKE 1.26 onward. Existing cgroup v1 nodes do not automatically change simply because the cluster is upgraded to 1.26.
GKE began automatically migrating remaining cgroup v1 clusters starting with GKE 1.33, and cgroup v1 support is scheduled for removal in GKE 1.35. (cloud.google.com)
This creates a practical timeline:
Older node pools
|
v
cgroup v1
|
v
Migration
|
v
cgroup v2
|
v
cgroup v1 removalTeams should therefore identify legacy nodes before the platform forces the migration.
Standard Versus Autopilot
The migration process differs depending on the type of GKE environment.
With Autopilot, Google manages much more of the node infrastructure.
With Standard clusters, teams have more control over:
Node pools
Node configuration
Runtime settings
System software
SchedulingThat also means Standard users have more responsibility for compatibility.
Do not copy a node-level migration procedure from a Standard cluster into an Autopilot environment without checking the platform-specific behavior.
A Safer GKE Migration Pattern
For Standard clusters, a controlled node-pool migration is usually easier to reason about than changing every node simultaneously.
The pattern is:
Existing Node Pool
|
v
Create cgroup v2 Node Pool
|
v
Validate Nodes
|
v
Deploy Test Workloads
|
v
Run Integration Tests
|
v
Run Load Tests
|
v
Move Production Workloads
|
v
Drain Old Nodes
|
v
Remove Old PoolThis gives you a rollback option.
If something fails:
cgroup v2 node
|
v
Unexpected behavior
|
v
Move workload back
|
v
InvestigateThat is significantly safer than making a cluster-wide change without a fallback.
Pod Disruption Budgets Matter
Node migration normally involves draining nodes.
That means Kubernetes may need to evict Pods.
If an application has:
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: api-pdb
spec:
minAvailable: 2
selector:
matchLabels:
app: apithe migration must respect the application's availability requirements.
A poorly configured PodDisruptionBudget can also make node migration extremely slow.
The migration plan should therefore consider:
Replicas
PodDisruptionBudget
Topology spread
Anti-affinity
Node capacity
Startup time
Readiness probesReadiness Probes Become Important
During node replacement, Pods restart or move.
A good readiness probe ensures traffic reaches the new Pod only after the application is actually ready.
For example:
readinessProbe:
httpGet:
path: /health/ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5The exact values depend on the application.
The migration should test:
Old Pod terminating
|
v
New Pod starting
|
v
Readiness check
|
v
Traffic enabledThis prevents infrastructure migration from becoming an application outage.
Check Horizontal Pod Autoscaler Behavior
Autoscaling should also be tested.
A workload may use:
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70Run load against the application before and after migration.
Verify:
Desired replicas
Current replicas
CPU utilization
Scale-up time
Scale-down time
Metric availabilityThe cgroup migration should not unexpectedly change the application's perceived CPU behavior enough to destabilize HPA.
Test Cluster Autoscaler Too
If HPA increases replicas, the cluster may need more nodes.
That creates another control loop:
Traffic increases
|
v
HPA increases Pods
|
v
Insufficient node capacity
|
v
Cluster Autoscaler adds nodes
|
v
Pods become schedulableThe migration should test this entire chain.
A node-level resource-management change can otherwise expose capacity assumptions that were not visible under normal traffic.
Do Not Forget DaemonSets
Node-level agents commonly run as DaemonSets.
Examples:
Monitoring
Logging
Security
Networking
StorageThese are exactly the workloads most likely to interact with node-level Linux features.
Before migration, identify every DaemonSet:
kubectl get daemonsets -AThen classify them:
Application-independent
Node monitoring
Security
Networking
Storage
Custom infrastructurePay special attention to anything that mounts:
/sys
/proc
/dev
/sys/fs/cgroupA Pod mounting the cgroup filesystem is a strong signal that its compatibility should be checked.
Search Your Own Code for cgroup Assumptions
Do not rely only on vendor documentation.
Search your repositories for:
/sys/fs/cgroupand:
cgroupFor example:
grep -R "/sys/fs/cgroup" .For PowerShell:
Get-ChildItem -Recurse | Select-String "/sys/fs/cgroup"Also inspect shell scripts used by:
Node initialization
Health checks
Monitoring
Deployment automation
DiagnosticsA ten-line shell script written years ago can be the most fragile part of a cgroup migration.
Container Image Testing
Build your application image using the same runtime and operating-system base used in production.
For example:
FROM mcr.microsoft.com/dotnet/aspnet:10.0
WORKDIR /app
COPY ./publish .
ENTRYPOINT ["dotnet", "Orders.Api.dll"]The specific runtime version should match your application's supported configuration.
Then run the image under realistic Kubernetes limits.
Do not conclude that the image is compatible simply because:
docker run ...works on a developer laptop.
The important environment is:
Linux node
+
container runtime
+
cgroup v2
+
Kubernetes resource limitsCommon Mistakes
Assuming cgroup v2 requires application rewrites
Most applications do not need code changes.
The main issue is compatibility with software that interacts directly with cgroups.
Checking only Pod health
A Running Pod does not prove that CPU and memory accounting are correct.
Ignoring node-level agents
Monitoring and security tools can be more sensitive than the application itself.
Mixing cgroup version and cgroup driver
They are different concepts.
Migrating all nodes simultaneously
A progressive node-pool migration provides better control.
Forgetting runtime versions
Old runtimes may not correctly detect container resource limits.
Ignoring memory pressure
Normal traffic is not enough to validate resource behavior.
Testing only application containers
DaemonSets and system components need validation too.
Changing the runtime and application at the same time
Keep the migration isolated so failures are easier to diagnose.
Advantages and Disadvantages
Advantages
Better resource model: cgroup v2 provides a unified Linux hierarchy.
Improved memory control: Kubernetes can use newer memory-management primitives.
Better diagnostics: PSI can provide additional visibility into resource contention.
Long-term Kubernetes direction: cgroup v2 is the platform's strategic path as cgroup v1 is deprecated.
More consistent runtime behavior: Modern runtimes can use container limits more accurately.
Disadvantages
Migration effort: Existing clusters and node pools may require controlled replacement.
Tool compatibility: Older monitoring and security software may depend on cgroup v1.
Runtime dependencies: Old language runtimes may have incorrect resource detection.
Operational complexity: Node replacement affects scheduling, availability, and capacity.
Debugging differences: Operators familiar with cgroup v1 must learn the new hierarchy and semantics.
A Practical Migration Checklist
Before migration:
[ ] Identify Kubernetes version
[ ] Identify node cgroup version
[ ] Identify cgroup driver
[ ] Identify container runtime
[ ] Inventory DaemonSets
[ ] Inventory monitoring agents
[ ] Inventory security agents
[ ] Check language runtime versions
[ ] Search application code for cgroup access
[ ] Check container imagesDuring migration:
[ ] Create cgroup v2 node pool
[ ] Verify node configuration
[ ] Run representative workloads
[ ] Test CPU limits
[ ] Test memory limits
[ ] Test OOM behavior
[ ] Verify monitoring
[ ] Verify security tooling
[ ] Test HPA
[ ] Test Cluster Autoscaler
[ ] Validate PodDisruptionBudgetsAfter migration:
[ ] Compare resource metrics
[ ] Compare application latency
[ ] Compare restart rates
[ ] Review node pressure
[ ] Review security events
[ ] Monitor for runtime warnings
[ ] Remove legacy cgroup v1 nodesWhat C# Developers Should Actually Do
For most .NET teams, the migration should be treated as a platform compatibility test, not a code migration.
Focus on these areas:
.NET runtime
|
+--> Memory detection
+--> GC behavior
+--> CPU behavior
+--> Thread scheduling
+--> Container limits
+--> OOM behaviorRun the same workload on:
cgroup v1 node
vs.
cgroup v2 nodeCompare:
Memory usage
CPU usage
P95 latency
P99 latency
GC pauses
Throughput
Restart behaviorIf the results are equivalent or better, you have strong evidence that the application is compatible.
If they differ significantly, investigate the runtime and resource-management behavior before migrating the entire fleet.
Summary
The move from cgroup v1 to cgroup v2 is fundamentally an infrastructure change, but it can affect application behavior indirectly through runtimes, monitoring systems, security tools, and resource accounting.
Kubernetes has already established cgroup v2 as the long-term direction, and cgroup v1 is being deprecated. GKE has likewise been moving new nodes to cgroup v2 and has a defined migration path for older environments. (kubernetes.io)
The safest approach is progressive migration:
Inventory
|
v
Compatibility testing
|
v
New cgroup v2 nodes
|
v
Representative workloads
|
v
Production migration
|
v
Retire cgroup v1Most application code should remain unchanged.
The areas that deserve the most attention are software that reads /sys/fs/cgroup, runtimes that detect container resources, monitoring and security agents, and workloads that operate close to CPU or memory limits.
For production Kubernetes environments, cgroup v2 should therefore be treated neither as a purely invisible platform change nor as a reason to rewrite applications.
It is a node-level migration that requires application-level validation.
The earlier you identify cgroup v1 assumptions, the easier the eventual migration becomes.

Join the conversation! Your thoughts help the community grow.