.NET 11 Release Explained: What’s New for Cloud-Native Services, Performance, C# 15 and the Upgrade Path from .NET 10 LTS
For a decade the rule for .NET teams was simple: production runs on Long Term Support, and the odd-numbered Standard Term Support releases are for the adventurous. .NET 11 breaks that rule quietly. It shipped on November 10, 2026 as an STS release with 24 months of support, which means it reaches end of support in November 2028, the same month as .NET 10 LTS. For the first time, choosing the “short” release costs you no support runway at all.
That matters now because .NET 8 and .NET 9 both went out of support on the same day .NET 11 shipped. Thousands of services must move somewhere this quarter, and the choice between 10 and 11 is no longer about lifecycle risk. It is about breaking-change cost, hardware floors and whether runtime-native async pays for itself in your workload.
You will leave with a layer-by-layer map of what changed, Microsoft’s published benchmark numbers with caveats, working C# 15 and ASP.NET Core 11 code, a decision table and a migration checklist.
What this covers: the dotnet 11 release and support policy, runtime async, JIT and Native AOT, C# 15 features, ASP.NET Core 11, Aspire and containers, net 11 performance evidence, breaking changes, and how to upgrade net 10 to net 11 safely.
Context and Background
Microsoft ships a major .NET version every November. Even-numbered releases are Long Term Support (LTS) and odd-numbered releases are Standard Term Support (STS). According to the official .NET releases and support page, LTS releases get a minimum of three years of support, or one year after the next LTS ships, whichever is later. STS releases now get 24 months. Before the 2025 policy change, STS meant 18 months, which is why many platform teams banned odd-numbered releases outright.
The longer STS window produced an unusual alignment. The .NET Blog’s end-of-support notice confirmed that .NET 8 (LTS) and .NET 9 (STS) both reached end of support on November 10, 2026. Microsoft’s support table lists .NET 10 as LTS from November 2025 to November 2028, and .NET 11 as STS from November 2026 to November 2028. Two versions, one end date.
The incumbents in the cloud-native runtime market have not stood still either. Java has virtual threads and a long-lived LTS rhythm; Go keeps winning on small static binaries; Python is pushing its own performance and packaging work, which we covered in our Python 3.15 migration tutorial for ML pipelines. .NET’s answer in this cycle is less about headline features and more about the plumbing: how async methods execute, how the JIT removes checks, and how containers are built and observed.
The official announcement of .NET 11 frames the release around C# 15, “hundreds of runtime optimizations,” SDK speed-ups, and AI developer tooling such as an MCP server template. Most coverage you will find repeats that list. The angle in this post is narrower and, I think, more useful to anyone running services: with support parity, .NET 11 becomes a cost-benefit decision per service, and the deciding variables are your async profile, your CPU fleet and your exposure to more than fifty documented breaking changes.
That reframing changes who should make the call. It is no longer a governance rule for the architecture board. It is an engineering measurement that each service owner can make in a sprint, with a benchmark, a canary and a rollback plan.
.NET 11 Reference Architecture for Cloud-Native Services
Direct answer: .NET 11 is the November 2026 STS release of Microsoft’s .NET platform, supported for 24 months until November 2028. For cloud-native services it brings runtime-native async, a smarter JIT, faster Native AOT interface dispatch, C# 15 union types, OpenAPI 3.2 by default in ASP.NET Core 11, built-in OpenTelemetry HTTP spans and reproducible container image digests.

