.NET  

Migrating .NET 8 and 9 Apps Before November

.NET applications rarely become difficult to migrate because of the target framework alone.

The real challenge is usually the application ecosystem around it:

  • NuGet packages

  • ASP.NET Core dependencies

  • Entity Framework Core

  • Authentication libraries

  • Container images

  • CI/CD pipelines

  • Cloud hosting

  • Native dependencies

  • Build tooling

  • Runtime configuration

For teams running .NET 8 or .NET 9 applications, the support lifecycle creates a concrete planning requirement.

Microsoft currently lists both .NET 8 and .NET 9 as supported until November 10, 2026. .NET 8 is a Long Term Support release, while .NET 9 is a Standard Term Support release. .NET 10 is the current LTS target for teams planning a longer support window.

That means migration should not be treated as a last-minute framework upgrade.

A production migration is an application lifecycle project.

Why Migration Planning Should Start Early

A framework migration normally affects more than one project file.

Consider a typical solution:

MyCompany.sln
 |
 +-- Web
 +-- API
 +-- Application
 +-- Domain
 +-- Infrastructure
 +-- Tests
 +-- Worker
 +-- Shared

Changing:

<TargetFramework>net8.0</TargetFramework>

to:

<TargetFramework>net10.0</TargetFramework>

may be technically simple.

The difficult part is determining whether all the surrounding components are compatible.

For example:

.NET Runtime
    |
    +-- ASP.NET Core
    +-- EF Core
    +-- Authentication
    +-- OpenTelemetry
    +-- Database Provider
    +-- Cloud SDK
    +-- NuGet Packages
    +-- Container Image
    +-- CI/CD

A migration is complete only when the application and its deployment environment have been validated.

Understand the Support Timeline

The first step is to inventory the current runtime.

RuntimeRelease TypeSupport End
.NET 8LTSNovember 10, 2026
.NET 9STSNovember 10, 2026
.NET 10LTSNovember 14, 2028

Microsoft's official lifecycle documentation should be treated as the authoritative source when planning an upgrade because support dates can affect security patch availability and organizational compliance requirements.

The practical implication is straightforward:

If an application must remain on a supported runtime after November 10, 2026, the migration should be completed and validated before that date.

Choose the Target Framework

For teams moving from .NET 8 or .NET 9, .NET 10 is an obvious target when the goal is a longer support window.

The decision should nevertheless consider:

  • Application compatibility

  • Vendor support

  • Cloud platform support

  • Required third-party libraries

  • Internal release schedules

  • Testing capacity

  • Deployment constraints

Avoid choosing a target solely because it is the newest runtime.

The target must be supported across the entire application ecosystem.

Inventory the Existing Application

Before changing anything, create an inventory.

Start with the project files:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
  </PropertyGroup>

</Project>

Identify:

  • Target framework

  • SDK version

  • Runtime-dependent applications

  • Self-contained applications

  • Native dependencies

  • Package references

  • Build scripts

  • Container base images

For a solution with multiple projects, do not assume every project targets the same framework.

You may find:

Web       -> net8.0
Worker    -> net8.0
Legacy    -> net6.0
Tests     -> net8.0
Tooling   -> net9.0

The migration plan needs to account for all of them.

Check the Installed SDKs

Use:

dotnet --list-sdks

and:

dotnet --list-runtimes

Then check the active SDK:

dotnet --version

This is especially important when developers have multiple SDKs installed.

A local machine may build the application successfully using an SDK that is not available in CI.

Pin the SDK Used by the Repository

A global.json file can make SDK selection explicit.

For example:

{
  "sdk": {
    "version": "10.0.100",
    "rollForward": "latestPatch"
  }
}

The exact SDK version should match the version approved by your build environment.

The important principle is consistency:

Developer
     |
     v
Same SDK
     |
     v
CI
     |
     v
Same SDK
     |
     v
Release Build

Without consistent SDK selection, migration failures can become environment-specific.

Update the Target Framework

Once the inventory is complete, update the project:

<PropertyGroup>
  <TargetFramework>net10.0</TargetFramework>
</PropertyGroup>

For multi-targeted projects:

<PropertyGroup>
  <TargetFrameworks>net8.0;net10.0</TargetFrameworks>
</PropertyGroup>

Multi-targeting can be useful during a transition because it allows the application or library to validate compatibility incrementally.

However, do not keep an old target indefinitely simply because multi-targeting makes the migration easier.

The eventual objective should be clear.

Update NuGet Packages

Framework upgrades often expose package compatibility issues.

Start by listing outdated packages:

dotnet list package --outdated

Depending on the SDK version and tooling available in your environment, the exact command output can vary.

Review each package rather than blindly upgrading everything.

Important packages may include:

Microsoft.AspNetCore.*
Microsoft.EntityFrameworkCore.*
Microsoft.Extensions.*
Azure.*
OpenTelemetry.*
Authentication libraries
Database providers
Serialization libraries
Testing frameworks

A major package upgrade can introduce behavioral changes unrelated to the framework migration.

Keep changes understandable.

Check Package Compatibility

For each important dependency, verify:

