The .NET release cadence decides how long Microsoft will patch the runtime your applications ship on, and therefore how often your team has to budget for an upgrade. This guide explains the annual November release train, the real difference between Long Term Support (LTS) and Standard Term Support (STS) releases, and the support dates for .NET 8, 9, 10 and 11. It then lays out a repeatable upgrade strategy covering planning, multi-targeting, tooling, testing and stakeholder communication.

What Is the .NET Release Cadence?#

Since .NET 5 shipped in November 2020, Microsoft has released one major version of .NET every November, usually launched at the .NET Conf online event. The support rule is fixed and public: even-numbered versions (.NET 6, 8, 10) are LTS releases, and odd-numbered versions (.NET 7, 9, 11) are STS releases.

Each version follows the same yearly rhythm. Previews arrive roughly monthly from early in the year, release candidates follow in late summer, and general availability (GA) lands in November. .NET 11 shows the pattern: Preview 1 shipped on February 10, 2026, Release Candidate 1 on September 8, 2026, and GA is planned for November 10, 2026. Release candidates carry a go-live license, so Microsoft supports them in production, but each RC is supported only until its successor ships.

The language and tools move in lockstep. C# 12 arrived with .NET 8, C# 13 with .NET 9 and C# 14 with .NET 10, and C# 15 is the default language version for projects that target .NET 11. Targeting net10.0 requires Visual Studio 2026 (version 18.x); Visual Studio 2022 17.14 can use the .NET 10 SDK only for .NET 9 and older targets and reports warning NETSDK1233 otherwise. Because the whole schedule is published years ahead, you can put platform work on a roadmap like any other recurring cost.

How .NET Support Works: LTS, STS and Servicing#

The difference between LTS and STS is the length of support, not the quality of the bits. Both release types go through the same engineering, security and release processes and receive the same monthly servicing:

  • LTS releases get three years of free support and patches from their GA date.
  • STS releases get 24 months, which works out to one year after the next release ships. Before .NET 9, the STS window was 18 months.

Every release moves through the same phases:

  1. Preview: for evaluation only, not supported in production.
  2. Go-live: release candidates that Microsoft supports for production use.
  3. Active support: monthly servicing with functional and security fixes.
  4. Maintenance: the final six months, limited to security fixes.
  5. End of life: no patches, no security fixes and no technical assistance.

Servicing updates ship on Patch Tuesday, the second Tuesday of each month, although a .NET release is not guaranteed every month. Updates are cumulative, and you are only supported if you run the latest patch. A fleet stuck on 10.0.3 while 10.0.12 is available runs a supported major version at an unsupported servicing level, missing months of vulnerability fixes. As of the September 8, 2026 Patch Tuesday, the current patches are 10.0.12, 9.0.20 and 8.0.31.

The SDK is versioned separately. SDK versions such as 10.0.1xx and 10.0.2xx are feature bands, identified by the hundreds digit in the third position, and new bands bring tooling improvements a few times a year. Microsoft recommends using the newest SDK even when you target an older runtime, because it can build every supported target framework. Operating system support is a separate axis: the .NET team follows each OS vendor's lifecycle, so .NET on an out-of-support Linux distribution or Windows release is not a supported configuration.

.NET Support Timeline for .NET 8, 9, 10 and 11#

The table reflects the official support policy as of September 2026. End-of-support dates are aligned with Patch Tuesday.

VersionTypeGA dateEnd of supportStatus in September 2026
.NET 8LTSNovember 14, 2023November 10, 2026Maintenance (security fixes only)
.NET 9STSNovember 12, 2024November 10, 2026Maintenance (security fixes only)
.NET 10LTSNovember 11, 2025November 14, 2028Active support
.NET 11STSNovember 10, 2026 (planned)November 2028 (planned)Release candidate with go-live support

For context, .NET 7 left support on May 14, 2024 and .NET 6 on November 12, 2024. The dotnet/core release notes currently list .NET 11's planned window as November 10, 2026 through November 9, 2028; treat the exact end date as provisional until GA.

