Kubernetes minor-version upgrades are often treated as infrastructure maintenance.
For a .NET team, that can be misleading.
A Kubernetes upgrade can change the environment in which an ASP.NET Core application runs:
Kubernetes
↓
Kubelet
↓
Container Runtime
↓
Linux
↓
.NET Runtime
↓
ASP.NET Core
↓
Application
A change at any layer can appear as an application problem.
The Kubernetes 1.37 release is currently scheduled for August 26, 2026. The release candidate was published on August 5, and the project is currently in the final stages of the release cycle.
That makes the period before the release a good time to validate production clusters.
For .NET services, the biggest upgrade risks are not necessarily changes to C# code. They are operational issues involving node compatibility, container runtimes, cgroup configuration, pod lifecycle, health probes, resource behavior, and workload disruption.
What Changes During a Kubernetes Upgrade?
A Kubernetes minor upgrade can involve several components:
Control Plane
↓
API Server
Controller Manager
Scheduler
↓
Worker Nodes
↓
Kubelet
Container Runtime
For a kubeadm-managed cluster, Kubernetes explicitly recommends upgrading one minor version at a time rather than skipping minor versions. The documented upgrade procedure also notes that containers are restarted during an upgrade because the container specification hash changes.
That restart behavior matters for .NET services.
Even if the application image itself does not change, your application processes can still restart as nodes and cluster components are upgraded.
First Risk: Kubernetes 1.37 Is a Pre-Release Planning Target
As of this writing, Kubernetes 1.37 has not reached general availability.
The official schedule lists:
1.37.0-rc.0
↓
August 5, 2026
1.37.0-rc.1
↓
August 19, 2026
1.37.0
↓
August 26, 2026
These dates come from the Kubernetes release schedule and can change if the release process changes.
Therefore, production teams should distinguish between:
Testing against 1.37 RC
and:
Production upgrade to 1.37.0
Do not automatically promote a release candidate to production simply because application tests pass.
Risk 1: Container Runtime and cgroup Compatibility
One of the most important node-level checks is the relationship between:
Kubelet
+
Container Runtime
+
cgroup Driver
Kubernetes documents that the kubelet and container runtime must use compatible cgroup-driver configuration. The systemd driver is recommended when using cgroup v2.
Check the node:
stat -fc %T /sys/fs/cgroup/
For cgroup v2, Kubernetes documents:
cgroup2fs
For cgroup v1:
tmpfs
Kubernetes has deprecated cgroup v1, and kubelet does not start on cgroup-v1 nodes by default unless the administrator explicitly changes the failCgroupV1 setting.
For an upgrade, this should be a node-inventory check rather than something discovered after a reboot.
Check the Container Runtime Before Upgrading
Inspect the runtime:
kubectl get nodes \
-o custom-columns=NAME:.metadata.name,VERSION:.status.nodeInfo.kubeletVersion,RUNTIME:.status.nodeInfo.containerRuntimeVersion
For containerd, inspect:
containerd --version
and:
sudo grep -n "SystemdCgroup" \
/etc/containerd/config.toml
Kubernetes documentation distinguishes the configuration structure for containerd 1.x and 2.x and recommends matching the runtime and kubelet cgroup-driver configuration.
Do not assume that every node in the cluster has identical runtime configuration.
Mixed node pools are common in long-lived clusters.
Risk 2: cgroup v1 Nodes
A cluster can appear healthy while containing old nodes.
That becomes dangerous during an upgrade.
Suppose:
Node A → cgroup v2
Node B → cgroup v2
Node C → cgroup v1
The cluster may operate normally until Node C is rebooted or replaced.
A Kubernetes upgrade is exactly the kind of event that can expose this configuration drift.
Find affected nodes before upgrading:
kubectl get nodes -o wide
Then inspect each Linux node:
stat -fc %T /sys/fs/cgroup/
Do not rely only on the control-plane version.
Risk 3: .NET Pods Will Restart
A Kubernetes node upgrade normally involves draining and rescheduling workloads.
For kubeadm upgrades, Kubernetes documents that containers are restarted after an upgrade because the container specification hash changes.
For an ASP.NET Core application:
Pod
↓
SIGTERM / shutdown
↓
Application stops
↓
Pod terminates
↓
New Pod starts
↓
Readiness probe passes
↓
Traffic resumes
Your application therefore needs to handle startup and shutdown correctly.
Risk 4: ASP.NET Core Graceful Shutdown
ASP.NET Core provides host lifecycle mechanisms for graceful shutdown.
The .NET Generic Host listens for termination signals through ConsoleLifetime, allowing the application to begin a graceful shutdown. During shutdown, the host stops accepting new connections and allows existing requests to complete within the configured shutdown period.
This matters when Kubernetes drains a node.
A service should not assume that:
Pod termination
=
instant process termination
Instead, test the complete lifecycle.
For example:
Request in progress
↓
Pod receives termination
↓
Application begins shutdown
↓
Existing request completes
↓
Process exits
.NET 10 Adds Another Shutdown Consideration
.NET 10 changed the default runtime behavior around Unix termination signals. The runtime itself no longer installs the previous default SIGTERM handler.
Microsoft notes that typical ASP.NET Core applications do not require action because higher-level hosting APIs such as ConsoleLifetime handle application-model-specific termination behavior.
This creates an important upgrade-testing rule:
Test the actual application entry point, not an isolated console process.
If your service uses custom process-level signal handling, verify that behavior explicitly.
Risk 5: Readiness and Liveness Probes
A Kubernetes upgrade can restart pods faster than a poorly configured application can become ready.
Consider:
readinessProbe:
httpGet:
path: /health/ready
port: 8080
and:
livenessProbe:
httpGet:
path: /health/live
port: 8080
These should not necessarily perform the same checks.
Microsoft recommends separating readiness and liveness semantics in ASP.NET Core. Readiness indicates whether the application can receive requests, while liveness indicates whether the application is functioning sufficiently to avoid being restarted.
A useful model is:
/health/live
↓
"Is the process functioning?"
/health/ready
↓
"Can this instance receive traffic?"
Avoid Using Database Health for Liveness
A common configuration is:
Liveness
↓
Database check
↓
Database unavailable
↓
Restart application
This can create a restart loop.
If the database is temporarily unavailable, restarting every application replica can make the incident worse.
Instead:
Liveness
↓
Process/application health
Readiness
↓
Can this instance serve requests?
The exact dependency checks should be based on your architecture.
Risk 6: Startup Time
.NET services may perform significant startup work:
Configuration
Database migration checks
Cache initialization
Certificate loading
Dependency discovery
Message-broker connection
If the readiness probe starts too early:
Pod starts
↓
Readiness fails
↓
Traffic delayed
That may be acceptable.
But if liveness is also configured too aggressively:
Pod starts
↓
Liveness fails
↓
Pod restarts
↓
Startup repeats
you can create a restart loop.
ASP.NET Core documentation demonstrates using separate readiness and liveness checks specifically for applications with long-running startup work.
Risk 7: Pod Disruption During Node Upgrades
Before upgrading nodes, inspect:
kubectl get pods -A
and:
kubectl get pdb -A
PodDisruptionBudgets can limit how many replicas are voluntarily disrupted at once.
For a .NET API with:
3 replicas
a sensible disruption policy may allow one replica to be unavailable while the others continue serving traffic.
The exact configuration depends on your availability requirements.
The important point is to verify that:
Node drain
+
PDB
+
Replica count
do not create an impossible scheduling situation.
Risk 8: Single-Replica .NET Services
A deployment such as:
replicas: 1
is especially sensitive to node upgrades.
During a node drain:
Replica
↓
Terminated
↓
Rescheduled
↓
Startup
There can be a period with no available instance.
For critical services, evaluate whether multiple replicas are required.
Do not confuse Kubernetes availability with application availability.
A Kubernetes deployment can be healthy while the application has zero ready replicas.
Risk 9: Resource Requests and Limits
Node upgrades change scheduling conditions.
Suppose your application requests:
resources:
requests:
cpu: "500m"
memory: "512Mi"
and limits:
resources:
limits:
cpu: "1"
memory: "1Gi"
During a rolling node upgrade, replacement capacity must exist for these requests.
If the remaining nodes do not have sufficient allocatable capacity:
Drain node
↓
Pod pending
↓
No capacity
The application may become unavailable.
Check:
kubectl describe nodes
and inspect:
Allocatable
Requested
Allocated
before starting the upgrade.
Risk 10: .NET Memory Behavior
.NET applications can be sensitive to container memory limits.
A service may appear healthy under normal traffic but fail after rescheduling onto a node with different resource pressure.
Monitor:
Working set
GC heap
GC pauses
Container memory
OOMKilled events
Check recent events:
kubectl get events \
--sort-by=.lastTimestamp
Also inspect pod status:
kubectl describe pod <pod-name>
Look for:
OOMKilled
Evicted
BackOff
Unhealthy
FailedScheduling
Do not increase memory limits automatically after an upgrade without identifying the actual cause.
Risk 11: ASP.NET Core Container Port Changes
Containerized ASP.NET Core applications should have explicit port configuration.
.NET 8 changed the default ASP.NET Core container port from 80 to 8080. Microsoft documents that Kubernetes manifests and other container configurations may need to account for this change.
For example:
containers:
- name: api
image: my-api:latest
ports:
- containerPort: 8080
A Kubernetes upgrade is not itself responsible for this .NET change, but upgrade testing is a good opportunity to detect stale manifests.
Verify:
Container port
Service targetPort
Probe port
Ingress/backend port
Application listening address
all agree.
Risk 12: API Deprecations and Removed APIs
Minor Kubernetes releases can deprecate APIs and remove functionality.
Kubernetes documents that deprecated APIs continue to function until their planned removal, while removed APIs are no longer available in the newer version.
This is particularly important for:
Helm charts
Operators
Admission controllers
Ingress controllers
Monitoring agents
CI/CD tools
Custom controllers
The .NET application may be completely correct while its deployment tooling is incompatible.
Audit Manifests Before the Upgrade
Search your repositories:
grep -R \
"apiVersion:" \
./k8s
Also inspect Helm templates:
helm template ./chart
Then validate the generated manifests against the target cluster.
Do not inspect only the application's Deployment.
Check:
Deployment
Service
Ingress
ConfigMap
Secret
HPA
PDB
NetworkPolicy
ServiceAccount
RBAC
CronJob
Risk 13: Ingress and Gateway Dependencies
A .NET API may be healthy while external traffic fails because the ingress or gateway layer is incompatible.
Test:
Internet
↓
Load Balancer
↓
Ingress / Gateway
↓
Service
↓
Pod
↓
ASP.NET Core
After the upgrade, verify:
kubectl get ingress -A
and:
kubectl get svc -A
For modern deployments, also inspect Gateway API resources where applicable.
Do not test only:
curl localhost:8080
That verifies the application but not the production traffic path.
Risk 14: DNS and Service Discovery
A distributed .NET application may depend heavily on Kubernetes DNS:
orders-api
payments-api
identity-api
Test internal connectivity after node upgrades:
kubectl exec \
<pod-name> \
-- getent hosts orders-api
Then test the actual service endpoint.
A healthy pod does not guarantee that:
DNS
+
Service
+
Network Policy
are functioning correctly.
Join the conversation! Your thoughts help the community grow.