dotnet run
Run an API from source with the right launch profile, environment and arguments, and know why dotnet run never belongs in production.
dotnet run [--project <path>] [-c <cfg>] [-f <tfm>] [--launch-profile <name> | --no-launch-profile] [--no-build] [-e <KEY=VALUE>] [-- <app args>]- Added in .NET .NET Core 1.0
Parameters
| Parameter | Type | Description |
|---|---|---|
$--projectoptional | path= the project in the current folder | Project to run when it is not in the current folder. |
$--launch-profileoptional | string= the first profile with commandName Project | Profile from Properties/launchSettings.json to apply. |
$--no-launch-profileoptional | flag= false | Ignore launchSettings.json: no URLs or environment variables from it. |
$-c, --configurationoptional | string= Debug | Build configuration. |
$-f, --frameworkoptional | TFM | Required when the project targets more than one framework. |
$--no-buildoptional | flag= false | Run the existing build output without building first. |
$-e, --environmentoptional | KEY=VALUE | Set an environment variable for the app process; repeatable. |
$--optional | separator | Everything after it is passed to the application's args, not parsed by the CLI. |
Return value
process exit codeThe exit code of the application, or 1 when the build fails before the app starts.
Errors
Returns the build's exit code if compilation fails, otherwise whatever the application returns; an unhandled exception in the app gives a non-zero code.
Changelog
- PHP .NET Core 2.0Properties/launchSettings.json profiles applied by dotnet run.
- PHP .NET 10dotnet run app.cs runs a single-file, file-based app without a project file.
dotnet run is a development command. It restores and builds the project (Debug by default), reads Properties/launchSettings.json, applies one profile's environment variables and URLs, then starts the built app. It is equivalent to dotnet build followed by dotnet bin/Debug/net10.0/Orders.Api.dll with a few variables set, and those variables are the part that surprises people.
Launch profiles
Without --launch-profile, the first profile whose commandName is Project wins. The template's profiles set ASPNETCORE_ENVIRONMENT=Development and an applicationUrl such as http://localhost:5228. So every local run is Development: user secrets load, the developer exception page shows, OpenAPI is mapped. A published app never reads launchSettings.json and starts as Production on port 8080 in the official container images. Use --no-launch-profile (and -e ASPNETCORE_ENVIRONMENT=Production) to reproduce production start-up locally.
Arguments and configuration
Options before -- belong to the CLI; everything after it reaches args. WebApplication.CreateBuilder(args) adds the command line as the highest-priority configuration source, so dotnet run -- --Orders:PageSize=50 --urls http://127.0.0.1:5617 --environment Staging overrides appsettings, environment variables and the launch profile's URL. Without --, options the CLI does not know (such as --urls) are passed through, but an option that shares a name with one of dotnet run's own (-e/--environment, --project, -c, -f, --launch-profile) is consumed by the CLI: dotnet run --environment Staging fails because the CLI expects KEY=VALUE. Always put app arguments after --.
| Source | Wins over | Example |
|---|---|---|
Command line after -- | Everything below | -- --Orders:PageSize=50 |
Environment variables (incl. -e and the launch profile) | Files | Orders__PageSize=50 |
| User secrets (Development only) | appsettings files | ConnectionStrings:Orders |
appsettings.{Environment}.json, then appsettings.json | Nothing | Defaults |
File-based apps
dotnet run app.cs runs a single C# file with no project. #:package, #:sdk and #:property directives at the top replace the project file, which makes it a good fit for a one-off data fix or a load-test client. dotnet project convert app.cs turns it into a real project when it grows.
Why not in production
- It needs the SDK in the image, builds on every start (slow cold starts, compiler memory) and builds Debug unless told otherwise.
- It applies a launch profile if one is present, so a container that copies the source tree can start as Development and expose the developer exception page.
- It adds a parent
dotnetprocess; signals reach the CLI first, which makes graceful shutdown less predictable. Run the published DLL or apphost instead.
Example
When to use it
- Reproduce a Production-only start-up failure on a laptop with --no-launch-profile -e ASPNETCORE_ENVIRONMENT=Production
- Start the orders API on a fixed loopback port for an integration script with dotnet run -- --urls http://127.0.0.1:5617
- Run a single-file data-fix script with dotnet run tools/reprice-orders.cs without adding a project to the solution
Frequently asked questions
Why is my app Development under dotnet run but Production in the container?
The launch profile in Properties/launchSettings.json sets ASPNETCORE_ENVIRONMENT=Development, and only dotnet run (and IDEs) read that file. A published app has no launch profile, so it defaults to Production.
Why did dotnet run reject --environment Staging?
dotnet run has its own -e|--environment option that sets an environment variable and expects KEY=VALUE, so it consumed the argument. Options the CLI does not know, such as --urls, pass through, but names it does know (--environment, --project, -c, -f) do not. Put application arguments after the separator: dotnet run -- --environment Staging.
Should a Dockerfile use dotnet run?
No. Publish in a build stage and start the published app with dotnet Orders.Api.dll or the apphost. dotnet run needs the SDK, compiles on start and may apply a Development launch profile.
More examples
launchSettings.json with a production-like profile
<code>dotnet run</code> picks <code>http</code> because it is first. <code>dotnet run --launch-profile prodlike</code> starts the same code as Production against a local database, which is how teams catch a missing <code>appsettings.Production.json</code> key before deploying.
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"profiles": {
"http": {
"commandName": "Project",
"launchBrowser": false,
"applicationUrl": "http://localhost:5228",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"prodlike": {
"commandName": "Project",
"launchBrowser": false,
"applicationUrl": "http://localhost:5229",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Production",
"ConnectionStrings__Orders": "Host=localhost;Database=orders;Username=orders_app"
}
}
}
}Running projects and file-based scripts
<code>--project</code> keeps scripts independent of the current folder. <code>--no-build</code> must be paired with the configuration the build used, otherwise it looks for <code>bin/Debug</code> output that is not there.
# From the repository root: pick the project explicitly.
dotnet run --project src/Orders.Api
# Production start-up locally: no launch profile, explicit environment.
dotnet run --project src/Orders.Api --no-launch-profile -e ASPNETCORE_ENVIRONMENT=Production
# App arguments go after --; without it --environment is taken by the CLI's own -e option.
dotnet run --project src/Orders.Api -- --environment Staging --urls http://127.0.0.1:5617
# Already built in CI? Do not build again.
dotnet run --project src/Orders.Api -c Release --no-build
# A one-off file-based script; its #:package directive replaces a project file.
dotnet run tools/reprice-orders.cs -- --dry-runA file-based maintenance script
The <code>#:package</code> directive replaces a <code>PackageReference</code>: the script gets Humanizer with no <code>.csproj</code>. File-based apps already enable <code>Nullable</code> and <code>ImplicitUsings</code> by default, and a plain console script needs no <code>#:sdk</code> line; add <code>#:sdk Microsoft.NET.Sdk.Web</code> only when the file hosts an API. When it needs tests or a second file, <code>dotnet project convert</code> produces a normal project.
mkdir -p tools
cat > tools/reprice-orders.cs <<'CS'
#:package [email protected]
using System.Text.Json;
using Humanizer;
var dryRun = args.Contains("--dry-run");
PriceChange[] changes =
[
new("KETTLE-BLUE", 34.50m, 36.00m),
new("MUG-WHITE", 8.00m, 8.00m),
];
foreach (var c in changes.Where(c => c.Old != c.New))
Console.WriteLine($"{c.Sku}: {c.Old} -> {c.New}{(dryRun ? " (dry run)" : "")}");
var changed = changes.Count(c => c.Old != c.New);
Console.WriteLine("price change".ToQuantity(changed));
Console.WriteLine(JsonSerializer.Serialize(new { changed, dryRun }));
record PriceChange(string Sku, decimal Old, decimal New);
CS
dotnet run tools/reprice-orders.cs -- --dry-run
dotnet project convert tools/reprice-orders.cs # when it outgrows one file
Discussion