The headline for 2026 is that .NET 8 and .NET 9 both leave support on November 10, 2026. Applications keep running after that date, but newly disclosed vulnerabilities stay open, and libraries and NuGet packages gradually drop the old targets. .NET 10, supported until November 14, 2028, is the natural destination. Commercial post-end-of-life patching exists (HeroDevs, for example, sells independently maintained builds of retired .NET versions), but Microsoft does not patch retired versions, so treat that as a stopgap rather than a strategy.

The 2025 Change: STS Releases Now Get 24 Months#

In September 2025, Microsoft extended STS support from 18 to 24 months, effective with .NET 9. LTS support stayed at three years and the November schedule did not change. The immediate effect was moving .NET 9's end of support from May 2026 to November 10, 2026.

The old rule made STS an awkward bet. An 18-month STS release expired six months after the next version shipped, and before the LTS release that preceded it, so early .NET 9 adopters had a shorter runway than teams that stayed on .NET 8. Now each STS release ends alongside the LTS release before it: .NET 8 and 9 end together in November 2026, and .NET 10 and 11 are both planned to end in November 2028. If you adopt every release, you still upgrade yearly, but you always get a one-year overlap, which makes STS a realistic choice for services that benefit from new runtime work a year earlier.

What About .NET Framework 4.8.x?#

.NET Framework follows a different model. Starting with version 4.5.2, it is defined as a component of Windows, and supported versions inherit the lifecycle of the Windows version they run on. .NET Framework 4.8.1, released in August 2022, is the latest version; Microsoft states that it will keep shipping with future Windows releases and stays supported on supported Windows versions, so 4.8 and 4.8.1 have no fixed end date today.

Older versions do have dates: .NET Framework 4.6.2 ends on January 12, 2027, 3.5 SP1 on January 9, 2029, and 4.5.2, 4.6 and 4.6.1 retired on April 26, 2022. Moving from 4.6.2 to 4.8 or 4.8.1 (4.8.1 is offered only on newer Windows releases) is usually a low-risk, in-place upgrade worth scheduling now.

Long support does not mean a healthy future. New runtime features, language versions and most performance work land in modern .NET, and many current libraries no longer target .NET Framework; EF Core 10, for example, requires the .NET 10 runtime. Keep 4.8.1 applications patched while you decide which to migrate, retire or leave alone, as described in the legacy modernization guide.

Getting Started: Inventory What You Run Today#

Every upgrade plan starts with facts: which SDKs and runtimes exist on machines and build agents, and what each repository targets.

Bash
# SDKs and shared runtimes installed on this machine or build agent
dotnet --list-sdks
dotnet --list-runtimes

# Selected SDK, host version, OS details and the global.json in effect
dotnet --info

# Count every target framework declared across a repository
grep -rhoE "<TargetFrameworks?>[^<]+" --include=*.csproj --include=*.props . \
  | sed -E 's/<TargetFrameworks?>//' | tr ';' '\n' | sort | uniq -c

The target framework in a project file is not always what runs in production. Framework-dependent apps can roll forward to a newer runtime depending on the RollForward setting, and hosting platforms may carry a different patch than you expect. Log the actual runtime at startup:

C#
using System.Runtime.InteropServices;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

// Record exactly which runtime served traffic; roll-forward can surprise you.
app.Logger.LogInformation(
    "Runtime {Framework} on {Rid}, OS {OS}",
    RuntimeInformation.FrameworkDescription,
    RuntimeInformation.RuntimeIdentifier,
    RuntimeInformation.OSDescription);

app.MapGet("/health", () => Results.Ok());
app.Run();

Then make SDK selection deterministic. Without a global.json, the CLI uses the newest installed SDK, so two developers can build the same commit with different toolchains. Pin a baseline and allow newer feature bands:

