Hosting environment: Production and Now listening on: http://localhost:5000
from an app that said Development and its own port a minute ago? You ran
dotnet run --no-launch-profile, which skips Properties/launchSettings.json,
and that file is where both the environment and the URL came from. Run with the profile, or set
the environment yourself.
The quickest fixes are dotnet run --launch-profile http, or keeping the flag and
passing the settings to the app after a double dash:
dotnet run --no-launch-profile -- --environment Development. On the .NET 10 SDK,
leaving out that -- silently does nothing for the app.
The error
There is no error, only different startup lines. With the launch profile (paths shortened, nothing else changed):
Using launch settings from ...\w1\Properties\launchSettings.json...
Building...
info: Microsoft.Hosting.Lifetime[14]
Now listening on: http://localhost:15910
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
Hosting environment: Development
The same project with dotnet run --no-launch-profile:
info: Microsoft.Hosting.Lifetime[14]
Now listening on: http://localhost:5000
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
Hosting environment: Production
info: Microsoft.Hosting.Lifetime[0]
Content root path: ...\w1
And what the test script logged when it fetched two endpoints from each run, with the profile first:
GET http://localhost:15910/ -> 200; body length 75
Environment: Development, Greeting: Hello from appsettings.Development.json
GET http://localhost:15910/boom -> 500; Content-Type 'text/plain; charset=utf-8'; body length 531
GET http://localhost:5000/ -> 200; body length 44
Environment: Production, Greeting: (not set)
GET http://localhost:5000/boom -> 500; Content-Type ''; body length 0
Why it happens
launchSettings.json is a development-time file that dotnet run and
Visual Studio read; the app itself never opens it. Its http profile supplies
ASPNETCORE_ENVIRONMENT=Development and an applicationUrl. Skip it and
neither arrives: the host falls back to Production and Kestrel to
http://localhost:5000.
Everything that depends on the environment changes with it. The builder looks for
appsettings.Production.json instead of appsettings.Development.json,
so the Greeting value was missing, and the developer exception page is only switched on in
Development, so the failing endpoint returned an empty 500 instead of a stack trace. In a Blazor
app the same switch also empties every CSS and JS file, as the
MapStaticAssets post shows.
The trap: dotnet run --no-launch-profile --environment Development still logged
Production on SDK 10.0.401. dotnet run --help on SDKs 9.0.317 and 10.0.401 lists its own
-e, --environment <NAME="VALUE"> option, so the SDK took the flag, set a
variable literally named Development to an empty string, and passed the app no
arguments. On SDK 8.0.425 the same command reached the app and gave Development.
The fix
# Simplest: use the profile, or name it
dotnet run --launch-profile http
# Keep --no-launch-profile: pass environment and port to the app after --
dotnet run --no-launch-profile -- --environment Development --urls http://localhost:15912
# Or let the SDK set the variable for the app
dotnet run --no-launch-profile -e ASPNETCORE_ENVIRONMENT=Development -- --urls http://localhost:15914
# Or set it for the process (PowerShell), then run as before
$env:ASPNETCORE_ENVIRONMENT = "Development"
dotnet run --no-launch-profile
All four logged Hosting environment: Development, returned the Greeting and gave
the stack trace back on the failing endpoint. Note that the last one still listened on port
5000: the environment variable fixes the environment, not the URL, which also came from the
profile. DOTNET_ENVIRONMENT=Development worked the same way when
ASPNETCORE_ENVIRONMENT was not set.
How it was reproduced
A dotnet new web project on the .NET SDK 10.0.401 (ASP.NET Core runtime 10.0.12),
with the template's http profile moved to port 15910, a Greeting value in
appsettings.Development.json, and the endpoints below. Each command was started
by a script from a shell with no ASPNETCORE_ or DOTNET_ENVIRONMENT
variables, probed with Invoke-WebRequest and stopped. The SDK 8 comparison used a
net8.0 copy pinned to SDK 8.0.425 with global.json.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", (IConfiguration config, IHostEnvironment env) =>
$"Environment: {env.EnvironmentName}, Greeting: {config["Greeting"] ?? "(not set)"}");
app.MapGet("/boom", string () => throw new InvalidOperationException("Something broke"));
// Diagnostics for the experiment: what reached the app?
app.MapGet("/args", () =>
$"args: [{string.Join(", ", args)}], variable 'Development': " +
(Environment.GetEnvironmentVariable("Development") is { } v ? $"set to '{v}'" : "not set"));
app.Run();
Frequently asked
- Why does my ASP.NET Core app say Hosting environment: Production with dotnet run?
- You probably ran it with --no-launch-profile. The Development setting normally comes from the launch profile in Properties/launchSettings.json, and with no environment set the host defaults to Production.
- Why does dotnet run --no-launch-profile listen on port 5000?
- The port in launchSettings.json is part of the skipped profile. With no URL configured anywhere, Kestrel listens on http://localhost:5000. Pass --urls after a double dash, or run with --launch-profile.
- Why does dotnet run --environment Development not work on .NET 10?
- On the .NET 10 SDK, dotnet run has its own -e, --environment option that sets environment variables in NAME=VALUE form, so it consumes the flag instead of passing it to your app. Write dotnet run --no-launch-profile -- --environment Development, where the double dash hands the rest to the app, or use the SDK option as intended: -e ASPNETCORE_ENVIRONMENT=Development.
More decoded errors in the Fixes category, including another dotnet run switch that lands somewhere you did not expect.