Current version
       |
       v
Supported target framework
       |
       v
Target version
       |
       v
Breaking changes

A package that compiles under .NET 10 is not necessarily behaviorally compatible with your application.

Pay particular attention to:

  • Authentication

  • Database providers

  • Serialization

  • Logging

  • Observability

  • Native libraries

  • Cloud SDKs

Upgrade ASP.NET Core Applications Carefully

ASP.NET Core applications often depend on framework behavior across the entire HTTP pipeline.

Review:

Middleware
Routing
Authentication
Authorization
Dependency Injection
Configuration
Logging
Exception handling
Endpoints
Static files

For example:

var builder =
    WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

var app = builder.Build();

app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

app.Run();

Do not assume that a successful compilation proves the pipeline is unchanged.

Run integration tests that exercise authentication, authorization, routing, and error handling.

Review Entity Framework Core

If the application uses EF Core, treat the framework and EF Core upgrade as related but separately testable changes.

Review:

DbContext configuration
Migrations
LINQ queries
Database provider
Transactions
Concurrency
Query behavior
Generated SQL

Run the existing migration tests against a realistic database environment.

For important queries, compare behavior before and after the migration.

A query that returns the same records can still have different performance characteristics if the provider or generated SQL changes.

Review Authentication and Authorization

Authentication should receive explicit regression testing.

Test:

Login
Token validation
Cookie authentication
Refresh tokens
Role claims
Policy authorization
Expired credentials
Invalid credentials
Unauthorized requests
Forbidden requests

A migration should never be considered successful simply because the home page loads.

Security-sensitive functionality needs dedicated tests.

Update Container Images

Containerized applications have another dependency:

FROM mcr.microsoft.com/dotnet/aspnet:8.0

When moving to .NET 10, the base image should be reviewed:

FROM mcr.microsoft.com/dotnet/aspnet:10.0

The build image also needs to change:

FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build

Then rebuild the image from scratch.

For example:

docker build --no-cache -t myapp:upgrade-test .

Do not assume that updating the project file automatically updates the runtime inside the production container.

Review Self-Contained and NativeAOT Deployments

Some applications are deployed differently from framework-dependent ASP.NET Core applications.

Check whether the application uses:

Framework-dependent
Self-contained
Single-file
ReadyToRun
NativeAOT

Each deployment model can have different compatibility considerations.

If native dependencies exist, test them explicitly.

For example:

Application
    |
    +-- Native library
    +-- Database driver
    +-- OS dependency

A framework upgrade can expose compatibility issues that do not appear in a simple managed-code test.

Update CI/CD Pipelines

A common migration failure occurs when the application targets the new runtime but the pipeline still uses an older SDK.

For example:

- task: UseDotNet@2
  inputs:
    packageType: sdk
    version: '10.0.x'

The exact syntax depends on your CI/CD platform.

Verify all pipeline stages:

Build
  |
  v
Unit Tests
  |
  v
Integration Tests
  |
  v
Security Scan
  |
  v
Container Build
  |
  v
Deployment

Every stage should use a supported and intentional SDK/runtime configuration.

Test Production Configuration

A migration should be tested with production-like configuration.

That includes:

  • Environment variables

  • Connection strings

  • Authentication settings

  • Certificates

  • Logging

  • Feature flags

  • External service endpoints

  • Container configuration

Do not put real production secrets into a test environment merely to make it realistic.

Use controlled test credentials and representative configuration.

Performance Testing

Do not assume a runtime migration automatically improves performance.

Measure the application before and after.

Useful metrics include:

MetricWhy Measure It
Requests/secThroughput
p50 latencyTypical response
p95 latencyTail behavior
p99 latencyHigh tail behavior
CPUResource consumption
MemoryWorking set
AllocationsGC pressure
GC pausesRuntime behavior
Database latencyDownstream impact

The comparison should use the same workload and infrastructure.

A migration should not be declared faster because a developer machine produced a lower response time during one manual test.

Regression Testing

At minimum, test:

Unit Tests
Integration Tests
API Tests
UI Tests
Database Tests
Authentication Tests
Authorization Tests
Background Jobs
Messaging
Scheduled Tasks
Health Checks

For public APIs, also test response contracts.

For example:

{
  "id": 1001,
  "name": "Customer"
}

Verify that important consumers still receive the expected structure.

Common Migration Mistakes

Changing Only TargetFramework

Changing:

<TargetFramework>net8.0</TargetFramework>

to:

<TargetFramework>net10.0</TargetFramework>

is only the beginning.

Upgrading Every Package at Once

This creates a large change surface.

Separate framework migration from unrelated dependency upgrades where practical.

Ignoring Containers

The application may compile for .NET 10 while production still uses a .NET 8 runtime image.

Forgetting CI

A developer machine can have the required SDK while the build agent does not.

Skipping Integration Tests

Compilation does not validate runtime behavior.

Migrating Just Before the Deadline

A migration needs time for unexpected dependencies, vendor issues, testing failures, and rollout problems.

A Safer Migration Strategy

A staged approach reduces risk.

Phase 1: Assessment

Document:

