dotnet run

Run an API from source with the right launch profile, environment and arguments, and know why dotnet run never belongs in production.

Signature
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

ParameterTypeDescription
$--projectoptionalpath= the project in the current folderProject to run when it is not in the current folder.
$--launch-profileoptionalstring= the first profile with commandName ProjectProfile from Properties/launchSettings.json to apply.
$--no-launch-profileoptionalflag= falseIgnore launchSettings.json: no URLs or environment variables from it.
$-c, --configurationoptionalstring= DebugBuild configuration.
$-f, --frameworkoptionalTFMRequired when the project targets more than one framework.
$--no-buildoptionalflag= falseRun the existing build output without building first.
$-e, --environmentoptionalKEY=VALUESet an environment variable for the app process; repeatable.
$--optionalseparatorEverything 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 --.

SourceWins overExample
Command line after --Everything below-- --Orders:PageSize=50
Environment variables (incl. -e and the launch profile)FilesOrders__PageSize=50
User secrets (Development only)appsettings filesConnectionStrings:Orders
appsettings.{Environment}.json, then appsettings.jsonNothingDefaults

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 dotnet process; signals reach the CLI first, which makes graceful shutdown less predictable. Run the published DLL or apphost instead.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

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.

Example · json
{
  "$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.

Example · bash
# 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-run

A 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.

Example · bash
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

  • Be the first to comment on this lesson.