JSON
{
  "sdk": {
    "version": "10.0.100",
    "rollForward": "latestFeature"
  }
}

The latestFeature policy picks the highest installed feature band and patch within 10.0 but never jumps to another major version. The default, patch, stays within the specified feature band.

How to Plan a .NET Upgrade#

A good upgrade plan is boring on purpose: a checklist any team can execute, for one service or hundreds.

  1. Inventory applications, target frameworks, the runtime actually deployed, hosting model and owners.
  2. Choose the destination. For most organizations in late 2026 that is .NET 10, the current LTS release.
  3. Read every breaking-change list you cross. Moving from .NET 8 to .NET 10 means reading the .NET 9 and .NET 10 compatibility pages.
  4. Clear dependency blockers first. Packages without a compatible release usually drive the timeline more than your own code.
  5. Prepare infrastructure: SDKs on build agents, container base images, hosting runtimes and IDE versions.
  6. Upgrade bottom-up: shared libraries first (often multi-targeted), then services, then edge applications.
  7. Roll out gradually with a documented rollback plan, then remove the old target.

Centralizing target frameworks makes step 6 cheap. Define them once in Directory.Build.props and reference them from projects, so moving the repository becomes a one-line change; the MSBuild and project system guide covers these files in depth.

XML
<Project>
  <PropertyGroup>
    <!-- Change these two lines to move the whole repository -->
    <LtsTargetFramework>net10.0</LtsTargetFramework>
    <PreviousLtsTargetFramework>net8.0</PreviousLtsTargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <!-- Audit transitive packages for vulnerabilities on every target -->
    <NuGetAuditMode>all</NuGetAuditMode>
  </PropertyGroup>
</Project>

Containers deserve their own line item. In .NET 10, the default image tags (those without an explicit OS) are based on Ubuntu 24.04 instead of Debian, and Debian-based images are no longer provided.

Dockerfile
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
COPY . .
RUN dotnet publish src/Orders.Api/Orders.Api.csproj -c Release -o /app

# Default 10.0 tags are Ubuntu-based; test anything that shells out or uses native libs
FROM mcr.microsoft.com/dotnet/aspnet:10.0
WORKDIR /app
COPY --from=build /app .
USER $APP_UID
ENTRYPOINT ["dotnet", "Orders.Api.dll"]

Multi-Targeting During a Migration#

Applications normally target one framework, but shared libraries often serve consumers on different versions while a portfolio migrates. Multi-targeting compiles one project for several frameworks and packs them into a single NuGet package, so each consumer gets the best match.

XML
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFrameworks>$(PreviousLtsTargetFramework);$(LtsTargetFramework)</TargetFrameworks>
  </PropertyGroup>

  <!-- EF Core 10 requires .NET 10, so each target gets its own major version -->
  <ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.31" />
  </ItemGroup>
  <ItemGroup Condition="'$(TargetFramework)' == 'net10.0'">
    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.12" />
  </ItemGroup>
</Project>

The SDK defines preprocessor symbols for each target, including the _OR_GREATER family (NET8_0_OR_GREATER, NET9_0_OR_GREATER, NET10_0_OR_GREATER). Prefer these over exact-version symbols so code stays correct when you add the next target.

C#
namespace Contoso.Metrics;

public sealed class RollingWindow
{
#if NET9_0_OR_GREATER
    private readonly Lock _gate = new(); // dedicated lock type added in .NET 9
#else
    private readonly object _gate = new();
#endif
    private readonly Queue<double> _samples = new();

    public void Add(double value)
    {
        lock (_gate)
        {
            _samples.Enqueue(value);
            if (_samples.Count > 100)
            {
                _samples.Dequeue();
            }
        }
    }
}

Keep multi-targeting temporary for applications and deliberate for libraries. Every extra target multiplies build and test effort, so drop the older framework once its last consumer has moved or it leaves support. For published packages, enable package validation (EnablePackageValidation) so removing a target or changing an API surface is flagged before release.

