diff --git a/src/frontend/config/sidebar/docs.topics.ts b/src/frontend/config/sidebar/docs.topics.ts index 0d2791c6e..d04025fa8 100644 --- a/src/frontend/config/sidebar/docs.topics.ts +++ b/src/frontend/config/sidebar/docs.topics.ts @@ -754,6 +754,18 @@ export const docsTopics: StarlightSidebarTopicsUserConfig = { 'zh-CN': 'Aspire 应用生命周期指南', }, }, + { + label: 'Debug Aspire apps', + slug: 'fundamentals/debug-aspire-apps', + }, + { + label: 'Startup performance', + slug: 'fundamentals/startup-performance', + }, + { + label: 'Hot reload and watch mode', + slug: 'fundamentals/hot-reload', + }, ], }, { diff --git a/src/frontend/src/content/docs/fundamentals/debug-aspire-apps.mdx b/src/frontend/src/content/docs/fundamentals/debug-aspire-apps.mdx new file mode 100644 index 000000000..9c19e3d84 --- /dev/null +++ b/src/frontend/src/content/docs/fundamentals/debug-aspire-apps.mdx @@ -0,0 +1,356 @@ +--- +title: Debug Aspire apps +description: Learn how to debug Aspire applications across different IDEs including Visual Studio, VS Code, JetBrains Rider, and the command line. +--- + +import { Aside, Steps } from '@astrojs/starlight/components'; +import { Kbd } from 'starlight-kbd/components'; +import LearnMore from '@components/LearnMore.astro'; + +Debugging distributed applications with Aspire is straightforward because Aspire orchestrates all your services and containers in a single session. You can attach a debugger to any or all of your services simultaneously, debug selectively, or run some services without a debugger while debugging others. This guide explains how to debug Aspire apps across different IDEs and environments. + +## How Aspire debugging works + +When you start an Aspire application in debug mode, the AppHost launches all configured resources and sets up the Aspire dashboard. Each service project is started with the debugger attached if requested. You can: + +- Attach the debugger to **all** services at once +- Debug only **specific** services while running others normally +- Use your IDE's standard debugging features (breakpoints, watch windows, call stacks) across all attached services + + + +## Debug in Visual Studio + +Visual Studio offers the tightest Aspire integration. When you open an Aspire solution in Visual Studio and press **F5** (or select **Debug** > **Start Debugging**), Visual Studio automatically starts the AppHost and attaches the debugger to all service projects. + +### Start debugging + + + +1. Open your Aspire solution (`.sln`) in Visual Studio. +1. Set the AppHost project as the startup project if it isn't already. +1. Press **F5** or select **Debug** > **Start Debugging**. + + + +Visual Studio builds all projects, starts the AppHost, and attaches the debugger to every .NET service project in your solution. + +### Debug a specific service + +To debug only specific services while running others normally, use the **Attach to Process** approach: + + + +1. Start the Aspire solution without debugging from the command line: + + ```bash title="Aspire CLI — Run without debugger" + aspire run + ``` + +1. In Visual Studio, select **Debug** > **Attach to Process** (). +1. Find and select the specific service process you want to debug, then select **Attach**. + + + +This lets you choose exactly which service to debug without attaching to all services at once. + +### Set breakpoints across services + +Visual Studio allows you to set breakpoints in any service project. When a breakpoint is hit, Visual Studio pauses that service's execution, while all other services continue to run normally. + + +For more information, see [Launch profiles](/fundamentals/launch-profiles/). + + +## Debug in Visual Studio Code + +Visual Studio Code requires a `launch.json` configuration to debug Aspire apps. The [Aspire VS Code extension](/get-started/aspire-vscode-extension/) simplifies this setup. + +### Configure launch.json + + + +1. Install the [Aspire VS Code extension](/get-started/aspire-vscode-extension/). +1. Open the Command Palette (). +1. Run the **Aspire: Configure launch.json** command. + + + +The extension creates a `.vscode/launch.json` with the Aspire debugger configuration: + +```json title="JSON — .vscode/launch.json" +{ + "version": "0.2.0", + "configurations": [ + { + "type": "aspire", + "request": "launch", + "name": "Aspire: Launch Default AppHost", + "program": "${workspaceFolder}" + } + ] +} +``` + +### Start debugging + + + +1. Open the Run and Debug view (). +1. Select **Aspire: Launch Default AppHost** from the dropdown. +1. Press the green **Start Debugging** button or press **F5**. + + + +VS Code attaches the debugger to all .NET service projects in your Aspire solution and opens the Aspire dashboard in your browser. + +### Debug a specific project + +To target a specific project: + +```json title="JSON — .vscode/launch.json" +{ + "version": "0.2.0", + "configurations": [ + { + "type": "aspire", + "request": "launch", + "name": "Aspire: Launch MyAppHost", + "program": "${workspaceFolder}/src/MyApp.AppHost/MyApp.AppHost.csproj" + } + ] +} +``` + +Point the `program` field to the specific AppHost project file you want to use. + +## Debug in JetBrains Rider + +JetBrains Rider supports Aspire projects natively from Rider 2024.1. You can run and debug Aspire solutions directly from the IDE. + +### Start debugging + + + +1. Open your Aspire solution in Rider. +1. In the **Run/Debug Configurations** dropdown, select the AppHost project configuration. +1. Press **Shift+F9** or select **Run** > **Debug** to start debugging. + + + +Rider launches the AppHost, starts all configured services, and attaches the debugger to .NET service projects. + +### Troubleshooting Rider + +- **Missing run configuration**: If no Aspire run configuration appears, ensure you have a supported Rider version (2024.1 or later) and that the [.NET Aspire plugin](https://plugins.jetbrains.com/plugin/23162) is installed and enabled. +- **Debugger not attaching**: Check that the AppHost project targets the correct .NET SDK version. See the [prerequisites](/get-started/prerequisites/) for supported versions. +- **Certificate issues**: Run `aspire doctor` from the terminal to diagnose HTTPS certificate problems. + + +For more information about Rider and Aspire, see the [JetBrains blog post on .NET Aspire support](https://blog.jetbrains.com/dotnet/2024/02/19/jetbrains-rider-and-the-net-aspire-plugin/). + + +## Debug in Neovim or other terminal editors + +You can debug services in Aspire from any editor that supports the Debug Adapter Protocol (DAP), including Neovim with plugins like `nvim-dap`, by attaching to running service processes. + +### Attach to a running process + + + +1. Start the Aspire solution from the CLI without a debug session: + + ```bash title="Aspire CLI — Start Aspire" + aspire run + ``` + +1. Find the process ID of the service you want to debug: + + ```bash title="Bash — Find service process" + ps aux | grep MyService + ``` + +1. In your editor, use the attach-to-process feature to attach to that PID. For Neovim with `nvim-dap`, configure a `coreclr` adapter using the `attach` request type and the discovered PID. + + + +### Start a VS Code debug session from the CLI + +If you're using VS Code or an editor that supports the VS Code debug adapter protocol, you can start a debug session from the terminal: + + + +1. Run the Aspire app with the debug session flag: + + ```bash title="Aspire CLI — Start with debug session" + aspire run --start-debug-session + ``` + + The CLI outputs a VS Code debug session URI to the console. + +1. Open VS Code and connect to the debug session URI printed by the CLI. + +1. Set breakpoints and begin debugging as normal. + + + +## Debug Linux-only projects + +Some projects can only run on Linux (for example, projects using Linux-specific APIs or targeting Linux containers). You have several options for debugging these in Aspire. + +### Option 1: Run Aspire on Linux + +The most straightforward approach is to develop and debug directly on Linux (or WSL2 on Windows). + + + +1. Install the prerequisites on your Linux machine or WSL2 environment. For more information, see [Prerequisites](/get-started/prerequisites/). +1. Run your Aspire solution normally using `aspire run` or your IDE. + + + +### Option 2: Use Docker containers with remote debugging + +For projects that run as Linux containers, you can configure remote debugging: + + + +1. Add a development-time Dockerfile for your Linux service that includes the .NET debugger tools: + + ```dockerfile title="Dockerfile — Linux service with debugger" + FROM mcr.microsoft.com/dotnet/sdk:9.0 AS debug + WORKDIR /app + EXPOSE 4024 + # Install vsdbg (the .NET debugger) + RUN curl -sSL https://aka.ms/getvsdbg | bash /dev/stdin -v latest -l /vsdbg + ENTRYPOINT ["dotnet", "MyLinuxService.dll"] + ``` + +1. In your AppHost, reference the container and expose the debug port: + + ```csharp title="C# — AppHost.cs" + var builder = DistributedApplication.CreateBuilder(args); + + var linuxService = builder.AddDockerfile("linux-service", "../MyLinuxService") + .WithEndpoint(targetPort: 4024, name: "debug"); + + builder.Build().Run(); + ``` + +1. Attach your IDE's remote debugger to the container on port 4024. + + + +### Option 3: Use WSL2 on Windows + +If you're on Windows and need to test Linux-specific behavior, use WSL2 to run the Linux portions: + + + +1. Install WSL2 and a Linux distribution (Ubuntu is recommended). +1. Install the .NET SDK and Aspire prerequisites inside WSL2. +1. Open your solution in VS Code with the **Remote - WSL** extension and debug from there. + + + + +For more information about WSL2 development, see the [VS Code Remote Development documentation](https://code.visualstudio.com/docs/remote/wsl). + + +## Selective debugging + +When working with large Aspire solutions, you may want to debug only specific services rather than attaching the debugger to everything. This reduces overhead and keeps focus on the service under investigation. + +### Exclude services from debugging + +In your AppHost, you can configure specific resources to start without the debugger: + +```csharp title="C# — AppHost.cs" +var builder = DistributedApplication.CreateBuilder(args); + +// These services run without a debugger +var cache = builder.AddRedis("cache"); +var db = builder.AddPostgres("db").AddDatabase("appdb"); + +// Attach debugger only to this service +var api = builder.AddProject("api") + .WithReference(cache) + .WithReference(db); + +builder.Build().Run(); +``` + +Containers (like Redis and PostgreSQL above) never have the .NET debugger attached by default. Only `.AddProject()` resources receive debugger attachment. + +### Start without debugging + +You can also start the entire solution without the debugger and attach manually later: + +```bash title="Aspire CLI — Run without debugger" +aspire run +``` + +Then attach from your IDE when you want to investigate a specific service. In Visual Studio, use **Debug** > **Attach to Process** and select the service process. In VS Code, add an attach configuration to your `launch.json`: + +```json title="JSON — .vscode/launch.json (attach)" +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Attach to ApiService", + "type": "coreclr", + "request": "attach", + "processName": "ApiService" + } + ] +} +``` + +## Use the Aspire dashboard for diagnostics + +Even without a traditional debugger, the Aspire dashboard provides powerful diagnostics: + +- **Structured logs** — view log events filtered by service, log level, and trace ID +- **Distributed traces** — visualize the entire call chain across services +- **Metrics** — monitor performance counters in real time +- **Console logs** — see raw stdout/stderr from any service or container + + +For more information about the Aspire dashboard, see [Explore Aspire dashboard](/dashboard/explore/). + + +## Common debugging issues + +### Breakpoints not hit + +- Ensure the project is compiled in **Debug** configuration (not Release). +- Confirm **Just My Code** is disabled if you need to step into library code. +- Verify the source file and deployed binary are in sync—rebuild and restart the debug session. + +### Debugger attaches but then detaches + +- Check that `DOTNET_ENVIRONMENT` is set to `Development` in your launch profile. +- Look for exceptions during service startup in the Aspire dashboard console logs. + +### Cannot debug a service + +- Confirm the service is added with `AddProject()`, not as a container or executable, for automatic debugger attachment. +- If using VS Code, ensure the `.vscode/launch.json` is properly configured using the **Aspire: Configure launch.json** command. + +### Port conflicts + +- Use `aspire doctor` to check for common configuration issues. +- Review the networking overview to understand how Aspire assigns ports. + + +For more information about networking, see [Networking overview](/fundamentals/networking-overview/). + + +## See also + +- [Launch profiles](/fundamentals/launch-profiles/) +- [Aspire VS Code extension](/get-started/aspire-vscode-extension/) +- [Explore Aspire dashboard](/dashboard/explore/) +- [Networking overview](/fundamentals/networking-overview/) diff --git a/src/frontend/src/content/docs/fundamentals/hot-reload.mdx b/src/frontend/src/content/docs/fundamentals/hot-reload.mdx new file mode 100644 index 000000000..af08a6b1f --- /dev/null +++ b/src/frontend/src/content/docs/fundamentals/hot-reload.mdx @@ -0,0 +1,183 @@ +--- +title: Hot reload and watch mode +description: Learn about hot reload and watch mode capabilities in Aspire, including current limitations and workarounds for a faster inner-loop experience. +--- + +import { Aside, Steps } from '@astrojs/starlight/components'; +import LearnMore from '@components/LearnMore.astro'; + +Hot reload and watch mode let you see code changes reflected in a running application without a full restart. This guide explains what Aspire currently supports, where limitations exist, and how to get the fastest possible inner-loop experience. + +## What is hot reload? + +.NET hot reload lets you apply code changes to a running application without stopping and restarting it. It works by patching the running process in place. There are two related features: + +- **.NET Hot Reload** — applies C# code changes to a running .NET process during a debug session or via `dotnet watch` +- **Browser hot reload (CSS/Razor)** — applies CSS and Razor component changes to the browser without a page refresh (supported in Blazor and ASP.NET Core) + +## Hot reload with Aspire + +### Supported scenarios + +Aspire supports hot reload for individual .NET service projects when they are started with `dotnet watch`. Hot reload works at the **service level**, meaning you enable it per service, not for the entire solution at once. + +**Blazor CSS and Razor hot reload** works automatically when Blazor projects are run with `dotnet watch run` or from Visual Studio with debugging enabled. + +### How to enable hot reload for a service + +The recommended way to use hot reload with a service in your Aspire solution is to run `dotnet watch run` in a separate terminal for that project while the rest of the solution runs normally via `aspire run`. + +### Run a service with dotnet watch from the terminal + +You can start a specific service with watch mode while running Aspire for the other services: + + + +1. Start the Aspire solution normally (without the service you want to watch): + + ```bash title="Aspire CLI — Start Aspire" + aspire run + ``` + +1. In a separate terminal, navigate to the service project you want to hot-reload: + + ```bash title="Bash — Navigate to service" + cd src/MyApp.Frontend + ``` + +1. Run the service with watch: + + ```bash title="Bash — Start with hot reload" + dotnet watch run + ``` + + + +When `dotnet watch run` is running in the terminal, any `.cs`, `.razor`, or `.css` changes you make trigger an automatic reload of that service. + +## Current limitations + +### AppHost itself does not support hot reload + +The AppHost project **does not support hot reload**. Changes to `AppHost.cs` (for example, adding a new resource or changing a connection reference) require a full restart of the Aspire session. + +This is by design: the AppHost defines the topology and wiring of your application. Changing it requires re-creating resources and re-establishing references. + +**Workaround**: Keep AppHost changes small and batch them. Run `aspire run` again after making AppHost changes. + +### Hot reload is per-service, not solution-wide + +Aspire does not currently provide a single command to start all services in watch mode simultaneously. You must enable watch mode per service. + +**Workaround**: Enable watch mode only for the service(s) you are actively working on. For services you aren't changing, run them normally. + +### Container and executable resources don't support .NET hot reload + +Hot reload is a .NET-specific feature. Resources added as Docker containers or external executables don't participate in .NET hot reload. + +**Workaround**: Use the Aspire dashboard logs and restart the resource manually from the dashboard when you need to update it. + +### Hot reload does not work for all code changes + +.NET hot reload supports a subset of C# changes. The following changes require a restart rather than a hot reload: + +- Changes to application startup code (for example, `Program.cs`) +- Adding or removing class hierarchies or interface implementations +- Changes to struct layouts +- Adding new generic type instantiations +- Some attribute changes + +When `dotnet watch` detects an unsupported change, it automatically performs a full restart of that service. + + + +## Blazor hot reload + +Blazor applications benefit from both .NET hot reload (for C# code) and browser hot reload (for CSS and Razor markup): + +- **Razor component changes** (`.razor` files) — trigger a browser reload automatically +- **CSS changes** — applied to the browser without a page reload (CSS hot reload) +- **C# logic changes** — applied via .NET hot reload without restarting the process + +To get all three types of hot reload in a Blazor project within an Aspire solution: + + + +1. Run the Aspire solution so containers and other services start. +1. In a separate terminal, run the Blazor project with watch: + + ```bash title="Bash — Blazor hot reload" + cd src/MyApp.Web + dotnet watch run + ``` + + + +## Improve inner-loop speed without hot reload + +When hot reload isn't available (for example, during AppHost changes), these practices minimize the restart overhead: + +### Use persistent containers + +Containers like Redis and PostgreSQL take time to start and initialize. Use persistent containers so they remain running between restarts: + +```csharp title="C# — AppHost.cs" +var builder = DistributedApplication.CreateBuilder(args); + +var cache = builder.AddRedis("cache") + .WithLifetime(ContainerLifetime.Persistent); + +var db = builder.AddPostgres("db") + .WithLifetime(ContainerLifetime.Persistent) + .AddDatabase("appdb"); + +builder.Build().Run(); +``` + + + + +For more information, see [Persistent containers](/app-host/persistent-containers/). + + +### Minimize the number of services during development + +When working on a specific service, reduce the number of running services to only what you need: + +```csharp title="C# — AppHost.cs (development subset)" +var builder = DistributedApplication.CreateBuilder(args); + +// Only run the services needed for this feature +var db = builder.AddPostgres("db").AddDatabase("appdb"); +var api = builder.AddProject("api") + .WithReference(db); + +// Comment out unrelated services +// var worker = builder.AddProject("worker"); +// var reporting = builder.AddProject("reporting"); + +builder.Build().Run(); +``` + +### Pre-restore NuGet packages + +Each restart triggers a build, and builds can be slow if NuGet packages need to be restored. Keep packages restored by pre-running restore: + +```bash title="Bash — Pre-restore" +dotnet restore +``` + + +For more tips on speeding up the build and restore process, see [Startup performance](/fundamentals/startup-performance/). + + +## See also + +- [Startup performance](/fundamentals/startup-performance/) +- [Persistent containers](/app-host/persistent-containers/) +- [Debug Aspire apps](/fundamentals/debug-aspire-apps/) +- [Supported code changes (C# and Visual Basic)](https://learn.microsoft.com/visualstudio/debugger/supported-code-changes-csharp) diff --git a/src/frontend/src/content/docs/fundamentals/startup-performance.mdx b/src/frontend/src/content/docs/fundamentals/startup-performance.mdx new file mode 100644 index 000000000..c81e4b3a4 --- /dev/null +++ b/src/frontend/src/content/docs/fundamentals/startup-performance.mdx @@ -0,0 +1,246 @@ +--- +title: Startup performance +description: Learn how to optimize Aspire application startup time, speed up NuGet restore, and reduce container pull delays. +--- + +import { Aside, Steps } from '@astrojs/starlight/components'; +import LearnMore from '@components/LearnMore.astro'; + +Aspire orchestrates multiple services and containers during local development, so startup times can add up when packages aren't cached, container images need to be pulled, or the build system isn't optimized. This guide covers practical strategies for improving the startup experience. + +## Understand startup phases + +Before optimizing, it helps to understand the three main phases of an Aspire startup: + +1. **NuGet restore** — downloading and caching NuGet packages referenced by AppHost and service projects +2. **Build** — compiling your .NET projects +3. **Container pull** — downloading container images (Redis, PostgreSQL, etc.) if they aren't cached locally + +Each phase has different optimization strategies. + +## Speed up NuGet restore + +The first time you build an Aspire project, NuGet must download all referenced packages. Subsequent restores use the local cache. + +### Use a local NuGet cache + +NuGet caches packages locally by default. To check your current cache location and size: + +```bash title="Bash — Check NuGet cache" +dotnet nuget locals all --list +``` + +To clear a stale or corrupted cache: + +```bash title="Bash — Clear NuGet cache" +dotnet nuget locals all --clear +``` + + + +### Disable floating version ranges + +Using wildcard or floating version constraints (for example, `*` or `1.0.*`) forces NuGet to check for new versions on every restore, which adds latency. Pin to specific versions in your project files: + +```xml title="XML — MyAppHost.csproj" + + + + + +``` + +### Use a NuGet feed mirror or proxy + +If your NuGet restore is slow because of network latency to nuget.org, consider configuring a closer mirror or a local proxy like [Azure Artifacts](https://learn.microsoft.com/azure/devops/artifacts/nuget/upstream-sources) or a corporate feed. Add the feed in a `nuget.config` at the solution root: + +```xml title="XML — nuget.config" + + + + + + + + +``` + +### Run restore before building + +When possible, pre-restore packages before starting the build, for example in a CI environment or when pulling a repository for the first time: + +```bash title="Bash — Pre-restore packages" +dotnet restore MyApp.sln +``` + +## Speed up the build + +### Use incremental builds + +Aspire relies on standard MSBuild incremental builds. Ensure you're not inadvertently forcing full rebuilds by: + +- Not cleaning the output directory between runs during development +- Avoiding `dotnet clean` before `aspire run` unless you have a specific reason + +### Reduce the number of projects + +Every project in your solution adds to the build time. Consider combining small utility libraries into larger shared projects during development if it doesn't affect your architecture. + +### Enable parallel builds + +MSBuild builds independent projects in parallel by default. If you have a large solution, ensure you're not accidentally limiting parallelism: + +```bash title="Bash — Build with maximum parallelism" +dotnet build --maxcpucount +``` + +## Speed up container image pulls + +Container images can be large (several hundred MB to several GB). The first `aspire run` after adding a new container resource can be slow while images are pulled. + +### Pre-pull container images + +Before starting your development session, pull the images you need in advance: + +```bash title="Bash — Pre-pull Docker images" +docker pull redis:latest +docker pull postgres:16 +``` + +### Pin container image versions + +Using `latest` or a floating tag forces the container runtime to check for updates. Pin to a specific digest or version tag to use the cached image: + +```csharp title="C# — AppHost.cs" +var builder = DistributedApplication.CreateBuilder(args); + +// Uses a specific version; Docker can use the cached layer +var cache = builder.AddRedis("cache") + .WithImageTag("7.4"); + +var db = builder.AddPostgres("postgres") + .WithImageTag("16.3"); + +builder.Build().Run(); +``` + +### Use persistent containers + +By default, Aspire creates and destroys containers on each run. Using [persistent containers](/app-host/persistent-containers/) keeps containers running between sessions, eliminating the startup cost of creating and initializing new containers: + +```csharp title="C# — AppHost.cs" +var builder = DistributedApplication.CreateBuilder(args); + +var cache = builder.AddRedis("cache") + .WithLifetime(ContainerLifetime.Persistent); + +builder.Build().Run(); +``` + + + + +For more information about persistent containers, see [Persistent containers](/app-host/persistent-containers/). + + +### Switch to a faster container runtime + +If Docker Desktop is slow on your machine, consider using an alternative OCI-compatible runtime: + +- **[Podman](https://podman.io/)** — A daemonless container runtime that can be faster on some systems. +- **[Rancher Desktop](https://rancherdesktop.io/)** — Provides containerd or moby (Docker) as the runtime. + + +For supported container runtimes, see [Prerequisites](/get-started/prerequisites/). + + +## Optimize AppHost startup + +The AppHost itself starts quickly, but there are a few patterns that can improve the inner-loop experience. + +### Reduce the number of resources + +During early development, consider commenting out resources you don't need for your current task. For example, if you're working on a UI change, you may not need all backend services running: + +```csharp title="C# — AppHost.cs (reduced for UI development)" +var builder = DistributedApplication.CreateBuilder(args); + +// Comment out services not needed right now +// var cache = builder.AddRedis("cache"); +// var worker = builder.AddProject("worker"); + +var api = builder.AddProject("api"); +var frontend = builder.AddProject("frontend") + .WithReference(api); + +builder.Build().Run(); +``` + +### Profile startup with aspire doctor + +Use `aspire doctor` to identify common configuration issues that can slow startup: + +```bash title="Aspire CLI — Diagnose issues" +aspire doctor +``` + +The command checks for certificate issues, container runtime availability, .NET SDK version compatibility, and other common problems. + + +For more information, see [`aspire doctor`](/reference/cli/commands/aspire-doctor/). + + +## Common slow startup scenarios + +### First run after cloning a repository + +The first run is always slower because NuGet packages aren't cached and container images haven't been pulled. To speed this up: + + + +1. Run `dotnet restore` to pre-restore NuGet packages. +1. Pull required container images with `docker pull`. +1. Run `aspire run`. + + + +### Slow restore in CI/CD environments + +CI/CD pipelines often start with a clean environment, which means NuGet packages and container images must be downloaded fresh on every run. + +**Cache NuGet packages between runs** using your CI system's cache feature. For example, in GitHub Actions: + +```yaml title="YAML — .github/workflows/ci.yml" +- name: Cache NuGet packages + uses: actions/cache@v4 + with: + path: ~/.nuget/packages + key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }} + restore-keys: | + ${{ runner.os }}-nuget- +``` + +**Cache container images** or use a container registry mirror to avoid pulling from Docker Hub on every run. + +### Restore taking more than 2 minutes + +If restore consistently takes over 2 minutes even after the first run, check: + +- **NuGet cache is working**: Run `dotnet nuget locals all --list` to confirm the cache directory is accessible. +- **No floating versions**: Search for `*` or `latest` version constraints in your project files. +- **Network issues**: Try `dotnet restore --verbosity detailed` to see which packages are being downloaded and from which feeds. + +```bash title="Bash — Diagnose slow restore" +dotnet restore --verbosity detailed 2>&1 | grep -E "(Restoring|OK|Error)" +``` + +## See also + +- [Persistent containers](/app-host/persistent-containers/) +- [Launch profiles](/fundamentals/launch-profiles/) +- [`aspire doctor`](/reference/cli/commands/aspire-doctor/) +- [Prerequisites](/get-started/prerequisites/)