Figure 1: The .NET 11 path from source to running service. C# 15 code compiles to IL with runtime-async methods, runs under the tiered JIT, ReadyToRun or Native AOT, hosts ASP.NET Core 11, and ships as a container that exports OTLP telemetry.
Read the figure top to bottom. Source code in C# 15 compiles with the .NET 11 SDK targeting net11.0. The output IL can carry methods compiled for runtime async. At deployment you choose one of three execution modes: the JIT with tiered compilation, ReadyToRun (R2R) precompiled code that the JIT can later replace, or Native AOT, which produces a self-contained native binary. Each mode now has different hardware assumptions. ASP.NET Core 11 sits on top, the SDK packages it as an OCI container, and the service emits traces and metrics to an OpenTelemetry collector.
Layer 1: runtime async is the change that touches every request
Since C# 5, the compiler has turned every async method into a hidden state-machine class. Each await point becomes a state, and the method builder allocates when execution truly suspends. That design was portable but expensive in subtle ways: deep call chains of small async methods paid builder overhead at each layer, and stack traces were full of infrastructure frames.
Runtime async, which Microsoft’s runtime documentation also calls Runtime Async V2, moves suspension and resumption tracking into the runtime itself. The JIT compiles a dedicated runtime-async version of a method instead of going through a thunk. Tiered compilation applies to async methods, Task.FromResult, Task.CompletedTask and ValueTask.FromResult fold into faster paths, and continuations skip ExecutionContext capture when there is no ambient state to restore. The .NET 11 runtime what’s-new page gives a concrete diagnostic example: the same chained-await program shows 13 stack frames under classic async and 5 under runtime async.
There is a status nuance you must get right. The announcement still labels runtime async as preview and tells you to opt in with <Features>runtime-async=on</Features>. At the same time, Microsoft compiled the .NET 11 runtime libraries themselves with runtime async on, and a net11.0 project no longer needs EnablePreviewFeatures. In practice, your service already benefits inside framework code such as Kestrel and System.Net.Http; your own code benefits only when you opt in.
Why does this matter more for services than for desktop apps? Because a typical ASP.NET Core request walks through middleware, a handler, a service layer, a repository and a database driver, each with one or more awaits. Many of those awaits complete synchronously from caches or buffered streams. Runtime async makes that synchronous-completion path nearly free, which is exactly the path that dominates a hot, well-cached API.
Layer 2: the JIT and Native AOT get cheaper per call
The JIT work in .NET 11 is a long list of local wins rather than one big feature. The runtime documentation names bounds-check elimination for i + cns < len patterns and index-from-end access such as values[^1], removal of redundant checked contexts, folding of switch expressions like x is 0 or 1 or 2 or 3 or 4 into branchless checks, and constant-folding of string.Equals and SequenceEqual on compile-time constants.
The more important item for typical business code is devirtualization. The .NET 11 JIT can devirtualize generic virtual methods, default interface methods on generic interfaces, and objects created by Activator.CreateInstance<T>(). Devirtualization matters because once a call is non-virtual it can be inlined, and once inlined, escape analysis can remove allocations such as boxed enumerators. Layered enterprise code that hides everything behind interfaces is exactly where these wins accumulate.
Native AOT gets a structural change too. Interface calls now route through a shared dispatch helper that is patched to the correct implementation after warm-up, which shrinks call sites and improves throughput on interface-heavy workloads. Generic virtual methods use the same dispatch-cell infrastructure. Microsoft publishes no numeric figure for this change, so treat it as a direction, not a number.
Hardware support moves forward in both directions. The JIT now uses hardware FP16 instructions for Half on capable x64 and Arm64 CPUs, adds AVX-VNNI-512 and Arm SVE2 intrinsics, and makes Math.BigMul(long, long, out long) emit a single MUL on x64. The flip side is a raised floor, covered in the gotchas section, that can stop .NET 11 from starting at all on older virtual machine types.
Layer 3: the service surface, libraries and container plumbing
ASP.NET Core 11 is a quieter release than some, but three changes matter for platform teams. OpenAPI 3.2 is now the default document version. ASP.NET Core adds OpenTelemetry semantic-convention attributes such as http.request.method, url.path and http.response.status_code directly to the request activity, so you subscribe to the Microsoft.AspNetCore source without an extra instrumentation package. And a new [ShortCircuit] attribute runs an endpoint right after routing, which is ideal for health probes that should not pay for authentication or CORS middleware.
The libraries add a modern Process API with run-and-capture helpers and Process.Signal(PosixSignal), Zstandard compression in System.IO.Compression, CRC32 validation on ZIP reads, JSON Lines output from async sequences in System.Text.Json, union support in System.Text.Json, a typed DnsResolver, QuicStream.Priority for HTTP/3 prioritization, configurable HTTP connection eviction, and built-in OpenTelemetry metrics for MemoryCache. The security stack adds an X25519DiffieHellman class and experimental composite ML-KEM and HPKE APIs, extending the post-quantum work started in .NET 10.
Container publishing is the last layer. The SDK can now produce reproducible image digests by honouring SOURCE_DATE_EPOCH, skips layer uploads when the target manifest already exists, builds multi-architecture images with Podman, and prefers platform-native local runtimes before Docker and Podman. Reproducible digests sound minor until you run supply-chain policy: if two builds of the same commit yield the same digest, signature and provenance checks become far simpler.
Walk-through: Performance Evidence, C# 15 Code and the Migration Path
The sections above describe what changed. This section asks the harder question: how much of it shows up in a real service, and what does the code look like when you adopt it? We start with Microsoft’s own numbers, then move to code, then to the migration mechanics.
What Microsoft’s benchmarks actually show
Stephen Toub’s annual post, Performance Improvements in .NET 11, published on September 15, 2026, is the primary source for net 11 performance claims. It uses BenchmarkDotNet in Release mode, compares .NET 10 against .NET 11, and explicitly warns that results vary with hardware, operating system and configuration. The table below reproduces a subset of the reported microbenchmarks most relevant to services.
| Benchmark (as named in the post) | Baseline | Mean | Ratio | Allocated |
|---|---|---|---|---|
| Completed async chain | .NET 10 classic async | 21.221 ns | 1.00 | 144 B |
| Completed async chain | .NET 11 runtime async | 6.151 ns | 0.29 | 0 B |
| Yielding async chain | .NET 10 classic async | 254.139 ns | 1.00 | 248 B |
| Yielding async chain | .NET 11 runtime async | 116.927 ns | 0.46 | 168 B |
| Exception through 10 async frames, no yield | Classic | 19.469 µs | 1.00 | 15.13 KB |
| Exception through 10 async frames, no yield | Runtime async | 5.885 µs | 0.30 | 2.13 KB |
| Exception through 30 async frames, yielding | Classic | 65.974 µs | 1.00 | 85.53 KB |
| Exception through 30 async frames, yielding | Runtime async | 11.254 µs | 0.17 | 7.72 KB |
| Generic virtual devirtualization | .NET 10 | 6.678 ns | 1.00 | 24 B |
| Generic virtual devirtualization | .NET 11 | 1.764 ns | 0.26 | 0 B |
| Read-only collection enumeration | .NET 10 | 13.874 ns | 1.00 | 32 B |
| Read-only collection enumeration | .NET 11 | 2.674 ns | 0.19 | 0 B |
Methodology caveats. These are microbenchmarks that isolate one mechanism. They measure nanoseconds per call on an unnamed test machine, not requests per second on your cluster. A 3.4x speed-up on a completed async chain does not mean your API gets 3.4x faster, because most request time goes to I/O, serialization and the database. The runtime documentation adds that Arm64 IndexOfAnyAsciiSearcher is 5 to 50 percent faster in workloads that use it in their core loop, which is a good example of a range that depends entirely on the workload.
The pattern in these numbers is still useful. Every large win comes with an allocation reduction: 144 bytes to zero, 24 bytes to zero, 85 KB to under 8 KB. In a service, fewer allocations mean fewer gen0 garbage collections, which means fewer pauses and lower tail latency under load. That is the mechanism to watch, and it is measurable with the System.Runtime GC counters you probably already export.
A worked capacity estimate, clearly labelled
To make the numbers concrete, consider an illustrative service, not a measured one. It handles 2,000 requests per second per pod, and each request runs through roughly 15 async methods that complete synchronously from an in-memory cache. Using the reported 144 bytes saved per completed chain as a rough per-method proxy, the saving is about 2,000 x 15 x 144 bytes, or around 4.3 MB per second of avoided allocation per pod.
At a gen0 budget of a few megabytes, that can mean one or two fewer gen0 collections per second on that pod. Whether that moves your p99 latency depends on what else allocates. If serialization allocates 50 KB per request, the async saving is a rounding error. If you have already tuned serialization with source generation and pooled buffers, async overhead may be a large share of what remains. The lesson: benchmark your own service before and after; the microbenchmark tells you where to look, not what you will get.
C# 15 features that matter in service code
The What’s new in C# 15 page lists six shipped features plus a memory-safety preview. C# 15 is the default language version for net11.0, and everything except memory safety works without LangVersion set to preview. Three of the c# 15 features change how service code is shaped.
Union types let a value be exactly one of a declared set of case types, with exhaustiveness checking in switch expressions. For APIs, unions are a natural fit for results that are either a success payload or one of several typed errors.
public record class Order(Guid Id, decimal Total);
public record class NotFound(Guid Id);
public record class Conflict(string Reason);
public union OrderResult(Order, NotFound, Conflict);
app.MapGet("/orders/{id:guid}", async (Guid id, IOrderStore store, CancellationToken ct) =>
{
OrderResult result = await store.FindAsync(id, ct);
return result switch
{
Order o => Results.Ok(o),
NotFound n => Results.NotFound(new { n.Id }),
Conflict c => Results.Conflict(new { c.Reason }),
};
});
If someone later adds a fourth case to OrderResult, every switch that forgets it produces a compiler warning. ASP.NET Core 11 supports unions in JSON request and response bodies and describes them with anyOf in OpenAPI, but not in route, query, header or form binding, so keep unions in bodies and in internal service layers.
Closed hierarchies give a similar guarantee for class trees. A closed class can only be derived from inside its declaring assembly, so the compiler knows every direct descendant and can check exhaustiveness. This suits state machines such as order lifecycles or device connection states. Note one documented subtlety: closure is not transitive, so intermediate descendants must also be marked closed if you want checking further down.
Collection expression arguments fix a long-standing gap. You can now pass constructor arguments with a with(...) element, for example a comparer or an initial capacity.
HashSet<string> tenants = [with(StringComparer.OrdinalIgnoreCase), .. configuredTenants];
List<Reading> buffer = [with(capacity: batchSize)];
Extension indexers and labeled break and continue round out the shipped list. The memory-safety work, which moves the unsafe requirement from pointer types to operations that actually dereference memory, remains preview and should stay out of production code in this release.
Project configuration for a cloud-native service
A minimal project file for an ASP.NET Core 11 service that opts in to runtime async, publishes a container and keeps builds strict looks like this.
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net11.0</TargetFramework>
<Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<!-- Opt in to runtime-native async for this project's own code -->
<Features>runtime-async=on</Features>
<!-- Container publishing via the SDK -->
<ContainerRepository>orders-api</ContainerRepository>
<ContainerImageTag>$(Version)</ContainerImageTag>
</PropertyGroup>
</Project>
The runtime documentation also names an opt-out, <UseRuntimeAsync>false</UseRuntimeAsync>, and notes that the old DOTNET_RuntimeAsync environment variable was removed. Keep both properties in mind: the opt-in for your code, the opt-out if a library or profiler misbehaves.
For reproducible images, set SOURCE_DATE_EPOCH from the commit timestamp in CI before publishing.
export SOURCE_DATE_EPOCH=$(git log -1 --format=%ct)
dotnet publish -c Release -t:PublishContainer
Wiring health probes and tracing the .NET 11 way
Two ASP.NET Core 11 features remove boilerplate that most Kubernetes services carry. The [ShortCircuit] attribute exits the pipeline right after routing, and the built-in OpenTelemetry attributes remove the need for the ASP.NET Core instrumentation package for basic HTTP server spans.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOpenTelemetry()
.WithTracing(t => t
.AddSource("Microsoft.AspNetCore") // native HTTP server spans in ASP.NET Core 11
.AddOtlpExporter()); // from the OpenTelemetry.Exporter.OpenTelemetryProtocol package
builder.Services.AddValidation(); // includes async validation in .NET 11
var app = builder.Build();
app.MapGet("/healthz", [ShortCircuit] () => "ok"); // skips auth, CORS and other middleware
app.MapGet("/readyz", [ShortCircuit] (IReadiness r) => r.IsReady ? Results.Ok() : Results.StatusCode(503));
app.Run();
Those spans flow to whatever collector you run. If you have not settled your pipeline design, our guide to OpenTelemetry Collector architecture and pipelines covers receivers, processors and exporters, and the companion piece on tail-based sampling in the OpenTelemetry Collector explains how to keep the extra spans from inflating your tracing bill.
How runtime async changes the request path

