Support for .NET 8 and .NET 9 ends November 10, 2026. Upgrade .NET 8 projects to .NET 10 with five steps covering SDK pinning, packages and CI fixes.
You have a month to upgrade .NET 8 projects, and the nudge is not subtle. Microsoft’s .NET support policy lists November 10, 2026 as the end of support for both .NET 8 and .NET 9. After that date, neither receives security patches. The current long-term release, .NET 10, is supported until November 14, 2028.
The timing surprises people because .NET 8 is an LTS release and feels like it should have longer. It shipped on November 14, 2023, so the end date lands just under three years later. .NET 9, a standard-term release, got the same end date, which means skipping it and moving straight from 8 to 10 is the sensible route for most teams.
What follows is a five-step migration for a typical application or library: install the SDK, pin it, change the target, fix what breaks, and check the pipeline. It leans on Microsoft Learn pages and the support policy; anything I could not verify there is marked.
What You Need
A machine where you can install the .NET 10 SDK, a source repository under version control, and a working test suite. The last item matters most. A migration with no tests is a guess that production will confirm or deny.
Check the current patch levels too. The support policy page lists the latest patches as 8.0.31, 9.0.20 and 10.0.12, all released September 8, 2026. If your CI image pins an older 8.0 patch, update it first. Starting from the final servicing release removes a variable from every later diagnosis.
Make a branch, and decide how the upgrade will land. A single pull request that changes the target framework and nothing else is far easier to review than one that also refactors three services. Resist the urge to modernize in the same change.
Step 1: Install the .NET 10 SDK Side by Side
On Windows, Microsoft’s install page gives a one-line command: winget install Microsoft.DotNet.SDK.10. Run it from a terminal with administrator rights, since these installs are system-wide. The SDK includes the runtime, so you do not need a separate runtime download. To see other versions, winget search Microsoft.DotNet.SDK lists them.

Side-by-side installation is the norm, and installing .NET 10 does not remove .NET 8 or 9 by default. There is one setting to know about. The page describes a RemovePreviousVersion value that defaults to always, meaning the installer removes previous installations after the new one lands, and it warns that running apps might be interrupted or crash when older runtimes disappear. The exact scope of that removal is not spelled out, so test on a spare machine before you trust it. The registry value lives under HKLM\SOFTWARE\Microsoft\.NET and accepts never, always or nextSession. On a build server or a shared box, set it deliberately before installing.
Confirm what you now have with dotnet --list-sdks and dotnet --list-runtimes. The global.json documentation names the first of these as the way to see installed SDKs. If an old x86 copy of dotnet shadows the 64-bit one, where.exe dotnet shows the order, and Microsoft’s advice is to put the x64 entry first on the PATH.
Visual Studio users should know that it installs only one copy of the .NET SDK and removes the previous one when it upgrades. If you need a specific SDK to stay put, install the standalone SDK; the cost is losing automatic SDK updates through Visual Studio.
Step 2: Pin the SDK in global.json
Pinning keeps your laptop, your colleague’s laptop and the build agent on the same toolchain. Create or edit a global.json at the repository root. Microsoft’s own example for this scenario is:
{ "sdk": { "version": "10.0.100", "rollForward": "latestFeature" } } That selects 10.0.100 or any later 10.0 feature band or patch installed on the machine. Note that the version must be a full version number. Values like 10, 10.0 or 10.0.x are rejected, and the documentation shows “10.0” producing an error.
You can generate the file from the command line with dotnet new globaljson --sdk-version 10.0.100 --roll-forward latestFeature, adjusting the version to one you actually have installed. The default rollForward, when omitted, is patch, which is tighter than most teams want. At the other end, disable demands an exact match, and Microsoft recommends it if you use package lock files.
One .NET 10 detail: the SDK adds optional paths and errorMessage fields to global.json. The errorMessage field is handy for telling a new developer what to install instead of showing a cryptic failure. Microsoft labels both as .NET 10 SDK features, so use them once every machine has that SDK.
Step 3: Change the Target Framework and Update Packages
Open each project file and change the target framework moniker. For a single target, that is the element <TargetFramework>net10.0</TargetFramework> in place of net8.0. Libraries that other teams consume are a special case: use the plural TargetFrameworks with a semicolon-separated list such as net8.0;net9.0;net10.0 until your consumers have moved, and use the NET8_0 or NET10_0 preprocessor symbols for any code that must differ. Microsoft also recommends the OS-specific moniker net10.0-windows for WinForms and WPF projects.
Next, look at your dependencies. With the .NET 10 SDK, the command is noun first: dotnet package list --outdated. On SDK 9 and earlier it is dotnet list package. The same page documents --deprecated, --include-transitive, and --vulnerable, the last available from SDK 9.0.300 and not combinable with outdated or deprecated. Update packages with dotnet package add.
Update the Microsoft framework packages (the ones versioned in lockstep with the runtime) first, and the third-party ones second. Then build. Expect to spend most of your time here, reading compiler output line by line.
Step 4: Read the Breaking Changes and Fix the Build
Microsoft keeps a breaking-changes list for .NET 10, grouped by technology. It labels each entry as binary incompatible, source incompatible or behavioral. The page itself says it is a work in progress and not a complete list, which is worth taking literally.