Upgrade Tooling: Upgrade Assistant, Copilot Modernization and the CLI#

Microsoft now marks the .NET Upgrade Assistant as deprecated and directs developers to the GitHub Copilot modernization agent. The agent ships with Visual Studio 2026 and recent Visual Studio 2022 17.14 updates and is also available for Visual Studio Code. It requires a GitHub Copilot plan, and Microsoft's modernization page lists C# projects in Git repositories as the supported scope.

The agent analyzes projects and dependencies, proposes a step-by-step plan, applies code and project-file changes, and commits each step to Git so you can review or roll back individual changes. It covers upgrades from .NET Framework or older .NET versions as well as Azure migration. Treat its output like a pull request from a capable new colleague: review every diff and insist on a green test suite. The AI-assisted development guide covers review practices for agent-generated code.

The CLI remains the backbone of any upgrade because it behaves the same on every machine and CI agent:

Bash
# .NET 10 SDK noun-first form
dotnet package list --outdated
dotnet package list --vulnerable --include-transitive

# .NET 9 SDK and earlier use the verb-first form
dotnet list package --outdated

Two SDK behaviors matter here. For projects that target .NET 10 or later, NuGetAuditMode defaults to all, so restore warns about vulnerable transitive packages, and builds with warnings-as-errors may fail after the upgrade. New obsoletions surface as SYSLIB diagnostics; resolve them rather than suppressing them, because obsoleted APIs are candidates for removal.

Testing Strategy for .NET Upgrades#

Upgrades rarely break at compile time. The risky failures are behavioral: serialization details, changed defaults, container OS differences, performance regressions and packages that behave differently on the new runtime.

Capture a baseline before changing anything: a green test suite, benchmark numbers for hot paths and production telemetry such as p95 latency, error rate, CPU, memory and garbage collection counts. Then run the same tests against both frameworks on every commit:

Bash
# Run the same suite against the current and the next target framework
for tfm in net8.0 net10.0; do
  dotnet test tests/Orders.Tests/Orders.Tests.csproj -c Release -f "$tfm"
done

Unit tests alone are not enough. Integration tests against real databases, brokers and HTTP endpoints catch configuration and serialization differences that mocks hide, as the integration testing guide explains. For hot paths, BenchmarkDotNet can run the same benchmarks on several runtimes in one invocation when the benchmark project targets each framework:

C#
using BenchmarkDotNet.Running;

// Program.cs of a benchmark project with <TargetFrameworks>net8.0;net10.0</TargetFrameworks>
BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);
Bash
dotnet run -c Release -f net8.0 --filter "*Serialization*" --runtimes net8.0 net10.0

Finish with a staged rollout. Deploy to a canary slice or a single region, compare telemetry with the baseline for a full business cycle, and keep the previous artifacts ready for a fast rollback.

Communicating the Upgrade to Stakeholders#

Engineers often pitch upgrades as technical hygiene, which is easy to postpone. Stakeholders respond better to fixed dates, risk and cost: the end-of-support date is not negotiable, and missing it means unpatched vulnerabilities, audit findings, blocked dependency updates and a more expensive jump later.

AudienceWhat they care aboutWhat to give them
Executives and product ownersRisk, cost, delivery impactEnd-of-support date, effort estimate, plan to avoid feature freezes
Security and compliancePatch status, audit exposureSupport timeline, vulnerability scan results, completion date
Operations and SREStability and rollbackRollout plan, canary criteria, rollback steps, telemetry baseline
Development teamsScope and toolingBreaking-change summary, SDK and IDE requirements, blockers

Make the work visible and recurring: a platform upgrade line in every annual plan, a one-page calendar showing each application's current version and target date, and progress reported alongside feature work.

