ASPNETCORE_ENVIRONMENT

Set the hosting environment name that picks appsettings.{Environment}.json and IsDevelopment(), and know which source wins when several set it.

Signature
ASPNETCORE_ENVIRONMENT=Staging  |  DOTNET_ENVIRONMENT=Staging  |  --environment Staging
  • Added in .NET ASP.NET Core 1.0

Parameters

ParameterTypeDescription
$valueoptionalstring= ProductionAny 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

SourceRead byPrecedence in WebApplication
--environment Staging (command line; with dotnet run, write dotnet run -- --environment Staging)All hostsHighest
DOTNET_ENVIRONMENTGeneric host, Host.CreateApplicationBuilder, WebApplicationBeats ASPNETCORE_ENVIRONMENT
ASPNETCORE_ENVIRONMENTWeb hosts onlyLowest
WebApplicationOptions.EnvironmentNameCodeFixed 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=staging with a file called appsettings.Staging.json works 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 with ENV ASPNETCORE_ENVIRONMENT=Development that 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

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

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.

Example · yaml
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.

Example · csharp
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.

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

  • Be the first to comment on this lesson.