Introduction
Kubernetes upgrades are often treated as infrastructure changes, but they can directly affect application deployments.
A .NET application may continue to build successfully while its Kubernetes deployment fails because of changes in APIs, container configuration, probes, networking, storage, security settings, or platform behavior.
Kubernetes 1.34 introduces an important maintenance milestone for teams still running older Kubernetes versions. As a result, teams managing .NET workloads should validate their deployment manifests and application behavior before moving their clusters forward.
The safest approach is not to wait for the production upgrade and discover compatibility problems afterward. Instead, create a repeatable compatibility test that validates the complete path:
.NET Application
|
v
Container Image
|
v
Kubernetes Manifest
|
v
Kubernetes Cluster
|
v
Running Application
This article explains how to test .NET application compatibility with Kubernetes 1.34 and identify deployment problems before they reach production.
Why Kubernetes Version Compatibility Matters
A Kubernetes version affects more than the Kubernetes API server.
A production application typically depends on several components:
Kubernetes API
Container runtime
Ingress or Gateway
Service networking
ConfigMaps
Secrets
Persistent storage
Health probes
Service accounts
RBAC
Autoscaling
Jobs and CronJobs
A .NET application can therefore be perfectly valid while its surrounding deployment configuration contains compatibility issues.
For example:
.NET Application
|
+-- Deployment
+-- Service
+-- ConfigMap
+-- Secret
+-- Ingress
+-- HPA
+-- PVC
+-- ServiceAccount
Testing only the application container does not validate the complete deployment.
Start With a Kubernetes Inventory
Before upgrading the cluster, document what the application actually uses.
A simple inventory can look like this:
| Resource | Used | Critical |
|---|
| Deployment | Yes | Yes |
| Service | Yes | Yes |
| ConfigMap | Yes | Yes |
| Secret | Yes | Yes |
| Ingress | Yes | Yes |
| HPA | Yes | Maybe |
| PVC | Yes/No | Depends |
| CronJob | Yes/No | Depends |
| ServiceAccount | Yes | Yes |
This gives the migration team a concrete list of resources to test.
Do not assume that every application needs every Kubernetes feature.
Check the Current Cluster Version
Before beginning the migration, record the current Kubernetes version.
For example:
kubectl version
Also inspect the nodes:
kubectl get nodes -o wide
And review the workloads:
kubectl get deployments -A
The objective is to understand the starting point.
A useful migration record contains:
Current Kubernetes Version
Target Kubernetes Version
Node Versions
Container Runtime
Ingress Implementation
Storage Provider
Application Image
.NET Runtime Version
Validate the .NET Container Image
The first compatibility layer is the container itself.
A typical .NET application Dockerfile might look like:
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS runtime
WORKDIR /app
EXPOSE 8080
COPY ./publish/ .
ENTRYPOINT ["dotnet", "Orders.Api.dll"]
The important part is that the Kubernetes deployment should match how the container actually listens and starts.
For example, if the application listens on port 8080, the Kubernetes configuration should expose the same port.
containers:
- name: orders-api
image: orders-api:latest
ports:
- containerPort: 8080
A Kubernetes upgrade is a good opportunity to verify that these assumptions are still correct.
Test the Application Locally in a Container
Before testing Kubernetes compatibility, verify the container independently.
Build the image:
docker build -t orders-api:test .
Run it:
docker run --rm -p 8080:8080 orders-api:test
Then test the health endpoint:
curl http://localhost:8080/health
If the container itself does not behave correctly, Kubernetes testing will only make troubleshooting harder.
The validation order should be:
Application
|
v
Container
|
v
Kubernetes
Validate Deployment Manifests
Review the application's Kubernetes manifests before applying them to the target cluster.
A typical Deployment may look like:
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
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "1"
memory: "512Mi"
Check every field rather than assuming that an existing manifest is still appropriate.
Test Health Probes
Health probes are particularly important for .NET applications.
A simple ASP.NET Core application can expose a health endpoint:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHealthChecks();
var app = builder.Build();
app.MapHealthChecks("/health");
app.Run();
Then Kubernetes can use:
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
The distinction matters.
Liveness Probe
Determines whether the container should continue running.
Readiness Probe
Determines whether the application should receive traffic.
A deployment upgrade can expose incorrect probe assumptions.
For example, if an application takes longer to initialize after deployment, an overly aggressive liveness probe can cause unnecessary restarts.
Test Startup Behavior
.NET applications can have different startup requirements depending on:
Do not assume that the application is ready immediately after the process starts.
A better deployment test is:
Pod Created
|
v
Container Started
|
v
Application Initialized
|
v
Readiness Check
|
v
Traffic Enabled
Observe the complete startup sequence.
Validate ConfigMaps and Secrets
Kubernetes configuration is another important compatibility area.
For example:
env:
- name: ASPNETCORE_ENVIRONMENT
valueFrom:
configMapKeyRef:
name: orders-config
key: environment
Secrets may be loaded similarly:
env:
- name: ConnectionStrings__Database
valueFrom:
secretKeyRef:
name: orders-secrets
key: database
After deployment, verify that the application receives the expected configuration.
Do not print sensitive values into application logs while testing.
Instead, validate configuration indirectly.
For example:
Database Configuration Loaded
|
v
Database Connection Successful
rather than logging the actual connection string.
Test Service Connectivity
A .NET application may expose port 8080, while the Kubernetes Service exposes another port.
For example:
apiVersion: v1
kind: Service
metadata:
name: orders-api
spec:
selector:
app: orders-api
ports:
- port: 80
targetPort: 8080
The traffic path becomes:
Client
|
v
Service :80
|
v
Pod :8080
|
v
ASP.NET Core
Test this path after deploying to the target cluster.
A healthy pod does not automatically mean that service routing is correct.
Validate Ingress or Gateway Configuration
If the application is externally accessible, validate its traffic entry point.
Test:
External Request
|
v
Ingress / Gateway
|
v
Service
|
v
Pod
|
v
.NET Application
Check:
If your environment uses a specific ingress controller or Gateway implementation, test that component independently as part of the cluster upgrade.
Test Resource Requests and Limits
A Kubernetes upgrade is also an opportunity to validate application resource configuration.
For example:
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "1"
memory: "512Mi"
These values should be based on actual workload requirements.
Do not copy arbitrary CPU and memory values into production.
For a .NET application, monitor:
CPU usage
Working set
Allocation rate
Garbage collection
Request latency
Container restarts
The objective is to determine whether the application remains stable under its expected resource constraints.
Test Horizontal Pod Autoscaling
If the application uses HPA, verify scaling behavior.
A simplified configuration might look like:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: orders-api
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: orders-api
minReplicas: 2
maxReplicas: 8
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
The test should confirm:
Low Load
|
v
Minimum Replicas
High Load
|
v
Scale Out
Load Decreases
|
v
Scale In
Do not test only whether the HPA object exists.
Generate representative load and observe whether the application actually scales as expected.
Test Graceful Shutdown
Container orchestration means applications are frequently started and stopped.
A .NET application should handle shutdown gracefully.
For example:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHostedService<Worker>();
var app = builder.Build();
app.MapGet("/", () => "Orders API");
app.Run();
Background services should correctly respond to cancellation.
A worker might use:
protected override async Task ExecuteAsync(
CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
await ProcessQueueAsync(stoppingToken);
await Task.Delay(
TimeSpan.FromSeconds(10),
stoppingToken);
}
}
During Kubernetes testing, terminate a pod and verify that active work is handled according to the application's requirements.
Test Rolling Updates
A Kubernetes deployment should be tested under an actual rolling update.
A simplified strategy is:
Version A
|
+-- Pod 1
+-- Pod 2
+-- Pod 3
|
v
Rolling Update
|
v
Version B
|
+-- Pod 1
+-- Pod 2
+-- Pod 3
During the update, monitor:
Request failures
Readiness status
Pod restarts
Response latency
Connection errors
Background jobs
The application should remain available according to the deployment's availability requirements.
Test Database Connectivity During Pod Replacement
This is particularly important for EF Core applications.
During a rolling deployment:
Old Pod
|
+-- Database Connections
New Pod
|
+-- Database Connections
Verify that:
New pods can establish connections.
Old pods release connections correctly.
Connection pools recover after termination.
Database connection limits are not exceeded.
A cluster upgrade can expose connection-management assumptions that were not visible during single-pod testing.
Validate Persistent Storage
Applications that use persistent volumes require additional testing.
For example:
Pod
|
v
PersistentVolumeClaim
|
v
PersistentVolume
|
v
Storage Backend
A test should verify:
The volume can be mounted.
The application can write data.
The pod can be restarted.
The volume can be remounted.
Previously written data remains available.
For example:
kubectl get pvc
Then inspect the pod:
kubectl describe pod <pod-name>
Storage behavior should be tested independently from application deployment.
Test CronJobs and Background Processing
Scheduled workloads can be overlooked during cluster upgrades.
For example:
apiVersion: batch/v1
kind: CronJob
metadata:
name: orders-cleanup
spec:
schedule: "0 * * * *"
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: cleanup
image: orders-cleanup:latest
Verify that:
The CronJob is scheduled.
Jobs are created.
The container starts.
Database operations succeed.
Failed jobs behave as expected.
Do not limit compatibility testing to long-running Deployments.
Validate RBAC
Kubernetes permissions can affect whether applications start correctly.
A service account might be associated with:
serviceAccountName: orders-api
Test the permissions required by the application.
Do not solve permission failures by simply granting broad cluster-level access.
Use the smallest permission set required.
A good validation process is:
Application
|
v
Requested Resource
|
v
RBAC Check
|
+-- Allowed -> Continue
|
+-- Denied -> Controlled Failure
Check Deprecated APIs
One of the most important upgrade checks is identifying manifests that rely on APIs that are no longer appropriate for the target Kubernetes version.
Inspect the application's manifests and deployment tooling.
Look for:
The exact API compatibility requirements should be validated against the Kubernetes version used by your cluster.
Do not assume that a manifest created several years ago is automatically safe for a current cluster.
Run a Staging Upgrade First
The safest migration sequence is:
Production
|
v
Clone Relevant Configuration
|
v
Staging Cluster
|
v
Upgrade Kubernetes
|
v
Deploy .NET Application
|
v
Run Tests
|
v
Observe
|
v
Production
Staging should reproduce the important production characteristics.
This includes:
Kubernetes configuration
Node characteristics
Networking
Storage
Ingress
Secrets
Application image
Database connectivity
The objective is not to perfectly reproduce every production detail. It is to reproduce the parts that could affect compatibility.
Common Mistakes
Testing Only Pod Startup
A pod can be running while the application is inaccessible.
Ignoring Readiness Probes
Incorrect readiness configuration can cause traffic to reach an application before it is ready.
Testing Only the .NET Container
The Kubernetes platform includes services, ingress, storage, RBAC, and other components.
Ignoring Background Jobs
CronJobs and workers may use different deployment paths.
Using Excessive RBAC Permissions
Granting broad permissions can hide the real permission requirements of the application.
Skipping Rolling Updates
A deployment that works from zero replicas does not necessarily behave correctly during an upgrade.
Ignoring Storage
Persistent workloads require restart and remount testing.
Troubleshooting
Pod Is Running but Application Is Unavailable
Check:
Pod
|
+-- Container Port
+-- Readiness Probe
+-- Service
+-- Target Port
+-- Ingress
Determine where traffic stops.
Pod Keeps Restarting
Inspect:
kubectl describe pod <pod-name>
Then review container logs:
kubectl logs <pod-name>
Look for:
Readiness Probe Fails
Verify:
Correct endpoint
Correct port
Application startup time
Required dependencies
Probe timing
Do not simply increase probe delays without understanding why readiness is failing.
Application Cannot Connect to PostgreSQL
Verify:
Service name
DNS resolution
Connection string
Secret configuration
Network policies
Database availability
Connection pool behavior
HPA Does Not Scale
Check:
Metrics availability
Resource requests
HPA configuration
Current utilization
Deployment reference
An HPA cannot make meaningful CPU-utilization decisions if the required resource configuration is missing or unsuitable.
Best Practices
Record the current Kubernetes and node versions.
Inventory all Kubernetes resources used by the application.
Validate the .NET container independently.
Review deployment manifests for deprecated APIs.
Test readiness and liveness probes.
Validate ConfigMaps and Secrets without exposing sensitive values.
Test Service and ingress routing.
Test resource requests and limits.
Test HPA behavior where applicable.
Test rolling updates.
Test graceful shutdown.
Test persistent volumes for stateful applications.
Test CronJobs and background workers.
Validate RBAC permissions.
Perform the upgrade in staging first.
Monitor application and cluster logs during the upgrade.
Keep a rollback strategy available.
Record the results of every compatibility test.
Advantages
Detects deployment problems before production.
Reduces risk during Kubernetes upgrades.
Provides a repeatable validation process for .NET workloads.
Helps identify problems outside the application code.
Improves confidence in rolling deployments.
Makes infrastructure compatibility testing more systematic.
Disadvantages
Requires a representative staging environment.
Kubernetes upgrades can involve many infrastructure components.
Testing storage and networking may require additional environments.
Some failures depend on cluster-specific configurations.
Maintaining compatibility tests adds operational work.
A Practical Kubernetes Compatibility Test
A complete validation process can be organized into these stages:
Kubernetes Upgrade
|
v
Environment Inventory
|
v
.NET Container Test
|
v
Manifest Compatibility
|
v
Staging Deployment
|
+--------------+--------------+
| | |
v v v
Networking Storage Security
| | |
+--------------+--------------+
|
v
Application Tests
|
+--------------+--------------+
| | |
v v v
API Worker CronJob
| | |
+--------------+--------------+
|
v
Rolling Update
|
v
Monitoring
|
v
Production Upgrade
This approach makes the upgrade a controlled engineering process rather than a single infrastructure event.
Conclusion
Kubernetes upgrades can affect .NET applications even when the application code itself has not changed. Deployment manifests, health probes, networking, storage, RBAC, autoscaling, background jobs, and container behavior all contribute to whether an application continues operating correctly after a cluster upgrade.
For teams preparing a Kubernetes 1.34 upgrade, the most useful preparation is to establish a staging environment, inventory the application's Kubernetes dependencies, validate the .NET container, test deployment resources, and perform realistic rolling-update scenarios.
The key is to test the complete application path rather than checking only whether pods become Running. A successful compatibility test should demonstrate that the .NET application starts correctly, receives traffic, connects to its dependencies, scales when required, handles shutdown correctly, and continues processing workloads throughout the Kubernetes upgrade process.