Best Practices#

  • Default to LTS for most workloads. Two-year cycles with a one-year overlap suit most enterprise and regulated teams.
  • Start soon after GA. Moving within six months of a new LTS release leaves a comfortable buffer.
  • Stay on the latest patch. Automate runtime and image updates so monthly servicing reaches production.
  • Use the newest SDK with a pinned baseline. global.json with latestFeature gives reproducible builds without blocking fixes.
  • Centralize target frameworks and package versions so a repository-wide move is a small diff.
  • Update dependencies continuously to keep upgrade-time jumps small.
  • Capture baselines first. Old-version tests, benchmarks and telemetry are your proof that the new version is no worse.

Common Pitfalls#

  • Assuming a supported major version means you are covered. Only the latest patch is supported.
  • Reading only the newest breaking-change page when the jump crosses two releases.
  • Forgetting IDEs and build agents. Visual Studio 2022 cannot target net10.0, and agents without the new SDK fail late.
  • Letting warnings-as-errors block restore. Triage transitive audit warnings instead of disabling auditing.
  • Missing container changes such as the Ubuntu-based default tags, which can affect scripts and native dependencies.
  • Merging large AI-generated upgrades unreviewed. Automation speeds up mechanical work; correctness still depends on review and tests.

LTS vs STS: Which Should You Choose?#

The right choice depends on how quickly your organization can ship a platform upgrade and how much it gains from new runtime features.

ConsiderationLTS (.NET 8, 10, 12)STS (.NET 9, 11)
Support length3 years24 months (since .NET 9)
Typical upgrade frequencyEvery 2 yearsEvery year
New features and performance workUp to a year laterAs soon as each release ships
Overlap with the next versionAbout 1 year after the next LTSAbout 1 year after the next release
Good fitRegulated industries, large portfolios, on-premises softwareCloud services with strong CI/CD and automated tests
Main riskLarge jumps if upgrades slipYearly upgrade work competes with features

A hybrid approach works well in larger organizations: keep the portfolio on LTS releases and let a few well-tested services adopt STS early to capture gains and surface issues before the next LTS arrives. Following the even-number rule, .NET 12 is expected to be the next LTS release in November 2027.

Frequently Asked Questions#

Is an STS release less stable than an LTS release?#

No. Microsoft builds, tests and services both release types with the same engineering and release processes, and both are fully supported in production. The only difference is support length: three years for LTS and 24 months for STS.

When does .NET 8 support end, and what happens then?#

.NET 8 reaches end of support on November 10, 2026, the same day as .NET 9. Applications keep running, but Microsoft stops shipping security fixes and technical support, so new vulnerabilities remain unpatched and the package ecosystem gradually drops the target.

Should I upgrade from .NET 8 directly to .NET 10?#

Yes, for most teams. .NET 10 is the current LTS release, and stopping at .NET 9 adds little because it ends on the same day as .NET 8. Review the breaking changes for both .NET 9 and .NET 10, since a direct jump still crosses both releases.

Is .NET Framework 4.8.1 going out of support?#

Not on a fixed date. It is a Windows component and stays supported while it runs on a supported Windows version. Older versions have deadlines: 4.6.2 ends on January 12, 2027 and 3.5 SP1 on January 9, 2029.

Can I still use the .NET Upgrade Assistant?#

Microsoft lists it as deprecated and recommends the GitHub Copilot modernization agent in Visual Studio 2026 and recent Visual Studio 2022 17.14 updates. The agent plans the upgrade, applies changes and commits each step to Git, and it requires a GitHub Copilot plan.

Summary#

  • .NET ships every November; even-numbered versions are LTS (3 years) and odd-numbered versions are STS (24 months since .NET 9).
  • .NET 8 and .NET 9 both end on November 10, 2026; .NET 10 LTS is supported until November 14, 2028.
  • Only the latest monthly patch counts as supported, so automate servicing.
  • .NET Framework 4.8.1 follows the Windows lifecycle, but new investment goes to modern .NET.
  • Plan bottom-up, centralize target frameworks, multi-target shared libraries and verify with tests, benchmarks and staged rollouts.

Further Reading#