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.
Risk 15: Network Policies
If your .NET services use NetworkPolicies, test both:
Allowed traffic
and:
Denied traffic
For example:
API
↓
Database
should remain allowed.
But:
Unknown namespace
↓
Database
should remain blocked.
Upgrade validation should confirm that security boundaries did not change unintentionally.
Build an Upgrade Test Environment
A practical sequence is:
Development
↓
Upgrade test cluster
↓
Staging
↓
Canary production nodes
↓
Full production rollout
Do not use production as the first place where the cluster encounters Kubernetes 1.37.
A representative staging cluster should include:
Same .NET runtime
Same container image
Same probes
Same ingress
Same runtime configuration
Same monitoring
Same policies
as production.
Test a Real .NET Workload
A basic smoke test is:
curl https://api.example.com/health/ready
Then exercise:
GET
POST
Authentication
Database access
External API calls
Background jobs
Message queues
File/storage operations
The exact tests should match your service.
Test Rolling Restarts
Before upgrading Kubernetes, simulate the event:
kubectl rollout restart deployment my-api
Observe:
kubectl rollout status deployment my-api
Then inspect:
kubectl get pods -w
You want to confirm:
Old pod
↓
Not ready
↓
New pod ready
↓
Old pod terminated
without unacceptable request failures.
Test Node Drain
A more realistic test is:
kubectl drain <node-name> \
--ignore-daemonsets \
--delete-emptydir-data
Do this only in an appropriate test environment and according to your cluster's operational procedures.
Observe:
Pod eviction
Rescheduling
Readiness
Application startup
Request failures
PDB behavior
This closely approximates what happens during node maintenance.
Monitor the .NET Application During the Test
Watch:
kubectl top pods
and:
kubectl get pods -w
Also inspect application telemetry:
Request rate
Error rate
P95 latency
P99 latency
GC behavior
Database latency
Dependency failures
The goal is not merely:
kubectl get nodes
→ Ready
A cluster can be green while the application is experiencing elevated latency.
Validate Graceful Shutdown
Create a long-running test endpoint:
app.MapGet(
"/test/slow",
async (CancellationToken cancellationToken) =>
{
await Task.Delay(
TimeSpan.FromSeconds(10),
cancellationToken);
return Results.Ok();
});
Then send a request while terminating the pod.
Observe whether the request:
Completes gracefully
or:
Gets terminated unexpectedly
Do not use an artificial endpoint in production. This is strictly for controlled testing.
Validate Background Services
If the .NET application uses:
BackgroundService
test shutdown behavior separately.
ASP.NET Core invokes StopAsync during graceful host shutdown and provides a cancellation token to allow background operations to stop. Microsoft documents a default shutdown timeout and recommends that remaining operations respond promptly when cancellation is requested.
A node upgrade is therefore also a test of:
Queue consumers
Schedulers
Background workers
Message processing
Protect Against Duplicate Processing
Suppose a background worker receives:
Message 123
and the pod is terminated during processing.
Your system should have an explicit delivery strategy:
At-most-once
At-least-once
Exactly-once semantics where actually supported
Do not assume Kubernetes termination automatically prevents duplicate processing.
For critical workloads, design application-level idempotency.
Upgrade One Node Pool at a Time
For production clusters with multiple node pools:
Pool A
Pool B
Pool C
do not upgrade everything simultaneously.
A safer operational sequence is:
Pool A
↓
Validate
↓
Pool B
↓
Validate
↓
Pool C
The exact sequence depends on the cluster architecture and managed-Kubernetes provider.
Maintain a Rollback Strategy
Before upgrading, define:
What happens if node upgrade fails?
What happens if pods cannot schedule?
What happens if ingress breaks?
What happens if the .NET service fails?
How will traffic be shifted?
How will nodes be replaced?
Do not assume "rollback" means simply changing:
1.37 → 1.36
Kubernetes version rollback procedures can have constraints depending on the upgrade stage and cluster management platform.
For node-level problems, replacing unhealthy nodes with known-good images may be safer than attempting an ad-hoc downgrade.
Kubernetes 1.36 to 1.37 Preflight Checklist
Use this checklist before production rollout:
| Area | Check |
|---|---|
| Kubernetes | Current patch release |
| Target | 1.37 release status |
| Nodes | All Ready |
| cgroups | cgroup v2 where required |
| Runtime | Supported and consistent |
| Kubelet | Version compatibility |
| CNI | Supported configuration |
| CSI | Supported configuration |
| Ingress | Supported controller/version |
| APIs | Deprecated/removed API audit |
| PDB | Disruption behavior |
| Replicas | Sufficient capacity |
| Probes | Readiness/liveness validated |
| Shutdown | Graceful termination tested |
| Resources | Scheduling capacity verified |
| .NET | Runtime/container compatibility |
| Observability | Metrics/logs/traces working |
| Backup | etcd/data backups verified |
| Rollback | Operational plan documented |
Common Mistakes
Upgrading the Control Plane First and Ignoring Nodes
The entire cluster lifecycle needs to be planned.
Checking Only Kubernetes Version
A healthy control plane does not prove that every node is ready.
Ignoring cgroup Configuration
Old node images can become upgrade blockers.
Treating Readiness and Liveness as the Same Thing
They serve different purposes in ASP.NET Core and Kubernetes.
Using a Database Check for Liveness
A dependency outage can turn into application restart loops.
Running One Replica
Node maintenance can temporarily eliminate service capacity.
Ignoring PDB Configuration
Disruption policies can prevent or complicate node drains.
Forgetting Background Workers
A restarted pod can interrupt message processing.
Testing Only /health
Health checks do not validate the complete application path.
Assuming Ready Means Production Is Healthy
Check application-level telemetry after the upgrade.
Troubleshooting
Pod Stays Pending After Node Upgrade
Check:
kubectl describe pod <pod-name>
Look for:
Insufficient CPU
Insufficient memory
Taints
Affinity
Node selectors
PDB constraints
Node Becomes NotReady
Inspect:
kubectl describe node <node-name>
Then check:
journalctl -u kubelet
and:
systemctl status containerd
for containerd-based nodes.
.NET Pod Keeps Restarting
Check:
kubectl describe pod <pod-name>
kubectl logs <pod-name>
kubectl logs <pod-name> --previous
Look for:
OOMKilled
Probe failures
Startup exceptions
Configuration errors
Port mismatch
Dependency failures
Readiness Probe Fails After Upgrade
Verify:
Application listening port
Service targetPort
Probe port
Health endpoint
Network policy
Startup duration
For .NET 8+ container images, remember that the default ASP.NET Core container port changed to 8080 unless explicitly configured otherwise.
Application Receives Traffic Before It Is Ready
Review the readiness probe.
ASP.NET Core supports separate readiness and liveness checks specifically so Kubernetes can avoid sending traffic to an application that is running but not yet ready.
Best Practices
Treat Kubernetes 1.37 as an infrastructure change, not merely a version change.
Upgrade one minor version at a time.
Use the latest supported patch release before upgrading.
Audit every worker node before the upgrade.
Verify cgroup configuration.
Verify container runtime compatibility.
Keep kubelet and kubeadm versions within supported skew.
Test node drains before production.
Maintain sufficient replica capacity.
Review PodDisruptionBudgets.
Separate readiness and liveness checks.
Test ASP.NET Core graceful shutdown.
Test background worker shutdown.
Validate database and message-processing behavior.
Audit Kubernetes API deprecations.
Test ingress and Gateway traffic.
Test NetworkPolicies.
Verify resource capacity for rescheduling.
Monitor application-level SLOs during rollout.
Use staged or canary node upgrades where possible.
Frequently Asked Questions
Is Kubernetes 1.37 released?
Not yet at the time of writing. The official Kubernetes release schedule currently targets August 26, 2026 for Kubernetes 1.37.0.
Can I skip Kubernetes 1.36 and upgrade directly to 1.37?
For kubeadm upgrades, skipping minor versions is unsupported. The official upgrade documentation recommends upgrading one minor version at a time.
Will my .NET application need code changes?
Not necessarily.
Many upgrade risks are operational rather than C#-level changes. However, applications with custom signal handling, fragile probes, single replicas, background workers, or strict runtime assumptions should be tested carefully.
Why are cgroups important?
Kubernetes and the container runtime use cgroups for resource management. Kubernetes has deprecated cgroup v1, and kubelet behavior requires particular attention on older nodes.
Should I upgrade containerd separately?
That depends on your Kubernetes distribution and supported runtime versions. Check the runtime requirements for the exact Kubernetes version and distribution you operate rather than assuming a runtime upgrade is automatically required.
What should my ASP.NET Core readiness probe check?
It should answer whether the application is ready to receive traffic.
For example:
Application initialized
Required configuration loaded
Critical dependencies available
The exact checks depend on the service.
Should liveness check the database?
Usually, avoid making liveness dependent on a database or other external dependency unless there is a specific architectural reason.
A temporary dependency outage should not automatically cause Kubernetes to restart every application instance.
How do I test whether a .NET service survives node upgrades?
Start with:
Pod restart
↓
Node drain
↓
Staged node upgrade
↓
Application telemetry
Verify request continuity, startup, readiness, graceful shutdown, background processing, and dependency connectivity.
What is the biggest upgrade risk?
There is no universal single risk.
For older self-managed clusters, node/runtime and cgroup compatibility can be critical. For .NET workloads, application lifecycle behavior—especially probes, shutdown, startup, and resource capacity—can determine whether a technically successful cluster upgrade becomes a successful application upgrade.
Conclusion
A Kubernetes minor upgrade is successful only when both the platform and its workloads remain healthy.
For a .NET service, the relevant upgrade path is:
Kubernetes 1.36
↓
Node/runtime validation
↓
Kubernetes 1.37 testing
↓
.NET lifecycle testing
↓
Node drain testing
↓
Staged rollout
↓
Application validation
↓
Production
The most important preparation is not changing the Kubernetes version in a configuration file.
It is identifying what your application assumes about its environment.
Check:
cgroups
container runtime
kubelet
resource capacity
probes
shutdown
startup
PDBs
replicas
ingress
network policies
background workers
API compatibility
Kubernetes 1.37 is currently scheduled for August 26, 2026, giving teams a useful pre-release window to test representative workloads before production adoption.
For .NET services, the upgrade should ultimately be evaluated at two levels:
Platform health
+
Application health
A cluster showing:
Nodes = Ready
is necessary, but it is not sufficient.
The real success criteria are:
Requests continue
Pods become Ready
Existing requests drain correctly
Background work behaves correctly
Dependencies remain reachable
No unexpected OOMs occur
Latency remains within SLO
Error rate remains acceptable
The key principle is:
Do not upgrade Kubernetes first and discover application compatibility afterward. Validate the infrastructure assumptions of your .NET services before the upgrade.
That approach turns Kubernetes 1.37 from a risky version change into a controlled infrastructure migration.

Join the conversation! Your thoughts help the community grow.