Figure 2: A request handler awaiting a service and repository under .NET 11 runtime async. Synchronous completions stay on the stack without allocating a Task; true I/O suspension is handled by the runtime, which skips an empty ExecutionContext and yields shorter stack traces.
The sequence shows the two paths that matter. In the hot path, the repository returns a cached value synchronously, and the result flows back up the chain without allocating intermediate tasks. In the cold path, real I/O is pending, so the runtime captures the frames it needs, skips ExecutionContext work when nothing ambient is set, and resumes the chain when I/O completes. A profiler or debugger sees a short, readable call stack instead of builder and state-machine frames.
The upgrade path from .NET 10 to .NET 11

Figure 3: A staged pipeline to upgrade net 10 to net 11. Each gate can send a service back to net10.0 without blocking others, because both versions are supported until November 2028.
The pipeline has five gates. First, inventory every service and its current target, because the .NET 8 and .NET 9 services have a deadline that the .NET 10 services do not. Second, pin the .NET 11 SDK in global.json and retarget to net11.0 with warnings as errors, so new analyzer diagnostics surface immediately. Third, read the .NET 11 breaking changes list against your dependency graph. Fourth, confirm that every node pool meets the new CPU floor. Fifth, run your tests and a BenchmarkDotNet suite, then canary a slice of traffic and compare SLOs before a full rollout.
A useful property of the .NET 11 SDK is that it builds net10.0 targets too. You can move the whole repository to SDK 11 for faster builds and new tooling, while leaving individual services on the .NET 10 runtime until their gate passes. That decouples toolchain upgrades from runtime upgrades, which is how large estates should run this migration.
Decision matrix: stay on .NET 10 LTS or move to .NET 11
| Situation | Recommendation | Reason |
|---|---|---|
| Service on .NET 8 or 9, simple dependencies | Move to .NET 11 | Same end of support as .NET 10, plus runtime async and JIT gains |
| Service on .NET 8 or 9, vendor SDKs certified only on LTS | Move to .NET 10 | Vendor support statements usually track LTS |
| Service on .NET 10, async-heavy hot path, tuned allocations | Pilot .NET 11 | Most likely to see measurable latency and GC gains |
| Service on .NET 10, I/O-bound, stable, low change rate | Stay on .NET 10 | Upgrade cost buys little; plan the jump to .NET 12 LTS |
| Node pools with x64 CPUs lacking SSE4.2 and POPCNT, or Windows Arm64 without LSE | Stay or replace hardware | .NET 11 may refuse to start on CPUs below the new baseline |
| Team wants C# 15 unions in domain models | Move to .NET 11 | C# 15 is the default language for net11.0 |
| Heavy use of OpenAPI client generation | Pilot with care | Default document version changes to OpenAPI 3.2 |
| Blazor Server with QuickGrid bookmarks | Test thoroughly | QuickGrid navigation and Virtualize defaults changed |
The “.NET 12 LTS” row assumes Microsoft keeps its alternating cadence, which would place the next LTS in November 2027. That is a reasonable expectation from the published policy, but it is a projection, not an announced date.
Trade-offs, Gotchas, and What Goes Wrong
Support parity removes the lifecycle objection to .NET 11, but it does not remove engineering risk. These are the failure modes I would plan for.
The CPU floor can stop a pod from starting. The JIT and Native AOT baseline on x86/x64 rises from x86-64-v1 to x86-64-v2, which requires CX16, POPCNT, SSE3, SSSE3, SSE4.1 and SSE4.2. On Windows Arm64 the baseline now includes LSE atomics. A process on older hardware may fail with “The current CPU is missing one or more of the baseline instruction sets.” Most current cloud instance types clear this bar, but old on-premises hypervisors, edge gateways and some emulated CPU models in nested virtualization do not. Audit with /proc/cpuinfo flags before you roll out, and remember that a hypervisor can mask flags the physical CPU has.
ReadyToRun targets moved higher than the floor. On Linux and Windows x64, R2R code now targets x86-64-v3, which assumes AVX2, BMI and FMA. On a CPU that meets v2 but not v3, the app still runs, but precompiled code for those methods cannot be used and the JIT has to compile them at startup. The runtime documentation warns about this launch overhead for Linux Arm64 too. Symptom: slower cold starts on a subset of nodes, which looks like a random scheduling problem in your dashboards.
Hosting behaviour changed when background work fails. IHost.RunAsync and IHost.StopAsync now throw when a BackgroundService fails, and dependency injection scope disposal aggregates exceptions. That is better behaviour, but it changes exit codes and shutdown paths. Pair it with the drain logic described in our post on Kubernetes graceful shutdown, PreStop hooks and PodDisruptionBudgets, or a failing consumer may now crash-loop where it previously limped along silently.
Configuration semantics shifted. Empty strings in chained configuration providers now override earlier providers. If an environment variable is set to an empty value in a Helm chart, it will now blank out the value from appsettings.json. This is the kind of change that passes every unit test and breaks one region in production.
Networking and crypto defaults tightened. SslStream server-side Authority Information Access (AIA) certificate downloads are disabled by default, and Linux limits AIA fetching to two fetches per chain build. Services that rely on servers fetching missing intermediate certificates must ship complete chains. HttpClient high-cardinality metrics now use observable counters, which can change how your metrics backend aggregates them.
OpenAPI 3.2 can break generated clients. The default document version moves to 3.2, built on Microsoft.OpenApi 3.x, which has its own breaking changes. Client generators that do not understand 3.2 may fail. Set options.OpenApiVersion to 3.1 until your toolchain catches up.
Runtime async is preview for your code. Profilers, APM agents and libraries that inspect async state machines by reflection may misbehave. Keep the opt-out property ready and pilot runtime async per service, not estate-wide.