Current runtime
SDK
Packages
Container images
Cloud services
Native dependencies
CI/CD

Phase 2: Compatibility

Upgrade critical dependencies and identify blockers.

Phase 3: Development Migration

Change the target framework and fix compilation issues.

Phase 4: Automated Testing

Run unit, integration, security, and API tests.

Phase 5: Performance Validation

Compare the new runtime against the existing production baseline.

Phase 6: Staging

Deploy the application to a production-like environment.

Phase 7: Canary or Controlled Rollout

Start with a limited percentage of traffic where the platform supports it.

Phase 8: Full Deployment

Expand the rollout after monitoring the required health signals.

Example Migration Checklist

[ ] Confirm current .NET version
[ ] Confirm support lifecycle
[ ] Choose target runtime
[ ] Inventory projects
[ ] Inventory NuGet packages
[ ] Review native dependencies
[ ] Update global.json
[ ] Update TargetFramework
[ ] Update packages
[ ] Update Docker images
[ ] Update CI/CD SDK
[ ] Run unit tests
[ ] Run integration tests
[ ] Run security tests
[ ] Run performance tests
[ ] Validate authentication
[ ] Validate database operations
[ ] Deploy to staging
[ ] Validate observability
[ ] Perform controlled production rollout
[ ] Remove obsolete runtime dependencies

Handling Applications That Cannot Migrate in Time

Some applications cannot be upgraded immediately.

Possible reasons include:

  • Unsupported third-party libraries

  • Vendor dependencies

  • Legacy native components

  • Internal compliance requirements

  • Large monolithic architectures

  • Limited testing coverage

Do not simply leave the application unchanged without documenting the risk.

Instead, create an explicit exception plan.

Document:

Application
Current runtime
Reason migration is blocked
Security impact
Owner
Temporary controls
Target migration date

The exact remediation strategy depends on organizational requirements.

Migration vs Rewrite

A runtime migration does not normally require rewriting the application.

A good first objective is:

Existing application
       |
       v
Supported target runtime

rather than:

Existing application
       |
       v
Completely new architecture

Combining a runtime upgrade with a major architectural rewrite dramatically increases the change surface.

Separate those projects unless there is a compelling reason to combine them.

Troubleshooting Migration Problems

Build Fails After Target Framework Change

Start with the first compiler error.

Check:

  • Package compatibility

  • Removed APIs

  • Changed APIs

  • SDK version

  • Target framework conditions

Do not fix dozens of downstream errors before addressing the first meaningful failure.

Application Builds but Fails at Runtime

Look at:

  • Startup configuration

  • Dependency injection

  • Authentication

  • Database provider

  • Native dependencies

  • Configuration binding

Run integration tests before deploying.

Container Starts but Health Checks Fail

Verify:

Runtime image
Port
Environment variables
Certificates
Database connectivity
Health-check endpoint

Performance Changes Unexpectedly

Compare the same workload under both runtimes.

Check:

CPU
Memory
GC
Database
Network
Threading
Serialization

Do not assume the runtime itself is responsible until profiling supports that conclusion.

Frequently Asked Questions

Do .NET 8 and .NET 9 stop working after November 10, 2026?

No. End of support does not normally mean that applications suddenly stop executing.

It means the Microsoft support lifecycle ends, including the normal servicing and security-update expectations associated with a supported release. Microsoft lists November 10, 2026 as the support end date for both .NET 8 and .NET 9.

Should a .NET 8 application move to .NET 10?

For teams that need a longer supported lifecycle, .NET 10 is a logical target because it is an LTS release. The application should still be evaluated for package, platform, and operational compatibility before migration.

Is upgrading the TargetFramework enough?

No.

You should also review packages, SDK selection, container images, CI/CD, deployment configuration, native dependencies, and runtime behavior.

Should I upgrade NuGet packages at the same time?

Some packages may need to be upgraded for compatibility.

However, avoid unnecessary dependency changes when possible because they increase the migration's change surface.

How long should a migration take?

There is no universal duration.

A small application with strong automated tests may require relatively little effort, while a large system with legacy dependencies can require significantly more planning.

Measure the actual application rather than estimating solely from the number of projects.

Conclusion

The approaching .NET 8 and .NET 9 support deadline should be treated as a planning milestone rather than a last-minute upgrade task.

Microsoft currently lists both releases as supported through November 10, 2026. Teams that need continued support beyond that point should establish a migration plan early, select an appropriate supported target, and validate the complete application stack.

A successful migration is more than changing:

<TargetFramework>net8.0</TargetFramework>

to:

<TargetFramework>net10.0</TargetFramework>

The complete path is:

Inventory
   |
   v
Compatibility Review
   |
   v
Framework Upgrade
   |
   v
Dependency Upgrade
   |
   v
Testing
   |
   v
Performance Validation
   |
   v
Staging
   |
   v
Controlled Production Rollout

The most important principle is:

Treat a framework migration as a production change, not a project-file change.

Starting early gives teams time to discover incompatible dependencies, validate application behavior, update deployment infrastructure, and roll out the new runtime without turning a support deadline into an emergency release.