Back to all posts

.NET 8 and .NET 9 Support Ends November 10th, 2026: Upgrade Now

Posted on Aug 30, 2026

Posted in category:
Development
.NET

There is a dangerous phrase that often appears during application planning: “We are on an LTS release, so we have time.”

That is true, but it's only true for a period of time. Software teams are still getting used to the increased cadence of forced upgrades due to the Microsoft Support Policy, and that feeling of ok can creep up on you quickly, and you are caught with remaining time that is shorter than the team realizes.

Given the every-other-year cycle of expirations between LTS and Current both .NET 8 and .NET 9 reach end of support on November 10, 2026. After that date, applications may continue to run, but those runtime versions will no longer receive security updates or technical support from Microsoft. In our current culture of increased AI usage, discussions of rising AI security threats, and more, being on unsupported software is a risk that most teams should NOT be ok with.

That gives teams running either version a clear destination: .NET 10, the currently supported Long Term Support release. The important question is no longer whether the upgrade belongs on the backlog. It is whether the organization can upgrade, validate, and deploy before the support window closes.

Organizationally, the concept of "leap-frogging" over releases is also considered, such as jumping directly to .NET 11. This can work, but it still exposes you to risks, just different ones.

What Happens at End of Support

An application does not normally stop working the morning after its runtime reaches end of support. That is part of what makes these deadlines easy to ignore.

The real change is in the application's risk profile. Once a runtime is unsupported:

  • New security fixes are no longer provided for that runtime version.
  • Microsoft technical support is no longer available for problems specific to it.
  • Hosting-platform recommendations and tooling increasingly move toward supported versions.
  • Third-party packages may stop testing against the older target, and most likely will only ship updates that work with the new minimum versions
  • The eventual upgrade becomes more difficult as additional framework and dependency changes accumulate.
  • Malicious users may try to target those out of support, hoping to find something unpatched, maybe .NET or maybe third-party.

Microsoft's support policy also requires applications to remain current on available patch updates during the supported lifecycle. Merely targeting net8.0 is not enough if the deployed environment is several servicing releases behind. (IE: you cannot be on 8.0.0 right now, you should be on 8.0.30!)

The goal is therefore not just to change a target-framework value. The goal is to move the entire application onto a supported, patched, tested, and observable production baseline. Getting into a true cadence, or even automation, for doing this process where possible may become the best long-term practice.

Managing and Implementing the Change

Before we start, we should consult the Breaking Changes Guide to see what might be impacted and things that should be included in our testing plan as part of the upgrade

To actually implement the changes, Microsoft's new recommendation is to use GitHub Copilot with the Modernization interface to help with this. It works well, but let's look at how we do this manually in a practical & methodical approach.

We start simple, update the target version in the various project files:

Target the .NET 10 framework
<PropertyGroup>
    <TargetFramework>net10.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
    

After changing the target, restore packages and review every warning. Look specifically for:

  • Packages that do not support the new target framework.
  • Obsolete APIs that now have supported replacements.
  • ASP.NET Core authentication or authorization behavior changes.
  • Entity Framework Core provider and tooling version mismatches.
  • Serialization changes that could alter API contracts.
  • Blazor component-library compatibility.
  • Native workload, signing, or store requirements for .NET MAUI projects.

I have written before about why “it builds” is not a sufficient upgrade target. Compilation proves that the compiler accepted the project. It does not prove that authentication still redirects correctly, database queries still translate as expected, background jobs still execute, or the deployed environment contains the correct runtime.

Update Packages as a Deliberate Step

A framework upgrade is an appropriate time to inspect outdated dependencies, but upgrading every dependency simultaneously can make troubleshooting harder. There is a useful balance between leaving the application on incompatible packages and turning one controlled framework upgrade into an unrestricted modernization project.

Start by listing outdated packages:

List outdated NuGet dependencies
dotnet list package --outdated
    

Prioritize framework-coupled packages first, including:

  • Microsoft.EntityFrameworkCore packages and the database provider.
  • ASP.NET Core authentication packages.
  • Microsoft.Extensions packages.
  • OpenTelemetry and Application Insights integrations.
  • UI component libraries with explicit framework support matrices.

