A Kubernetes upgrade is rarely just a matter of changing a version number.
For a production .NET application, the cluster version sits underneath the application runtime, networking, storage, ingress, observability stack, admission controllers, deployment tooling, and platform integrations. A change that looks safe at the cluster level can still expose an incompatibility somewhere in that chain.
Kubernetes 1.34 is particularly relevant for upgrade planning because its current support timeline is approaching a transition point. The official Kubernetes release information lists 1.34.9 as the latest patch release and shows maintenance mode beginning on August 27, 2026, with end of life scheduled for October 27, 2026.
That does not mean every application running Kubernetes 1.34 must immediately upgrade. It does mean platform teams should have a repeatable way to determine whether their .NET workloads are ready for the next supported Kubernetes version.
This article presents a practical upgrade-readiness methodology focused on production .NET applications.
Why Kubernetes Upgrade Testing Matters
A typical .NET application deployed to Kubernetes has more dependencies than its container image suggests.
A simplified architecture might look like this:
Kubernetes Cluster
|
+-------------+-------------+
| |
Control Plane Worker Nodes
| |
| +------+------+
| | |
| .NET API Worker
| | |
+-------------+------+-------------+
|
Ingress / Service
|
Database / Cache
The application itself may not need to change at all during a Kubernetes upgrade.
The surrounding platform can still affect:
Pod scheduling
Container startup
Service discovery
Networking
Readiness and liveness probes
Resource enforcement
Ingress behavior
Storage
Autoscaling
Deployment behavior
Admission policies
The purpose of upgrade testing is therefore to verify the complete application path, not just whether a Pod reaches Running.
Define an Upgrade Readiness Baseline
Before changing anything, record the current environment.
Start with:
kubectl version
kubectl get nodes -o wide
kubectl get pods -A
kubectl get deployments -A
kubectl get statefulsets -A
Also capture important workload configuration:
kubectl get deployment my-api -o yaml
Do not treat the resulting YAML as something to blindly restore later. It is primarily a baseline for understanding what is deployed.
Record:
| Area | Baseline |
|---|---|
| Kubernetes version | Current cluster version |
| .NET runtime | Application runtime |
| Container image | Current application image |
| Node OS | Current node image |
| CPU requests/limits | Current configuration |
| Memory requests/limits | Current configuration |
| Replica count | Current deployment |
| HPA | Current scaling configuration |
| Ingress | Current controller/configuration |
| Storage | PVC/storage configuration |
| Observability | Metrics/logging/tracing |
| Admission policies | Current policies |
This makes comparison much easier after the upgrade.
Check Kubernetes Version Skew
One of the first things to understand is that Kubernetes components do not all have unlimited version compatibility.
The official version-skew policy defines supported relationships between components such as kube-apiserver, kubelet, kube-proxy, controllers, and kubectl. For example, kubelet must not be newer than kube-apiserver, while supported older versions are allowed within documented limits. kubectl is supported within one minor version of the API server.
This matters during rolling upgrades because a cluster is temporarily running mixed component versions.
The upgrade should therefore follow the supported component order rather than upgrading arbitrary components independently.
The Kubernetes project also recommends moving from the latest patch version of the current minor release to the latest patch version of the target minor release when preparing an upgrade.
Test the .NET Application Before the Upgrade
The application should have a known-good baseline before the cluster changes.
For an ASP.NET Core application, verify the health endpoints.
For example:
builder.Services.AddHealthChecks()
.AddCheck("application", () => HealthCheckResult.Healthy());
var app = builder.Build();
app.MapHealthChecks("/health/live");
app.MapHealthChecks("/health/ready");
A production application should distinguish between liveness and readiness when appropriate.
A liveness probe answers:
Is this process alive?
A readiness probe answers:
Can this instance currently receive traffic?
Those questions become especially important during rolling upgrades.
Review Kubernetes Probes
A typical Deployment might define:
apiVersion: apps/v1
kind: Deployment
metadata:
name: orders-api
spec:
replicas: 3
template:
spec:
containers:
- name: orders-api
image: orders-api:1.0
ports:
- containerPort: 8080
startupProbe:
httpGet:
path: /health/live
port: 8080
failureThreshold: 30
periodSeconds: 5
readinessProbe:
httpGet:
path: /health/ready
port: 8080
periodSeconds: 10
livenessProbe:
httpGet:
path: /health/live
port: 8080
periodSeconds: 20
The exact values should be based on application startup and recovery behavior.
Do not copy probe timings from another service simply because they appear to work there.
Test Graceful Shutdown
A Kubernetes upgrade can cause Pods to be terminated and recreated.
The .NET application should handle termination cleanly.
ASP.NET Core applications participate in the host's graceful shutdown process, but your application should still be tested under actual termination conditions.
For example, if the service consumes background work, verify that shutdown does not result in:
Lost messages
Partially completed operations
Duplicate processing
Abandoned database transactions
Unflushed telemetry
A simple application test can terminate a Pod deliberately:
kubectl delete pod <pod-name>
Then observe:
kubectl get pods -w
The goal is not merely to confirm that Kubernetes creates a replacement.
You want to verify that the application exits and recovers correctly.
Validate Resource Requests and Limits
Kubernetes scheduling and resource management are closely connected to application stability.
A .NET deployment should define appropriate resource requests and, where appropriate, limits:
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "1"
memory: "1Gi"
These numbers are examples, not recommended defaults.
During upgrade testing, compare actual utilization against configured resources.
Useful commands include:
kubectl top pods
kubectl top nodes
Look for:
CPU throttling
Memory pressure
OOM kills
Pods becoming unschedulable
Node capacity changes
Unexpected changes in application latency
A Kubernetes upgrade should not be considered successful if the cluster is technically healthy but the application has degraded resource behavior.
Test Autoscaling
If the .NET application uses an HPA, test scaling before and after the upgrade.
Inspect the configuration:
kubectl get hpa orders-api
kubectl describe hpa orders-api
A typical HPA might look like:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: orders-api
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: orders-api
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
Run a controlled load test and verify:
Traffic increases
|
v
CPU increases
|
v
HPA detects utilization
|
v
Replica count increases
|
v
Traffic is distributed
The important measurement is not simply whether replicas increase.
Check whether scaling happens quickly enough for the application's traffic profile and whether newly created Pods become ready successfully.
Validate Service and Ingress Networking
A .NET API can be healthy while external traffic is broken.
Test the entire path:
Client
|
v
Load Balancer
|
v
Ingress
|
v
Service
|
v
Pod
|
v
ASP.NET Core
Validate:
kubectl get ingress
kubectl get svc
kubectl get endpoints
Then perform real HTTP requests.
For example:
curl -i https://api.example.com/health/ready
Test both normal traffic and failure scenarios.
Terminate one Pod while sending requests and verify that traffic continues through the remaining replicas.
Test Database Connectivity
Many .NET applications depend on PostgreSQL, SQL Server, Redis, or another external service.
Kubernetes upgrades should not be tested with only an application health endpoint.
A health check that says "process is alive" does not prove that database operations are working.
For a controlled integration test:
builder.Services.AddHealthChecks()
.AddDbContextCheck<AppDbContext>();
Be careful with dependency-heavy readiness checks. If the database is temporarily unavailable, you want Kubernetes to stop sending traffic to an unhealthy instance, but you also do not want an unhealthy dependency to create an uncontrolled cascading restart loop.
Join the conversation! Your thoughts help the community grow.