<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: LTS Lift</title>
    <description>The latest articles on DEV Community by LTS Lift (@ltslift).</description>
    <link>https://dev.to/ltslift</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4204688%2F20225966-3a97-4c47-b963-46714a48718f.png</url>
      <title>DEV Community: LTS Lift</title>
      <link>https://dev.to/ltslift</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ltslift"/>
    <language>en</language>
    <item>
      <title>Your repo isn't on .NET 8 in just one file: what to change before November 10</title>
      <dc:creator>LTS Lift</dc:creator>
      <pubDate>Sun, 25 Oct 2026 01:51:40 +0000</pubDate>
      <link>https://dev.to/ltslift/your-repo-isnt-on-net-8-in-just-one-file-what-to-change-before-november-10-8fk</link>
      <guid>https://dev.to/ltslift/your-repo-isnt-on-net-8-in-just-one-file-what-to-change-before-november-10-8fk</guid>
      <description>&lt;p&gt;On &lt;strong&gt;10 November 2026&lt;/strong&gt;, .NET 8 and .NET 9 both reach end of support. So does the in-process model for Azure Functions. After that date there are no more security patches for those runtimes. Nothing stops working on the 11th, but every vulnerability found from then on stays open.&lt;/p&gt;

&lt;p&gt;.NET 10 is the current LTS release, supported until November 2028, so it's the obvious target. And for a lot of projects the core of the upgrade really is a one-line change:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- &amp;lt;TargetFramework&amp;gt;net8.0&amp;lt;/TargetFramework&amp;gt;
&lt;/span&gt;&lt;span class="gi"&gt;+ &amp;lt;TargetFramework&amp;gt;net10.0&amp;lt;/TargetFramework&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The problem is that the version your app &lt;em&gt;actually builds and runs on&lt;/em&gt; isn't decided in that one line. In a typical repo it's spread across six or seven files, and each one can quietly keep you on .NET 8 after you think you've moved. This post walks through where to look, what to change, and what tends to break.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Project files, including the ones that don't look like they set a version
&lt;/h2&gt;