A sample of what the list contains: default .NET container images now use Ubuntu; BufferedStream.WriteByte no longer performs an implicit flush; OpenSSL 1.1.1 or later is required on Unix; single-file apps no longer look for native libraries in the executable directory; MailAddress enforces validation for consecutive dots; System.Text.Json checks for property name conflicts; and WPF disallows empty ColumnDefinitions and RowDefinitions. Also in the SDK area, dotnet new sln now defaults to the SLNX file format, which may surprise a tool that expects the older .sln.
Behavioral changes are the dangerous ones, because the build passes and the output changes. The JSON and networking entries deserve a targeted test each if you rely on either. For the rest, follow the compiler: source-incompatible items fail loudly.
If your project is a large legacy codebase, note that the .NET Upgrade Assistant page now says the tool is officially deprecated. Microsoft recommends the GitHub Copilot modernization chat agent, included with Visual Studio 2026 and Visual Studio 2022 17.14.16 or later, which produces a migration plan and commits each change so you can roll back. Treat any automated change as a draft that your tests have to approve.
Step 5: Update the Pipeline, Containers and Hosts
Code that builds locally can still fail in CI. Change the SDK version your pipeline installs, align it with global.json, and rebuild from a clean cache. Update base images to the .NET 10 tags you have verified exist in your registry; the compatibility page notes that the default images now use Ubuntu, so anything that assumed another base distribution, such as a package install line, needs a second look.
Then deal with hosting. Runtimes on servers do not upgrade themselves. A framework-dependent app needs the .NET 10 runtime on the machine, while a self-contained app carries its own. Check which model each service uses before deployment night.
One adjacent deadline comes from the same Microsoft lifecycle page for 2026: on November 10 the in-process model for Azure Functions also reaches end of support for .NET apps. If you run functions in that model, add the move to the isolated model to your list rather than discovering it later.
Roll out the way you would any risky release: staging first, with real traffic replayed if you can, and a rollback that is only a redeploy of the previous build. Once production runs on net10.0, remove the old SDK pins and delete the multi-target lines that served the transition.
Troubleshooting: Common Failures
“A compatible .NET SDK was not found.” The global.json version names an SDK that is not installed. Run dotnet --list-sdks, compare, and either install the right one or loosen rollForward. Remember the version string must be complete.

The wrong SDK is chosen. The dotnet command searches for global.json starting from the current directory, while MSBuild starts from the solution file’s directory. A stray file in a parent folder can win. Search upward from both places.
A prerelease SDK warning appears. Microsoft’s documentation says projects built with a prerelease SDK show a warning. Set allowPrerelease to false or pin a release version.
Tests pass locally and fail in Docker. Check the base image first. The documented switch to Ubuntu images is the most likely cause of differences in installed libraries.
A dependency has no net10.0 build. Many libraries targeting netstandard2.0 or net8.0 run fine on newer runtimes, but I did not find a Microsoft statement that guarantees this, so test rather than assume. If a package is abandoned, dotnet package list --deprecated will often say so.
Running apps crash after an installer update. If running apps vanish when a runtime is removed, the RemovePreviousVersion default is a plausible culprit, since Microsoft warns that removing older runtimes can interrupt running apps.
Frequently asked questions
When does .NET 8 support end?
November 10, 2026, according to Microsoft’s .NET support policy. .NET 9 ends the same day. .NET 10, an LTS release, is supported until November 14, 2028.
Should I move from .NET 8 to 9 or straight to 10?
Straight to 10 for most teams. .NET 9 shares the November 10, 2026 end date, so stopping there buys you nothing.
Does my .NET 8 app stop working after November 10?
Nothing in the policy says it will stop running. It stops receiving security updates, which is a risk rather than an outage.
Is the .NET Upgrade Assistant still the right tool?
Microsoft’s page calls it officially deprecated and recommends the GitHub Copilot modernization chat agent in recent Visual Studio versions.
End-of-support dates reward the teams that treat them as ordinary maintenance. The ones that struggle are the ones that only learn the date from a vulnerability scanner. A month is enough to do this calmly, if you start with the small step of pinning an SDK and compiling against net10.0 on a branch this week. Even if the full rollout takes longer, you will know exactly how much work remains, and that knowledge is worth more than any estimate.
Sources
- .NET and .NET Core official support policy
- Products reaching end of support in 2026 (Microsoft Learn)
- Install .NET on Windows (Microsoft Learn)
- global.json overview (Microsoft Learn)
- Target frameworks in SDK-style projects (Microsoft Learn)
- dotnet package list (Microsoft Learn)
- Breaking changes in .NET 10 (Microsoft Learn)
- Overview of the .NET Upgrade Assistant (Microsoft Learn)