Keep related packages on compatible versions. For example, mixing EF Core runtime packages, design-time tooling, and a database provider from different major versions is an avoidable source of build-time and runtime surprises.

Let CI Prove the Build Environment

Once the repository targets .NET 10, the continuous-integration workflow should install and use the same SDK family.

Install .NET 10 in a GitHub Actions workflow
- name: Setup .NET
  uses: actions/setup-dotnet@v5
  with:
    dotnet-version: '10.0.x'

- name: Restore
  run: dotnet restore

- name: Build
  run: dotnet build --configuration Release --no-restore

- name: Test
  run: dotnet test --configuration Release --no-build
    

Use the current supported major version of each action and review the action's release notes before changing production workflows. If the application uses .NET MAUI workloads, native compilation, or platform signing, its workflow will need additional SDK, workload, Xcode, Android, and signing validation.

Test the Behaviors That Matter in Production

Automated tests are valuable, but the upgrade plan should explicitly test the behaviors whose failure would create the largest operational impact.

For ASP.NET Core and Blazor applications

  • Anonymous and authenticated navigation.
  • Login, logout, token renewal, and expired-session behavior.
  • Authorization policies and role-protected content.
  • Blazor circuit disconnect and reconnection behavior.
  • JavaScript interop, file uploads, and file downloads.
  • Error handling and proper 404 responses.
  • Response caching and any server-side output caching.

For EF Core applications

  • Migration generation and review.
  • Queries with complex projections, grouping, or provider-specific behavior.
  • Compiled queries and bulk-operation libraries.
  • Transaction boundaries and concurrency handling.
  • Performance of high-volume or frequently executed queries.

For hosted and background processes

  • Recurring-job registration and execution.
  • Queue processing and retry behavior.
  • Health checks and startup dependencies.
  • Configuration, secrets, certificates, and managed identity access.
  • Graceful shutdown and deployment-slot swaps.

These tests should be based on the application's real risk areas. A generic smoke test that loads the home page is helpful, but it will not tell you whether a nightly job, administrator workflow, or rarely used integration stopped working.

Validate the Deployment, Not Just the Artifact

A successful publish does not prove that the hosting environment is correct. After deployment, record and verify the actual runtime and application version.

At minimum, confirm:

  1. The intended release was deployed.
  2. The host is using a supported .NET runtime.
  3. Application startup completed without new warnings.
  4. Health checks are passing.
  5. Error rate, request duration, dependency duration, and resource usage remain within expected ranges.
  6. Background processing is continuing normally.

For Azure App Service, review the configured runtime stack as well as the application's deployment model. A framework-dependent deployment relies on an appropriate runtime being available on the host. A self-contained deployment carries its runtime with it, but that also makes runtime servicing the application's responsibility.

Do Not Wait for the Final Patch Tuesday

The November 10 date is the end of the support window, not the recommended start of the project.

A practical upgrade schedule should leave time for:

  • Repository and application inventory.
  • Package and tooling compatibility research
  • Code and configuration changes
  • QA and stakeholder validation.
  • A staged production deployment.
  • Observation under real traffic.
  • Rollback or follow-up corrections if something unexpected appears.

Organizations with multiple applications should prioritize internet-facing systems, applications handling sensitive information, shared services, and systems with limited automated test coverage. Those are often the applications where unsupported software creates the greatest risk or where an upgrade requires the most lead time.

The Practical Next Step

If your organization is still running .NET 8 or .NET 9, start with a one-hour inventory. Find every target framework, SDK pin, build workflow, container image, and hosting-runtime configuration.

From there, assign an owner and a production deployment date for each affected application. Treat November 10 as the deadline for completing and observing the migration—not as the day to begin it.

.NET upgrades are usually manageable when they are planned, tested, and completed while the existing platform is still supported. They become far more disruptive when an incident, security finding, hosting change, or unsupported dependency dictates the schedule for you.

Is your organization already on .NET 10, actively upgrading, or still determining what is running on .NET 8? I would be interested to hear where the biggest upgrade challenge is appearing for your team.