From .NET Aspire 8 to Aspire 13.6: Release History, Breaking Changes and Migrating an Old Project
Aspire moved from the workload and manifest model of 8.0 to the SDK in 9.x and to the rename, the .NET 10 requirement and pipelines in 13.0; every 13.x minor release brings its own breaking changes. The post summarizes the release line and migrates a project written with Aspire 9.5 to 13.6 with aspire update, showing with real output what the command leaves undone (net10.0, AddNpmApp, the lifecycle hook, the launch profile).
Version note: The code in this post was built and run with .NET 10 and Aspire CLI 13.6.5; the project we migrated was created with the Aspire 9.5.2 template targeting net9.0. Release dates come from the aspire.dev release notes and the packages' publish dates on NuGet.
Guess First
You have an AppHost written with Aspire 9.5: a separate <Sdk> element in the project file, an AddNpmApp that starts the Angular app, and a lifecycle hook that writes a log line at startup. You run aspire update with Aspire CLI 13.6.5 and see "Update successful". Which of these three do you think changed: the target framework (net9.0), the renamed Aspire.Hosting.NodeJs package, the OpenTelemetry packages in ServiceDefaults? And does the project build, does it run? The second half of this post shows that with a real migration; the first half is a short map of the two years in between.
The Release Line: from 8.0 to 13.6
| Version | Date | Highlights |
|---|---|---|
| 8.0 | May 21, 2024 | First GA; installed as a .NET workload, deployed with the manifest + azd |
| 8.1, 8.2 | July, August 2024 | 8.x maintenance and small features |
| 9.0 | November 12, 2024 | Workload-free SDK, WaitFor, persistent containers, resource commands |
| 9.4 | July 29, 2025 | Aspire CLI GA, AddExternalService, code-based configuration in YARP |
| 9.5 | September 2025 | The last 9.x minor release |
| 13.0 | November 11, 2025 | ".NET Aspire" → "Aspire", .NET 10 SDK requirement, polyglot, pipelines and aspire do |
| 13.1 | December 17, 2025 | MCP for AI agents with aspire agent init |
| 13.2 | March 23, 2026 | TypeScript AppHost (preview), aspire.config.json, export, Compose stable |
| 13.3 | May 7, 2026 | aspire destroy, Helm-based Kubernetes deploy (preview) |
| 13.4 | June 1, 2026 | Full support for TypeScript AppHosts, aspire integration list/search |
| 13.5 | August 18, 2026 | Kubernetes persistent volumes, WithTerminal |
| 13.6 | September 29, 2026 | Persistent telemetry in the dashboard with SQLite, Helm improvements |
After 9.5 the number jumped straight to 13; there are no 10–12 releases. In 13.x, minor releases came out about every two months on average, and each comes with its own list of breaking changes. So the question "when did we last update Aspire?" should be asked more often than a .NET version upgrade.
8.x: the Workload, the Manifest and azd
In the first release Aspire was a .NET workload: it was installed into the SDK with dotnet workload install aspire, and the AppHost project was marked with <IsAspireHost>true</IsAspireHost>. This tied the Aspire version to the SDK installation on the machine: working with two different Aspire versions on the same machine was hard, and installing the workload was a separate step in CI. On the deploy side the AppHost produced a manifest (aspire-manifest.json), which the Azure Developer CLI (azd) read and turned into Azure Container Apps. We didn't run this section: installing a workload changes the .NET SDK installation on the machine. The documentation recommends that projects coming from 8.x move to 9.x first and then to 13.
9.x: the Workload-Free SDK, WaitFor and the CLI
9.0 (November 12, 2024) removed the workload: the AppHost project file got a <Sdk Name="Aspire.AppHost.Sdk" Version="9.0.0" /> line and the Aspire.Hosting.AppHost package, and the templates arrived as a NuGet package through dotnet new install Aspire.ProjectTemplates. That way the Aspire version travels with the project. The same release brought three features from post 02 of this series: WaitFor and WaitForCompletion, ContainerLifetime.Persistent, and resource commands you can add to the dashboard. Throughout 9.x, .NET 8 and 9 were supported. 9.4 (July 29, 2025) made the Aspire CLI GA (aspire new, aspire run, aspire add; aspire publish in preview, aspire deploy behind a feature flag), introduced AddExternalService for modeling external services, and made a breaking change in the YARP integration: WithConfigFile was removed and replaced by the code-based configuration (WithConfiguration) we used in 03.
13.0: the Aspire Name, .NET 10 and Pipelines
13.0 (November 11, 2025) is the biggest break. The product's name changed from ".NET Aspire" to "Aspire", the documentation moved to aspire.dev, and Python and JavaScript resources became first-class. What matters for migration:
- The .NET 10 SDK requirement. The AppHost project file moved to the single-line SDK form (
<Project Sdk="Aspire.AppHost.Sdk/13.0.0">), and theAspire.Hosting.AppHostpackage reference became unnecessary. - The
Aspire.Hosting.NodeJspackage was renamedAspire.Hosting.JavaScript;AddJavaScriptAppreplacedAddNpmApp(noargsparameter; useWithArgsor apackage.jsonscript), andAddNodeApp's signature changed. - The publish APIs (
PublishingContext,WithPublishingCallback,IDistributedApplicationPublisherand related types) were removed; deployment was rewritten on top of the pipeline andaspire dowe used in 07. - Lifecycle hooks are obsolete:
IDistributedApplicationEventingSubscriberinstead ofIDistributedApplicationLifecycleHook,AddEventingSubscriberinstead ofAddLifecycleHook. aspire run --watchwas removed; the database integrations started providing the connection properties we saw in 02 (X_HOST,X_URI…).
13.1–13.6: What Was Added, What Broke
Every 13.x minor release has breaking changes; we collected the ones this series ran into from the release notes. In 13.2 the service discovery environment variables started using the endpoint scheme instead of the endpoint name, resource commands were renamed from resource-start to start, and a single aspire.config.json replaced .aspire/settings.json and apphost.run.json (the CLI migrates the old files on the first run). In 13.3, --log-level on aspire publish and aspire deploy became --pipeline-log-level, and aspire init stopped wiring up the AppHost completely on its own. In 13.4 aspire exec was removed and default images were raised: RabbitMQ 4.3 (our container from 05) and PostgreSQL 18.3; the latter can't open PostgreSQL 17 data volumes, so if you work with old data you need to pin WithImageTag("17.6") before WithDataVolume(). In 13.5 PublishAsConnectionString became obsolete (use AddConnectionString), and the ServiceProvider property on hosting context types became Services. In 13.6 the dashboard started storing telemetry in SQLite (04), MongoDB started using automatic TLS in local runs, and the Azure Cosmos DB emulator moved to the Linux-based version. The lesson: skipping a minor release doesn't mean skipping its breaking changes; when you upgrade, read the notes of every release in between.
Migrating an Old Project: aspire update
With the Aspire 9.5.2 template (dotnet new aspire-starter -f net9.0) we created an old-style prototype of Bookstore, the online bookstore used as the shared example across this site's series: an API, a Blazor UI and ServiceDefaults. On top of it we added two typical pieces of the 9.x era: an AddNpmApp that starts a small Node.js app for the back office, and a lifecycle hook that writes a log line at startup:
builder.Services.TryAddLifecycleHook<StartupBanner>();
...
builder.AddNpmApp("admin", "../admin-web", "start")
.WithHttpEndpoint(env: "PORT")
.WithReference(apiService);
internal sealed class StartupBanner(ILogger<StartupBanner> logger) : IDistributedApplicationLifecycleHook
{
public Task BeforeStartAsync(DistributedApplicationModel appModel, CancellationToken cancellationToken = default)
{
logger.LogInformation("Starting Bookstore with {Count} resources", appModel.Resources.Count);
return Task.CompletedTask;
}
}
The project built without errors on 9.5.2, but today NuGet reports security warnings for these versions; that's the most concrete reason to migrate:
warning NU1903: Package 'Aspire.Hosting.AppHost' 9.5.2 has a known high severity vulnerability, https://github.com/advisories/GHSA-f7vh-h5g3-7jrh
warning NU1904: Package 'Aspire.Dashboard.Sdk.<platform>' 9.5.2 has a known critical severity vulnerability, https://github.com/advisories/GHSA-4g86-54fm-h9h8
warning NU1902: Package 'OpenTelemetry.Exporter.OpenTelemetryProtocol' 1.12.0 has a known moderate severity vulnerability, https://github.com/advisories/GHSA-4625-4j76-fww9
We updated with Aspire CLI 13.6.5:
aspire update --apphost Bookstore.AppHost/Bookstore.AppHost.csproj --yes --non-interactive --channel stable
💾 Created settings file at 'aspire.config.json'.
Checking for updates...
Analyzing project...
📦 Aspire.AppHost.Sdk 9.5.2 to 13.6.5
Applying updates...
Executing: Update package Aspire.AppHost.Sdk from 9.5.2 to 13.6.5
Migrated to new project format: <Project Sdk="Aspire.AppHost.Sdk/13.6.5">
Removed obsolete Aspire.Hosting.AppHost package reference
Restoring packages...
✅ Update successful!
The difference in the project file is exactly these three lines: <Project Sdk="Microsoft.NET.Sdk"> and the separate <Sdk> element collapsed into one line, the Aspire.Hosting.AppHost reference was deleted, and an aspire.config.json was added. The build:
warning CS0618: 'IDistributedApplicationLifecycleHook' is obsolete: 'Use IDistributedApplicationEventingSubscriber instead.'
warning CS0618: 'LifecycleHookServiceCollectionExtensions.TryAddLifecycleHook<T>(IServiceCollection)' is obsolete: 'Use EventingSubscriberServiceCollectionExtensions.TryAddEventingSubscriber instead.'
warning NU1902: Package 'OpenTelemetry.Exporter.OpenTelemetryProtocol' 1.12.0 has a known moderate severity vulnerability, ...
warning ASPIRE010: Bookstore.AppHost is configured with AspireUseCliBundle=false. Some Aspire features require the Aspire CLI bundle. ...
0 Error(s)
The answer to Guess First: the target framework stayed net9.0, Aspire.Hosting.NodeJs stayed at 9.5.2 (there's nothing to update a renamed package to), and the OpenTelemetry and Microsoft.Extensions.* packages in ServiceDefaults weren't touched; they aren't Aspire packages. aspire update --migrate also said "Nothing to migrate". The project builds, but we're not done.
What aspire update Doesn't Do
The first aspire start couldn't start the AppHost: the machine had no .NET 9 runtime, and the net9.0 AppHost exited without finding Framework: 'Microsoft.NETCore.App', version '9.0.0'. Aspire 13 requires the .NET 10 SDK anyway; we moved all four projects to net10.0. The second attempt stopped with a different error:
Microsoft.Extensions.Options.OptionsValidationException: The 'applicationUrl' setting must be an https address unless the
'ASPIRE_ALLOW_UNSECURED_TRANSPORT' environment variable is set to true. ...
The 9.5 template's http launch profile didn't have this variable; the 13.6 template adds it (it's in the profile we've used since 01). After adding the variable the app started, and something notable happened: next to Aspire.Hosting 13.6.5, AddNpmApp from Aspire.Hosting.NodeJs 9.5.2 worked, and the back office app got the API address right:
curl http://localhost:55633
Bookstore back office. API: http://localhost:5471
Working doesn't mean correct: this is a package two major versions behind that no longer gets updates, and it may break on the next upgrade. We did the remaining steps by hand:
// AppHost.cs: an eventing subscriber instead of a lifecycle hook, AddJavaScriptApp instead of AddNpmApp
builder.Services.TryAddEventingSubscriber<StartupBanner>();
...
builder.AddJavaScriptApp("admin", "../admin-web", "start")
.WithHttpEndpoint(env: "PORT")
.WithReference(apiService);
internal sealed class StartupBanner(ILogger<StartupBanner> logger) : IDistributedApplicationEventingSubscriber
{
public Task SubscribeAsync(IDistributedApplicationEventing eventing, DistributedApplicationExecutionContext executionContext, CancellationToken cancellationToken)
{
eventing.Subscribe<BeforeStartEvent>((@event, ct) =>
{
logger.LogInformation("Starting Bookstore with {Count} resources", @event.Model.Resources.Count);
return Task.CompletedTask;
});
return Task.CompletedTask;
}
}
In the project files, Aspire.Hosting.JavaScript 13.6.5 replaced Aspire.Hosting.NodeJs 9.5.2, the AppHost got <AspireUseCliBundle>true</AspireUseCliBundle> (the default for new AppHosts since 13.5), and in ServiceDefaults the OpenTelemetry packages went from 1.12.0 to 1.21.0 and Microsoft.Extensions.Http.Resilience and Microsoft.Extensions.ServiceDiscovery to 10.11.0. Result: 0 Warning(s), 0 Error(s). The migrated version is in the repo's samples/aspire-9-migration folder. When we ran it, one more novelty showed up: AddJavaScriptApp adds a hidden admin-installer resource that runs npm install, as we saw for Angular in 02; the old AddNpmApp left installing packages to you. The new subscriber ran in the AppHost's log too:
[INFO] [AppHost/StartupBanner] Starting Bookstore with 7 resources
A note on the deploy side as well: a 13.6 AppHost can still write the 8.x manifest format with the --publisher manifest --output-path aspire-manifest.json arguments (we tried it; it produced a 2.3 KB file with "type": "project.v0" entries). You don't have to drop a pipeline built on the manifest + azd right away, but the new targets (Compose, Kubernetes) and aspire destroy exist only on the pipeline side.
In Production
Update the Aspire packages regularly; NuGet's warnings in this post showed a "critical" dashboard vulnerability for 9.5.2. Even if the dashboard doesn't run in production, it runs on developer machines and in CI.
If you upgrade across several releases, read the breaking change list of every release in between; the examples in this post (the PostgreSQL 18 volume, service discovery variable names, command names) surface at run time without a build error.
After an upgrade, look not only at the build but also at the diff of the aspire publish output. Image versions, environment variable names and deploy artifacts can change quietly; reviewing the publish output in the pull request, as in 07, catches that.
If you use central package management, aspire update updates the versions in Directory.Packages.props; for non-Aspire packages (OpenTelemetry, Microsoft.Extensions.*) run dotnet list package --outdated as well.
Common Pitfalls
Taking "Update successful" as the end of the migration. The target framework, renamed packages, obsolete APIs and launch profiles are manual work.
Suppressing obsolete warnings. APIs that became obsolete in 13.0 will be removed in the next major release; in a project with TreatWarningsAsErrors they stop the build anyway.
Leaving an old integration package with the new Aspire. It worked in our run, but nothing indicates it's a supported combination.
Jumping from 8.x straight to 13. The documentation recommends moving to 9.x first; the move from workload to SDK and the breaks in 13 are easier to untangle separately.
Copying code from old blog posts as is. If you see "dotnet workload install aspire", AddNpmApp, IsAspireHost or WithConfigFile, the post is at least one major version behind today's Aspire.
A Small Experiment
★☆☆ Run aspire update --channel stable on a branch of your own project and look at git diff. What changed and what didn't?
★★☆ Create a project with the 9.5 template, switch from AddNpmApp to AddJavaScriptApp, and work out from its log what the -installer resource that appears in the dashboard does.
★★★ Upgrade this series' AppHost (its state at the end of 07) to the next Aspire minor release: which breaking changes in the release notes affect Bookstore? Compare the tests (06) and the aspire publish output (07) before and after the upgrade.
The series ends here. We started by running Bookstore with one command from an AppHost; with service discovery, the dashboard, integrations, end-to-end tests and deployment, the same model reached from the development machine to Compose and Kubernetes. Where Aspire helps most is in that model being a single, runnable recipe for everyone on the team; how Bookstore is split into services and its business flows continue in the .NET ile Mikroservis Uygulaması series.