&lt;p&gt;The obvious place is &lt;code&gt;&amp;lt;TargetFramework&amp;gt;&lt;/code&gt; in each &lt;code&gt;.csproj&lt;/code&gt;, &lt;code&gt;.fsproj&lt;/code&gt; or &lt;code&gt;.vbproj&lt;/code&gt;. Less obvious:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;&amp;lt;TargetFrameworks&amp;gt;&lt;/code&gt; (plural)&lt;/strong&gt; for multi-targeted libraries, e.g. &lt;code&gt;net8.0;netstandard2.0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Properties.&lt;/strong&gt; Many solutions set &lt;code&gt;&amp;lt;TargetFramework&amp;gt;$(AppTfm)&amp;lt;/TargetFramework&amp;gt;&lt;/code&gt; and define &lt;code&gt;AppTfm&lt;/code&gt; in a &lt;code&gt;Directory.Build.props&lt;/code&gt; one or two folders up. Searching for &lt;code&gt;net8.0&lt;/code&gt; in csproj files finds nothing; the real value is in the props file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Old-style projects&lt;/strong&gt; use &lt;code&gt;&amp;lt;TargetFrameworkVersion&amp;gt;v4.6.1&amp;lt;/TargetFrameworkVersion&amp;gt;&lt;/code&gt;. Those are .NET Framework, which is a different (and bigger) migration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;net48&lt;/code&gt; / &lt;code&gt;net462&lt;/code&gt; TFMs&lt;/strong&gt; in SDK-style projects are also .NET Framework. 4.6.2 reaches end of support on 12 January 2027.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A quick first pass:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt; &lt;span class="nt"&gt;--include&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;.csproj &lt;span class="nt"&gt;--include&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;.props &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s2"&gt;"TargetFramework|AppTfm"&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your TFMs come from a property, change it in &lt;code&gt;Directory.Build.props&lt;/code&gt; once rather than editing every project.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. global.json
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;global.json&lt;/code&gt; pins the SDK used to build. If it says &lt;code&gt;8.0.404&lt;/code&gt;, a machine with only the .NET 10 SDK will refuse to build (or roll forward in ways you didn't intend), and CI will happily keep using SDK 8.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"sdk"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"10.0.100"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"rollForward"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"latestFeature"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two details: the version must be a full version number like &lt;code&gt;10.0.100&lt;/code&gt;, not &lt;code&gt;10.0&lt;/code&gt;. And &lt;code&gt;rollForward: latestFeature&lt;/code&gt; saves you from bumping the file for every patch release.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Dockerfiles and compose files
&lt;/h2&gt;

&lt;p&gt;Base images carry their own version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;mcr.microsoft.com/dotnet/sdk:8.0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;build&lt;/span&gt;
...
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; mcr.microsoft.com/dotnet/aspnet:8.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both lines need to move to &lt;code&gt;10.0&lt;/code&gt;. Things to know when you do it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The default .NET 10 image tags are Ubuntu 24.04, not Debian.&lt;/strong&gt; If your Dockerfile runs &lt;code&gt;apt-get install&lt;/code&gt; for anything Debian-specific, or uses a &lt;code&gt;-bookworm&lt;/code&gt; tag, check the package names.&lt;/li&gt;
&lt;li&gt;If you're coming from .NET 6 or 7: since .NET 8 the default container port is &lt;strong&gt;8080&lt;/strong&gt;, not 80. &lt;code&gt;EXPOSE&lt;/code&gt;, compose port mappings, Kubernetes manifests and App Service's &lt;code&gt;WEBSITES_PORT&lt;/code&gt; may all need updating.&lt;/li&gt;
&lt;li&gt;.NET 9 images no longer include zlib. If you call it natively, install it.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;docker-compose.yml&lt;/code&gt; often has its own image references (a worker, a migration job), sometimes with a variable tag like &lt;code&gt;${DOTNET_TAG}&lt;/code&gt;. Those are easy to forget.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  4. CI pipelines
&lt;/h2&gt;

&lt;p&gt;The build agent installs whatever SDK the pipeline asks for:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-dotnet@v4&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;dotnet-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;8.0.x&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Azure Pipelines it's the &lt;code&gt;UseDotNet@2&lt;/code&gt; task's &lt;code&gt;version&lt;/code&gt;. Matrix builds are a common hiding place: &lt;code&gt;dotnet-version: ${{ matrix.dotnet }}&lt;/code&gt; with the actual values defined a few lines above.&lt;/p&gt;

&lt;p&gt;One .NET 10 SDK change to know about if your scripts parse CLI output: some messages now go to stderr, and &lt;code&gt;--interactive&lt;/code&gt; defaults to true in user contexts.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Packages
&lt;/h2&gt;

&lt;p&gt;Move &lt;code&gt;Microsoft.AspNetCore.*&lt;/code&gt;, &lt;code&gt;Microsoft.EntityFrameworkCore.*&lt;/code&gt; and &lt;code&gt;Microsoft.Extensions.*&lt;/code&gt; to 10.0.x. For third-party packages, only upgrade what doesn't support .NET 10 or has a known vulnerability. An LTS upgrade is a bad time to take three unrelated major versions at once.&lt;/p&gt;

&lt;p&gt;The .NET 10 SDK audits &lt;strong&gt;transitive&lt;/strong&gt; packages during restore. With &lt;code&gt;TreatWarningsAsErrors&lt;/code&gt;, a build that was green on 8 can go red on 10 for reasons unrelated to your code. Look at what it flags rather than turning audit off.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet package list &lt;span class="nt"&gt;--outdated&lt;/span&gt;          &lt;span class="c"&gt;# .NET 10 SDK syntax&lt;/span&gt;
dotnet list package &lt;span class="nt"&gt;--vulnerable&lt;/span&gt; &lt;span class="nt"&gt;--include-transitive&lt;/span&gt;   &lt;span class="c"&gt;# older SDKs&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  6. Azure Functions: in-process has to become isolated
&lt;/h2&gt;

&lt;p&gt;This is the item that turns "a one-line change" into real work. The in-process model ends support on the same day, and .NET 10 Functions only run on the isolated worker model. Microsoft's &lt;a href="https://learn.microsoft.com/azure/azure-functions/migrate-dotnet-to-isolated-model" rel="noopener noreferrer"&gt;migration guide&lt;/a&gt; covers it in full. The steps that catch people:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Microsoft.NET.Sdk.Functions&lt;/code&gt; is replaced by the worker packages, and every &lt;code&gt;Microsoft.Azure.WebJobs.Extensions.*&lt;/code&gt; by its &lt;code&gt;Microsoft.Azure.Functions.Worker.Extensions.*&lt;/code&gt; equivalent.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;FunctionsStartup&lt;/code&gt; goes away; configuration moves into a &lt;code&gt;Program.cs&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;[FunctionName]&lt;/code&gt; becomes &lt;code&gt;[Function]&lt;/code&gt;, &lt;code&gt;ILogger&lt;/code&gt; comes from DI, and output bindings work differently (&lt;code&gt;IAsyncCollector&amp;lt;T&amp;gt;&lt;/code&gt; is gone).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Serialization switches to System.Text.Json.&lt;/strong&gt; Newtonsoft's &lt;code&gt;[JsonProperty]&lt;/code&gt; attributes are ignored without any error. Your function still runs, it just reads or writes different property names.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;FUNCTIONS_WORKER_RUNTIME&lt;/code&gt; changes to &lt;code&gt;dotnet-isolated&lt;/code&gt;. Change the setting and deploy the new code together, ideally through a staging slot and a swap. Doing one without the other gives you AZFD0013 errors in between.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  7. What compiles but behaves differently
&lt;/h2&gt;

&lt;p&gt;Going 8 → 10 crosses two sets of breaking changes (&lt;a href="https://learn.microsoft.com/dotnet/core/compatibility/9.0" rel="noopener noreferrer"&gt;9.0&lt;/a&gt; and &lt;a href="https://learn.microsoft.com/dotnet/core/compatibility/10.0" rel="noopener noreferrer"&gt;10.0&lt;/a&gt;). The ones I'd check first because they don't show up as build errors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;BinaryFormatter&lt;/code&gt; always throws (since 9).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;BackgroundService.ExecuteAsync&lt;/code&gt; now runs entirely as a Task, so synchronous work at the top of it no longer delays startup.&lt;/li&gt;
&lt;li&gt;Configuration keeps &lt;code&gt;null&lt;/code&gt; values.&lt;/li&gt;
&lt;li&gt;Cookie authentication returns 401 instead of redirecting to login for API endpoints.&lt;/li&gt;
&lt;li&gt;In Development the host validates DI scopes on build, so latent DI bugs suddenly appear locally.&lt;/li&gt;
&lt;li&gt;EF Core 9 throws if you apply migrations while the model has pending changes. EF Core 10 on Azure SQL may generate a migration that changes JSON columns to the new &lt;code&gt;json&lt;/code&gt; type. After upgrading, run &lt;code&gt;dotnet ef migrations add UpgradeCheck&lt;/code&gt;. If it isn't empty, read it before anything gets deployed.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Finding all of it at once
&lt;/h2&gt;

&lt;p&gt;Doing the search above by hand works, but it's easy to miss a compose file or a property two directories up. I wrote a small open-source CLI, &lt;strong&gt;dotnet-eol-scan&lt;/strong&gt;, that reads all of these sources as text and lists every .NET version it finds with its end-of-support date. It needs Node 18+, has no dependencies, and doesn't send anything anywhere.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx github:khoaanh10/dotnet-eol-scan
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is its output on a small demo repo: an ASP.NET Core API whose TFM comes from &lt;code&gt;Directory.Build.props&lt;/code&gt;, a Functions project still on in-process, a shared &lt;code&gt;netstandard2.0&lt;/code&gt; library, a Dockerfile, &lt;code&gt;global.json&lt;/code&gt; and a GitHub Actions workflow (run with &lt;code&gt;--date 2026-10-24&lt;/code&gt; so the numbers are stable):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[SOON] .github/workflows/build.yml  GitHub Actions setup-dotnet: 8.0  end of support: 2026-11-10 (17 days left)
[SOON] global.json  SDK pin (global.json): 8.0  end of support: 2026-11-10 (17 days left)
[SOON] src/Shop.Api/Dockerfile  Docker image (sdk): 8.0  end of support: 2026-11-10 (17 days left)
[SOON] src/Shop.Api/Dockerfile  Docker image (aspnet): 8.0  end of support: 2026-11-10 (17 days left)
[SOON] src/Shop.Api/Shop.Api.csproj  TargetFramework: net8.0  end of support: 2026-11-10 (17 days left)
         Resolved from $(AppTfm) in Directory.Build.props
[SOON] src/Shop.Functions/Shop.Functions.csproj  TargetFramework: net8.0  end of support: 2026-11-10 (17 days left)
[SOON] src/Shop.Functions/Shop.Functions.csproj  Azure Functions: in-process model  end of support: 2026-11-10 (17 days left)
         Must move to isolated worker model before upgrading to .NET 10
[OK  ] src/Shop.Core/Shop.Core.csproj  TargetFramework: netstandard2.0  end of support: -

EOL: 0 | Ending soon: 7 | Info: 0 | OK: 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Seven places, one of which (the API's TFM) wouldn't show up in a search for &lt;code&gt;net8.0&lt;/code&gt; in project files. It exits with code 1 when something is out of support or ending soon, so you can run it in CI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Scan for end-of-life .NET versions&lt;/span&gt;
  &lt;span class="na"&gt;shell&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bash&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npx --yes github:khoaanh10/dotnet-eol-scan#v0.2.0 . --md --fail-on eol | tee -a "$GITHUB_STEP_SUMMARY"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the upgrade, run it again: everything should read &lt;code&gt;OK&lt;/code&gt; except whatever you've deliberately left for later.&lt;/p&gt;

&lt;p&gt;Its limits, so you know what it won't catch: it doesn't run MSBuild, so &lt;code&gt;Condition&lt;/code&gt; attributes aren't evaluated, and properties set in &lt;code&gt;.targets&lt;/code&gt; files, environment variables or on the command line are reported as &lt;code&gt;INFO&lt;/code&gt; for you to check by hand. It doesn't look at NuGet packages. Use &lt;code&gt;dotnet list package --vulnerable&lt;/code&gt; for that.&lt;/p&gt;

&lt;h2&gt;
  
  
  A short version of the checklist
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Find every version pin: project files, &lt;code&gt;Directory.Build.props&lt;/code&gt;, &lt;code&gt;global.json&lt;/code&gt;, Dockerfiles, compose files, CI.&lt;/li&gt;
&lt;li&gt;Record baseline build warnings and test results &lt;strong&gt;before&lt;/strong&gt; changing anything.&lt;/li&gt;
&lt;li&gt;Move TFMs, SDK, images and CI to 10 in one branch.&lt;/li&gt;
&lt;li&gt;Update Microsoft packages to 10.0.x; read what the restore audit reports.&lt;/li&gt;
&lt;li&gt;Migrate any in-process Functions to the isolated worker.&lt;/li&gt;
&lt;li&gt;Build, test, run in Docker, and check for an unexpected EF migration.&lt;/li&gt;
&lt;li&gt;Compare against the baseline, then deploy to a non-production slot first.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The tool is MIT-licensed and on GitHub: &lt;a href="https://github.com/khoaanh10/dotnet-eol-scan" rel="noopener noreferrer"&gt;https://github.com/khoaanh10/dotnet-eol-scan&lt;/a&gt;. If it gets something wrong on your repo, an issue with a redacted snippet of the file is the most useful thing you can send.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article was written with help from an AI assistant. I checked the commands and the scanner output by running them, and the breaking changes against the Microsoft docs linked above.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>azure</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
