dotnet watch
Use dotnet watch for hot reload while editing an API, know which edits need a restart, and control the set of files it watches.
dotnet watch [run|test|build] [--project <path>] [--no-hot-reload] [--non-interactive] [--list] [-q|-v] [-- <app args>]- Added in .NET .NET Core 1.0 (bundled with the SDK since .NET Core 2.1)
Parameters
| Parameter | Type | Description |
|---|---|---|
$commandoptional | run | test | build= run | The dotnet command to run and re-run. |
$--projectoptional | path= the project in the current folder | Project to watch. |
$--no-hot-reloadoptional | flag= false | Restart the process on every change instead of patching the running app. |
$--non-interactiveoptional | flag= false | Never prompt; on an edit hot reload cannot apply, restart automatically. |
$--listoptional | flag= false | Print the files that would be watched and exit. |
$-q, -voptional | flag | Quieter or more verbose watcher output. |
Return value
process exit codeRuns until stopped with Ctrl+C; returns the exit code of the watched command when it exits.
Errors
Prints compile errors and waits for the next save instead of exiting. Edits that hot reload cannot apply (rude edits) trigger a prompt or an automatic restart.
Changelog
- PHP .NET 6Hot reload: supported edits are applied to the running process without a restart.
dotnet watch wraps another command (run by default) and re-runs it when files change. For run it also does hot reload: it starts the app with DOTNET_MODIFIABLE_ASSEMBLIES=debug, compiles each saved change to a delta and applies it to the running process through the runtime's metadata update API. Requests in flight keep running; the next call to a changed method uses the new code.
What hot reload can and cannot do
| Edit | Result |
|---|---|
| Change a method body, a lambda handler, a LINQ query | Applied in place |
| Add a method, property or new type | Usually applied |
Change top-level Program.cs (register a service, add middleware, map a route) | Applied, but the code already ran at start-up: no effect until restart |
| Change a method signature, a generic constraint, a record's positional parameters | Rude edit: needs a restart |
Edit .csproj or add a package | Full rebuild and restart |
The confusing case is the middle row: the edit "worked", no error shows, yet the new endpoint returns 404. Press Ctrl+R to restart. For rude edits the watcher asks whether to restart; --non-interactive restarts without asking, which suits a terminal you do not watch.
Caches that outlive the edit
Hot reload changes code, not state. A singleton that cached computed prices, a compiled regex built from a constant, or a memoised route table still hold old results. An assembly-level [MetadataUpdateHandler(typeof(T))] names a type whose static ClearCache(Type[]?) and UpdateApplication(Type[]?) methods run after each update, so you can clear them.
What is watched
By default the project's Compile and EmbeddedResource items plus referenced projects, and appsettings*.json through configuration reload. Add <Watch Include="Sql/**/*.sql" /> for other inputs, and mark items or project references Watch="false" to ignore them. dotnet watch --list prints the final set. In Docker bind mounts or network drives file events often never arrive; set DOTNET_USE_POLLING_FILE_WATCHER=1.
Rules for the team
- Development only. The watched app runs Debug, under the launch profile, with modifiable assemblies; never use it in a container that serves real traffic.
- The app sees
DOTNET_WATCH=1; use it to skip slow start-up work, never to change behaviour that tests should cover. dotnet watch testre-runs tests on save; pair it with a filter so it stays fast.
Example
When to use it
- Iterate on an order-pricing handler with hot reload and see the change on the next request without losing the running process
- Clear a memoised discount table on every hot reload with a MetadataUpdateHandler
- Run dotnet watch inside a Docker dev container where file events only arrive with polling enabled
Frequently asked questions
I added an endpoint and dotnet watch said the change was applied, but it returns 404. Why?
Route mapping runs once at start-up. Hot reload patched Program.cs but did not re-run it. Restart with Ctrl+R; endpoint, middleware and DI changes always need a restart.
Why does dotnet watch never notice my changes in Docker?
File system events from a bind mount often do not reach the container. Set DOTNET_USE_POLLING_FILE_WATCHER=1.
Can I use hot reload in production?
No. It needs a Debug build with modifiable assemblies and the SDK's watcher. Deploy changes through the normal publish pipeline.
More examples
Controlling the watched files
<code>Watch</code> items add inputs and <code>Watch="false"</code> removes them. Excluding generated files stops a code generator and the watcher from triggering each other in a loop; <code>dotnet watch --list</code> confirms the result.
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<!-- SQL scripts embedded and read at start-up: restart when they change. -->
<EmbeddedResource Include="Sql/**/*.sql" />
<Watch Include="Sql/**/*.sql" />
<!-- Generated client code is rewritten by a tool; do not react to it. -->
<Compile Update="Generated/**/*.cs" Watch="false" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../Orders.Contracts/Orders.Contracts.csproj" Watch="false" />
</ItemGroup>
</Project>Watch in a dev container
Bind mounts from macOS and Windows hosts often do not deliver file change events into the container, so the watcher never fires; polling fixes it at some CPU cost. The port is published on loopback only, so the developer exception page and OpenAPI document are not reachable from the local network. <code>--non-interactive</code> matters because nobody can answer the rude-edit prompt in a container log.
services:
orders-api:
image: mcr.microsoft.com/dotnet/sdk:10.0
working_dir: /src
command: ["dotnet", "watch", "--project", "src/Orders.Api", "--non-interactive", "--", "--urls", "http://0.0.0.0:8080"]
environment:
DOTNET_USE_POLLING_FILE_WATCHER: "1"
ASPNETCORE_ENVIRONMENT: Development
ports:
- "127.0.0.1:5080:8080"
volumes:
- ./:/srcEveryday watch commands
<code>--no-hot-reload</code> is the escape hatch when patched state confuses you: each save becomes a clean restart, which is slower but always truthful. Watching tests with a filter keeps the feedback loop to seconds.
# API with hot reload, launch profile applied
dotnet watch --project src/Orders.Api
# Restart on every change: for code that hot reload handles badly
dotnet watch --project src/Orders.Api --no-hot-reload
# Re-run fast tests on save
dotnet watch test --project tests/Orders.UnitTests --filter "Category=Unit"
# What is being watched?
dotnet watch --project src/Orders.Api --list
Discussion