Figure 4: A per-service decision tree. Services leaving .NET 8 or 9 choose between 10 and 11 by dependency risk; services already on .NET 10 move only when a pilot shows measurable gains.
The tree encodes the thesis of this post. Lifecycle no longer decides the version; dependency risk and measured benefit do. The loop back from the pilot to “stay on .NET 10” is deliberate: a failed pilot is a cheap, useful outcome, not a setback.
Practical Recommendations
Treat .NET 11 as a portfolio decision, not a single migration. Your .NET 8 and .NET 9 services are already out of support, so they move first, and for most of them .NET 11 is at least as good a destination as .NET 10. Your .NET 10 services are not under any deadline, so they move only where a pilot proves value. Upgrade the SDK everywhere before you upgrade runtimes, because SDK 11 gives faster builds, the MSBuild server, better dotnet test controls and a NuGet minimum-publish-age setting that helps against supply-chain attacks, all without changing the runtime your service runs on.
Measure before you believe. Run a BenchmarkDotNet suite for your hottest code paths on both runtimes, then compare p50, p99, allocation rate and gen0 collection counts on a canary. If you see no difference, staying on .NET 10 is a legitimate engineering result. Finally, budget time for configuration and hosting behaviour changes, which are more likely to cause incidents than API removals.
- [ ] List every service with its target framework and owner; flag all .NET 8 and .NET 9 services.
- [ ] Pin the .NET 11 SDK in
global.jsonacross repositories; keep runtime targets unchanged at first. - [ ] Check every node pool and edge device for
x86-64-v2, and Windows Arm64 for LSE. - [ ] Read the .NET 11 and ASP.NET Core 11 breaking-change lists; grep for empty-string config overrides.
- [ ] Retarget one service to
net11.0with warnings as errors; enable runtime async in a branch. - [ ] Benchmark, canary 10 percent of traffic, compare SLOs and GC counters for at least a day.
- [ ] Set
SOURCE_DATE_EPOCHin CI and verify identical image digests across two builds. - [ ] Pin OpenAPI to 3.1 if client generators lag; plan the move to 3.2.
- [ ] Record the decision per service, with a review date before .NET 12 ships.
Frequently Asked Questions
When was .NET 11 released and how long is it supported?
.NET 11 became generally available on November 10, 2026, announced on the official .NET Blog. It is a Standard Term Support release. Under Microsoft’s current policy, STS releases receive 24 months of support, so .NET 11 is supported until November 2028. That is the same month .NET 10, the current Long Term Support release, reaches end of support. Microsoft states that STS and LTS releases receive the same quality and servicing; the only difference is the length of the support window, which is now equal for these two versions.
Is .NET 11 LTS or STS?
.NET 11 is an STS, or Standard Term Support, release. Microsoft alternates release types: even-numbered versions such as .NET 8 and .NET 10 are LTS, and odd-numbered versions such as .NET 9 and .NET 11 are STS. The practical difference shrank in 2025, when Microsoft extended STS support from 18 months to 24 months. As a result, .NET 11 and .NET 10 both reach end of support in November 2028. If your organisation’s policy still bans STS releases, it is worth revisiting the reasoning behind that rule.
What are the main C# 15 features?
C# 15 is the default language version for projects targeting net11.0. Its shipped features are collection expression arguments using a with(...) element, union types declared with the union keyword, closed class hierarchies using the closed modifier, extension indexers, labeled break and continue, and non-virtual static interface members on older runtimes. Union types and closed hierarchies both enable exhaustiveness checking in switch expressions. Memory safety changes that tie unsafe to dereferencing operations rather than pointer types are a preview feature and require opting in to the preview language version.
Should I upgrade from .NET 10 to .NET 11?
Upgrade only where you can measure a benefit. Because both versions are supported until November 2028, there is no lifecycle pressure to leave .NET 10. Services with async-heavy hot paths, interface-heavy code or a need for C# 15 unions are the best candidates, because runtime async and JIT devirtualization target those patterns. Stable, I/O-bound services gain little and carry the same breaking-change risk. Pilot one service, compare latency, allocation rate and garbage collection counts on a canary, and decide per service rather than estate-wide.
What is runtime async in .NET 11?
Runtime async moves the work of suspending and resuming async methods from compiler-generated state-machine classes into the .NET runtime. In .NET 11 the framework libraries are compiled with it enabled, and you can opt your own project in with <Features>runtime-async=on</Features>. Microsoft still describes it as preview for application code. Reported microbenchmarks show a completed async chain dropping from 21.221 ns and 144 bytes to 6.151 ns and zero bytes, and stack traces shrinking from 13 frames to 5 in one documented example. Real service gains will be smaller.
Will .NET 11 run on my existing servers?
Most modern cloud instances will run it, but check older hardware. .NET 11 raises the x86/x64 baseline to x86-64-v2, which requires SSE4.2, POPCNT and related instructions, and Windows on Arm64 now requires LSE atomics. On CPUs below the baseline, the runtime can refuse to start with an error about missing baseline instruction sets. ReadyToRun code on Linux and Windows x64 now targets x86-64-v3, so CPUs without AVX2 will run correctly but may start more slowly because more methods must be compiled by the JIT at launch.
Further Reading
- Python 3.15 migration tutorial: lazy imports, frozendict, the Tachyon profiler and the JIT shows how another major runtime handled its 2026 upgrade cycle.
- OpenTelemetry Collector architecture and pipelines is where ASP.NET Core 11’s native HTTP spans should land.
- Kubernetes graceful shutdown, PodDisruptionBudgets and PreStop hooks covers the rollout mechanics behind a safe canary.
- Announcing .NET 11 on the .NET Blog is the primary release announcement.
- What’s new in .NET 11 on Microsoft Learn is the authoritative feature reference for runtime, libraries and SDK.
By Riju — about
