Introduction
Kubernetes upgrades are rarely difficult because of the version number itself.
The difficult part is everything connected to the cluster.
A production .NET application running on Kubernetes may depend on:
When a Kubernetes version approaches maintenance mode, teams have a useful opportunity to review those dependencies before the platform moves into a less active support phase.
Kubernetes 1.34 is scheduled to enter maintenance mode on August 27, 2026, with end of life currently scheduled for October 27, 2026. The 1.34 release series is currently listed with 1.34.9 as the latest released patch version. (Kubernetes)
That makes August 2026 a practical point for teams running .NET workloads on Kubernetes 1.34 to review their upgrade plans, test workloads against a newer Kubernetes version, and identify anything that could cause problems during the transition.
What Does Maintenance Mode Mean?
Maintenance mode does not mean that a Kubernetes cluster suddenly stops working.
Your existing:
Pod
Service
Deployment
Ingress
ConfigMap
Secret
does not automatically become invalid when a release enters maintenance mode.
The bigger concern is the support lifecycle.
A simplified lifecycle looks like:
New Release
|
v
Active Development
|
v
Regular Maintenance
|
v
Maintenance Mode
|
v
End of Life
For Kubernetes 1.34:
Release
August 2025
|
v
Active Support
|
v
Maintenance Mode
August 27, 2026
|
v
End of Life
October 27, 2026
The exact operational implications depend on how Kubernetes is deployed and which distribution or managed Kubernetes service you use.
Therefore, teams should also check the support policy of their specific Kubernetes platform.
Why .NET Developers Should Care
A Kubernetes upgrade is an infrastructure change, but application developers can still be affected.
Consider an ASP.NET Core application:
Internet
|
v
Ingress
|
v
Service
|
v
ASP.NET Core Pods
|
v
Database
The application may not contain any Kubernetes-specific C# code.
However, the deployment depends on Kubernetes APIs and runtime behavior.
For example:
apiVersion: apps/v1
kind: Deployment
metadata:
name: orders-api
spec:
replicas: 3
selector:
matchLabels:
app: orders-api
template:
metadata:
labels:
app: orders-api
spec:
containers:
- name: orders-api
image: orders-api:latest
ports:
- containerPort: 8080
A Kubernetes upgrade therefore needs to validate both:
Application
+
Deployment Platform
Start With the Current Cluster Version
Before upgrading anything, establish exactly what is running.
Use:
kubectl version
You can also inspect nodes:
kubectl get nodes
and workloads:
kubectl get deployments -A
The goal is to understand:
Control Plane Version
Node Versions
Namespaces
Deployments
Services
Ingress
Operators
CRDs
Do not assume every node is running the same version simply because the control plane has been upgraded.
Inventory Your Kubernetes APIs
One of the most important upgrade checks is API compatibility.
A manifest might contain:
apiVersion: apps/v1
kind: Deployment
which is a well-established API.
But a legacy application may still contain older API versions.
Look through:
Deployment
StatefulSet
DaemonSet
Ingress
CronJob
RBAC
NetworkPolicy
Custom Resources
The objective is to identify APIs that have been deprecated or removed.
A simple repository search can help:
grep -R "apiVersion:" ./k8s
For a larger repository, use your normal code-search tooling.
Why API Versions Matter
Kubernetes APIs evolve.
A resource might move through a lifecycle such as:
v1alpha1
|
v1beta1
|
v1
The problem occurs when an old API is no longer served by the Kubernetes version you are upgrading to.
For example, an old manifest may contain:
apiVersion: example.io/v1beta1
while the newer platform expects:
apiVersion: example.io/v1
The application code may be perfectly healthy.
The deployment can still fail because the cluster no longer understands the old API.
Test the .NET Application Before the Upgrade
Create a baseline.
Record:
Application startup
Health checks
API requests
Background jobs
Database connectivity
Message processing
Scaling behavior
Deployment rollout
Graceful shutdown
For an ASP.NET Core application, verify the health endpoint:
builder.Services.AddHealthChecks();
var app = builder.Build();
app.MapHealthChecks("/health");
app.Run();
A Kubernetes probe can then use:
livenessProbe:
httpGet:
path: /health
port: 8080
readinessProbe:
httpGet:
path: /health
port: 8080
Before upgrading Kubernetes, make sure these probes actually represent the application's health correctly.
Liveness vs Readiness
This is particularly important during upgrades.
Readiness
Readiness answers:
"Can this pod receive traffic?"
Liveness
Liveness answers:
"Is this container still functioning?"
They should not automatically use exactly the same logic.
For example, if an application temporarily loses access to a downstream database, marking the container as completely dead may cause unnecessary restarts.
A better design might allow the application to remain alive while reporting itself as not ready.
Database unavailable
|
v
Readiness = Failed
Liveness = Healthy
This distinction becomes especially useful during rolling upgrades.
Test Rolling Deployments
A normal deployment might use:
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0
maxSurge: 1
This tells Kubernetes to maintain availability while replacing pods.
Before upgrading the cluster, verify that the application itself behaves correctly during rolling replacement.
Test:
Old Pod
|
v
New Pod Starts
|
v
Readiness Passes
|
v
Traffic Moves
|
v
Old Pod Terminates
If the application takes 30 seconds to initialize but the readiness probe starts immediately, Kubernetes may make decisions based on incomplete application state.
Graceful Shutdown Matters
ASP.NET Core applications should handle termination properly.
When Kubernetes replaces a pod:
SIGTERM
|
v
Application Shutdown
|
v
Existing Requests Finish
|
v
Container Stops
The application should avoid abruptly terminating active work.
For background services:
public sealed class Worker(
ILogger<Worker> logger)
: BackgroundService
{
protected override async Task ExecuteAsync(
CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
await ProcessWorkAsync(stoppingToken);
await Task.Delay(
TimeSpan.FromSeconds(10),
stoppingToken);
}
}
}
The cancellation token is important because the application needs a way to stop work cleanly.
Test Pod Disruption
An upgrade can result in node or pod movement.
Therefore, test how the application behaves when:
Pod disappears
Node drains
Pod restarts
Deployment rolls out
Replica count changes
For a three-replica API:
Before
Pod A
Pod B
Pod C
During replacement:
Pod A
Pod B
Pod C
Pod D
Then:
Pod B
Pod C
Pod D
The application should remain available throughout the expected transition.
Review PodDisruptionBudgets
A PodDisruptionBudget can help protect application availability during voluntary disruptions.
For example:
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: orders-api-pdb
spec:
minAvailable: 2
selector:
matchLabels:
app: orders-api
For a three-replica application, this expresses an intention to keep at least two replicas available during supported voluntary disruptions.
However, a PodDisruptionBudget is not a guarantee against every type of failure.
It should be considered together with:
Replica count
Node distribution
Pod topology
Application startup time
Readiness probes
Test Horizontal Pod Autoscaling
A .NET API may use:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: orders-api
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: orders-api
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
During upgrade testing, verify:
Traffic increases
|
v
CPU increases
|
v
HPA scales
|
v
New Pods become ready
|
v
Traffic distributes
Do not assume that an HPA configuration that worked previously will behave exactly the same under every new cluster configuration.
Review Custom Resource Definitions
Many production clusters use CRDs.
Examples include:
Ingress Controllers
Certificate Management
Monitoring
Service Mesh
GitOps
Cloud Integrations
Storage Operators
These resources may have their own compatibility requirements.
The Kubernetes version is only one component of the upgrade.
Your actual dependency graph might look like:
Kubernetes
|
+--> Ingress Controller
|
+--> CSI Driver
|
+--> Monitoring Operator
|
+--> Certificate Controller
|
+--> Service Mesh
|
+--> GitOps Controller
Each component should have a tested compatibility path.
Check Helm Charts
If your .NET application is deployed using Helm, inspect the chart.
For example:
orders-api/
├── Chart.yaml
├── values.yaml
└── templates/
├── deployment.yaml
├── service.yaml
├── ingress.yaml
└── hpa.yaml
Render the manifests before deployment:
helm template orders-api ./orders-api
Then inspect:
apiVersion
kind
annotations
labels
probes
resources
securityContext
This catches problems before they reach the cluster.
Test Resource Requests and Limits
A deployment might define:
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "1"
memory: "512Mi"
These values influence scheduling and runtime behavior.
Do not simply copy resource values from another application.
Measure your workload.
For example:
Application
|
+--> CPU usage
+--> Memory usage
+--> Startup time
+--> Request rate
Then tune the resource configuration based on actual behavior.
Test .NET Startup Time
Startup time matters during rolling upgrades.
Suppose:
Pod startup = 20 seconds
but the deployment assumes:
Pod ready = immediately
Traffic may reach the application before it is actually ready.
Use readiness probes and appropriate startup configuration.
For applications with longer initialization periods, a startup probe may be useful:
startupProbe:
httpGet:
path: /health
port: 8080
failureThreshold: 30
periodSeconds: 5
This gives the application time to initialize before Kubernetes begins applying normal liveness decisions.
Test Database Connections
A Kubernetes upgrade can expose application assumptions around connection handling.
For an EF Core application:
builder.Services.AddDbContext<AppDbContext>(options =>
{
options.UseSqlServer(
connectionString,
sqlOptions =>
{
sqlOptions.EnableRetryOnFailure();
});
});
Test:
Pod restart
Database connection loss
Connection recovery
Application startup
Concurrent requests
The goal is to make the application resilient to expected infrastructure transitions.
Test Secrets and Configuration
Check whether your application depends on:
Secrets
ConfigMaps
Environment Variables
Mounted Files
External Secret Controllers
For example:
env:
- name: ConnectionStrings__Default
valueFrom:
secretKeyRef:
name: orders-api
key: connection-string
Make sure the secret exists in the target namespace:
kubectl get secrets -n production
Do not print secret contents during troubleshooting.
Test Network Policies
Network policies can affect application behavior during cluster changes.
A typical .NET application may require:
API
|
+--> Database
|
+--> Redis
|
+--> Message Broker
|
+--> External API
If a policy accidentally blocks one dependency:
API
|
X--> Database
the application may fail even though the deployment itself is healthy.
Test actual traffic paths.
Test Ingress
Ingress is another important area.
Verify:
TLS
Hostnames
Path Routing
Timeouts
Headers
Client IP Handling
Health Checks
For example:
spec:
rules:
- host: api.example.internal
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: orders-api
port:
number: 80
Ingress behavior also depends on the ingress controller, so test the controller version independently from Kubernetes itself.
Build a Pre-Upgrade Test Matrix
A practical matrix might look like:
| Area | Test | Expected Result |
|---|
| Application | API startup | Successful |
| Health | Readiness | Correct state |
| Health | Liveness | Correct state |
| Deployment | Rolling update | No unexpected outage |
| Database | Connection | Successful |
| Database | Retry behavior | Recovers from transient issue |
| Scaling | HPA | Scales correctly |
| Networking | Service-to-service | Successful |
| Ingress | HTTPS | Successful |
| Secrets | Configuration | Loaded correctly |
| Storage | Persistent volume | Data remains available |
| Background jobs | Graceful shutdown | No lost work |
| Observability | Logs/metrics | Available |
| Security | RBAC | Expected permissions |
| CRDs | Controllers | Compatible |
This makes the upgrade measurable.
Common Mistakes
Mistake 1: Upgrading Production First
Always test in a representative non-production cluster.
Mistake 2: Checking Only the Control Plane
Nodes, operators, controllers, and workloads also matter.
Mistake 3: Ignoring CRDs
Third-party controllers may have their own Kubernetes compatibility requirements.
Mistake 4: Testing Only Application Startup
A successful startup does not prove that the workload is healthy under rolling replacement.
Mistake 5: Ignoring Deprecated APIs
Old manifests can fail when an API is removed.
Mistake 6: Forgetting Ingress and Storage
Applications depend on more than Deployments and Services.
Mistake 7: Treating a Cluster Upgrade as an Application Deployment
Infrastructure upgrades need their own validation process.
Troubleshooting
| Problem | What to Check |
|---|
| Deployment fails | API versions and manifest validation |
| Pods remain Pending | Resource requests, node capacity, scheduling rules |
| Pods restart | Liveness probe, application exceptions, memory limits |
| Pods never become Ready | Readiness probe and dependency availability |
| Ingress stops working | Controller version and routing configuration |
| Database unavailable | Secrets, network policies, connection settings |
| HPA does not scale | Metrics availability and HPA configuration |
| CRD fails | Operator/controller compatibility |
| Helm deployment fails | Rendered manifests and chart compatibility |
| Background jobs lose work | Graceful shutdown and queue semantics |
Best Practices
Establish a Baseline
Record application behavior before changing the cluster.
Test the Entire Dependency Chain
Do not test Kubernetes in isolation.
Review APIs
Search manifests and Helm templates for deprecated or obsolete APIs.
Validate Controllers
Check ingress, storage, monitoring, and other operators.
Test Rolling Behavior
A production upgrade should not be considered successful if applications cannot survive normal pod replacement.
Verify Health Probes
Make sure readiness and liveness represent the application's actual state.
Keep Resource Configuration Realistic
Use observed CPU and memory behavior instead of arbitrary values.
Test Failure Scenarios
Include pod termination, node drain, dependency failure, and connection recovery.
Maintain a Rollback Plan
Know what you will do if the upgrade does not behave as expected.
Test Before the Deadline
Do not wait until maintenance mode begins to discover compatibility problems.
Advantages of Testing Before Maintenance
Reduced Upgrade Risk
Problems can be identified before the production change.
Better Application Reliability
Testing probes, graceful shutdown, scaling, and dependencies improves the application's overall resilience.
Easier Planning
Teams have time to coordinate infrastructure and application changes.
Better Documentation
The upgrade process becomes repeatable instead of relying on tribal knowledge.
Opportunity to Remove Technical Debt
Old manifests, outdated Helm charts, and unnecessary dependencies can be identified during the review.
Disadvantages and Challenges
Upgrade Testing Takes Time
A production-like environment is required for meaningful testing.
Multiple Dependencies Must Be Validated
The Kubernetes version is only one part of the platform.
Some Failures Are Environment-Specific
A local cluster may not reproduce managed Kubernetes behavior.
Rollback Can Be Complicated
Database migrations and application changes can make rollback more difficult.
Third-Party Components Add Risk
Operators, ingress controllers, and other extensions need their own compatibility testing.
A Practical Final Test Plan
For a .NET application running on Kubernetes 1.34, a useful pre-upgrade sequence is:
1. Record current Kubernetes versions
|
v
2. Inventory workloads and APIs
|
v
3. Review deprecated APIs
|
v
4. Check Helm charts and CRDs
|
v
5. Verify .NET application health
|
v
6. Test database and external dependencies
|
v
7. Test rolling deployments
|
v
8. Test pod disruption
|
v
9. Test HPA and resource behavior
|
v
10. Validate ingress and networking
|
v
11. Upgrade a staging cluster
|
v
12. Run regression tests
|
v
13. Upgrade production
|
v
14. Monitor closely
The current Kubernetes release schedule makes this particularly timely: Kubernetes 1.34 is scheduled to enter maintenance mode on August 27, 2026, and its current end-of-life date is October 27, 2026. (Kubernetes)
What .NET Teams Should Verify
Before moving away from Kubernetes 1.34, make sure you can answer these questions:
[ ] Is the target Kubernetes version supported by our platform?
[ ] Are all Kubernetes APIs still supported?
[ ] Are our Helm charts compatible?
[ ] Are our CRDs and operators compatible?
[ ] Does the ASP.NET Core application start correctly?
[ ] Do readiness and liveness probes behave correctly?
[ ] Does graceful shutdown work?
[ ] Do database connections recover correctly?
[ ] Does HPA behave as expected?
[ ] Do network policies still allow required traffic?
[ ] Does ingress continue routing correctly?
[ ] Are secrets and configuration available?
[ ] Do background jobs survive pod replacement?
[ ] Does observability continue working?
[ ] Have we tested rolling upgrades?
[ ] Do we have a rollback plan?
If several answers are unknown, the cluster is not ready for a production upgrade.
Conclusion
Kubernetes 1.34 entering maintenance mode is a good reminder that Kubernetes upgrades should be treated as engineering projects rather than simple version changes. For .NET teams, the most important work is not only checking whether the cluster starts after an upgrade. You need to verify the complete application lifecycle, including ASP.NET Core startup, health probes, rolling deployments, database connections, background workers, scaling, ingress, networking, secrets, storage, and observability. Kubernetes 1.34 is scheduled to enter maintenance mode on August 27, 2026, so teams still running it should use the remaining time to establish a baseline, test their workloads against the target version, and identify compatibility issues early. A well-tested upgrade is much easier to manage than discovering a Kubernetes compatibility problem during a production deployment.