ASPNETCORE_ENVIRONMENT
Set the hosting environment name that picks appsettings.{Environment}.json and IsDevelopment(), and know which source wins when several set it.
ASPNETCORE_ENVIRONMENT=Staging | DOTNET_ENVIRONMENT=Staging | --environment Staging- Added in .NET ASP.NET Core 1.0
Parameters
| Parameter | Type | Description |
|---|---|---|
$valueoptional | string= Production | Any name. Development, Staging and Production have helper methods; other names (QA, Perf) work with IsEnvironment(). Comparison in the helpers is case-insensitive. |
Return value
stringBecomes IHostEnvironment.EnvironmentName (builder.Environment, app.Environment). It selects appsettings.{EnvironmentName}.json, decides whether user secrets load, and drives IsDevelopment(), IsStaging(), IsProduction() and IsEnvironment(name).
Errors
Nothing. An unknown name is accepted silently: no matching appsettings file is loaded, IsDevelopment() is false, and the app behaves like Production with whatever base settings it has.
Changelog
- PHP ASP.NET Core 3.0The generic host reads DOTNET_ENVIRONMENT, so worker services without ASP.NET Core have an equivalent variable.
- PHP ASP.NET Core 6.0WebApplicationBuilder introduced; it reads both ASPNETCORE_ and DOTNET_ prefixed variables, and --environment on the command line beats both.
ASPNETCORE_ENVIRONMENT sets one string, IHostEnvironment.EnvironmentName, and the host makes three decisions from it at startup: which appsettings.{Environment}.json file is layered over appsettings.json, whether user secrets are added (only in Development, and only when the entry assembly has a UserSecretsId), and what IsDevelopment() and its siblings return. WebApplication turns on the developer exception page by itself when the name is Development, so no code is needed for that; the templates use IsDevelopment() to map the OpenAPI document with app.MapOpenApi(). When nothing sets it, the name is Production.
Where the value comes from, and who wins
| Source | Read by | Precedence in WebApplication |
|---|---|---|
--environment Staging (command line; with dotnet run, write dotnet run -- --environment Staging) | All hosts | Highest |
DOTNET_ENVIRONMENT | Generic host, Host.CreateApplicationBuilder, WebApplication | Beats ASPNETCORE_ENVIRONMENT |
ASPNETCORE_ENVIRONMENT | Web hosts only | Lowest |
WebApplicationOptions.EnvironmentName | Code | Fixed in code; use for tests only |
The table is what a .NET 10 WebApplicationBuilder does: when both variables are set to different values, DOTNET_ENVIRONMENT wins. That surprises teams who set ASPNETCORE_ENVIRONMENT=Staging in a Kubernetes manifest while a base image or a shared ConfigMap sets DOTNET_ENVIRONMENT=Production. Pick one variable per service (ASPNETCORE_ENVIRONMENT for web apps is the common convention) and make sure nothing else sets the other. Locally, Properties/launchSettings.json sets Development; that file is only used by dotnet run and the IDEs, never by a published app or a container. With dotnet run, the application's --environment argument must come after --: dotnet run has its own -e|--environment option that sets an environment variable and expects KEY=VALUE, so dotnet run --environment Staging fails. dotnet run -e ASPNETCORE_ENVIRONMENT=Staging is the other working form.
Production failure modes
- Case on Linux. The helpers compare case-insensitively, but the settings file is opened by name on a case-sensitive file system.
ASPNETCORE_ENVIRONMENT=stagingwith a file calledappsettings.Staging.jsonworks on a Windows laptop and silently loads nothing in the Linux container. - Missing variable. The default is
Production, which is the safe failure: no developer exception page, no user secrets. The unsafe mistake is the reverse, an image built withENV ASPNETCORE_ENVIRONMENT=Developmentthat leaks stack traces in production. - Business logic on the name.
if (env.IsStaging()) skipPayment()ties behaviour to a label. Put the behaviour behind a setting (Payments:Mode) that each environment's config sets explicitly.
The variable is read when the builder is created. Setting it later through builder.Configuration changes the configuration value but not builder.Environment, and the environment-specific JSON file has already been chosen.
Example
When to use it
- Promote one container image from Staging to Production by changing only ASPNETCORE_ENVIRONMENT in the Deployment manifest
- Load appsettings.Staging.json with a staging payment provider URL while keeping secrets out of every JSON file
- Diagnose why IsDevelopment() is false in docker compose: launchSettings.json is not used outside dotnet run
Frequently asked questions
What is the default environment?
Production. If no variable or command-line argument sets it, IHostEnvironment.EnvironmentName is "Production", so a forgotten setting fails safe: no developer exception page and no user secrets.
ASPNETCORE_ENVIRONMENT or DOTNET_ENVIRONMENT?
Either works for WebApplication; worker services read only DOTNET_ENVIRONMENT. If both are set, DOTNET_ENVIRONMENT wins in WebApplicationBuilder, so set exactly one and keep the other unset everywhere.
Why is appsettings.Staging.json ignored in my container?
Usually casing: ASPNETCORE_ENVIRONMENT=staging looks for appsettings.staging.json and Linux file names are case-sensitive. Also check the file is copied to the publish output (it is by default for appsettings*.json).
More examples
Kubernetes: one variable, set once
The same image is promoted from Staging to Production and only this value changes. Make sure no base image or shared <code>envFrom</code> ConfigMap also sets <code>DOTNET_ENVIRONMENT</code>, because in a WebApplication that one wins.
apiVersion: apps/v1
kind: Deployment
metadata:
name: orders-api
spec:
replicas: 3
selector:
matchLabels:
app: orders-api
template:
metadata:
labels:
app: orders-api
spec:
containers:
- name: api
image: registry.example.com/orders-api:1.8.2
env:
- name: ASPNETCORE_ENVIRONMENT
value: Staging
# appsettings.Staging.json must exist in the image with exactly this casing
A setting instead of an environment check
The environment name chooses diagnostics, such as a configuration dump that must never exist in Production; a named setting chooses behaviour. A load-test environment can then use the sandbox gateway without pretending to be Staging, and Production can never reach it by a typo in the environment name.
var builder = WebApplication.CreateBuilder(args);
// Bad: if (builder.Environment.IsStaging()) use the fake gateway.
// Good: each environment's appsettings file states the mode explicitly.
var paymentsMode = builder.Configuration["Payments:Mode"] ?? "Live";
if (paymentsMode == "Sandbox")
{
builder.Services.AddSingleton<IPaymentGateway, SandboxPaymentGateway>();
}
else
{
builder.Services.AddSingleton<IPaymentGateway, LivePaymentGateway>();
}
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
// Diagnostics only: this is what the environment name is for.
// (The developer exception page is already on in Development.)
app.MapGet("/debug/config", () =>
((IConfigurationRoot)app.Configuration).GetDebugView());
}
app.MapPost("/payments", (IPaymentGateway gateway, PaymentRequest request) =>
TypedResults.Ok(gateway.Charge(request)));
app.Run();
public record PaymentRequest(string OrderId, decimal Amount, string Currency);
public record PaymentResult(string PaymentId, string Status);
public interface IPaymentGateway
{
PaymentResult Charge(PaymentRequest request);
}
public class SandboxPaymentGateway : IPaymentGateway
{
public PaymentResult Charge(PaymentRequest request) => new($"sbx_{request.OrderId}", "approved");
}
public class LivePaymentGateway : IPaymentGateway
{
public PaymentResult Charge(PaymentRequest request) => new($"pay_{request.OrderId}", "pending");
}
Pinning the environment in an integration test
<code>WebApplicationOptions.EnvironmentName</code> fixes the name in code, which suits tests and tools. <code>IsEnvironment</code> compares case-insensitively, so it prints True; the file <code>appsettings.Testing.json</code> would still need that exact casing on Linux.
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
Args = args,
EnvironmentName = "Testing",
});
builder.Configuration.AddInMemoryCollection(new Dictionary<string, string?>
{
["ConnectionStrings:Orders"] = "Host=127.0.0.1;Database=orders_test;Username=test;Password=<from-secret-store>",
});
var app = builder.Build();
app.MapGet("/env", (IHostEnvironment env) => env.EnvironmentName);
Console.WriteLine(app.Environment.EnvironmentName);
Console.WriteLine(app.Environment.IsEnvironment("testing"));
Discussion