How ASP.NET Core maps environment variables
Configuration is a flat list of string keys like Logging:LogLevel:Default. appsettings.json builds those keys from its nesting, and array elements get their index: Endpoints:0:Url. Environment variables cannot contain a colon on every platform, so the environment variable provider accepts a double underscore and turns each __ into :. That is the whole mapping.
The checks behind this page run the real providers from Microsoft.Extensions.Configuration: converting appsettings.json to environment variables and reading them back gives exactly the configuration the JSON file gives.
Which value wins
The default ASP.NET Core host reads sources in this order, and a later source overrides an earlier one:
appsettings.jsonappsettings.{Environment}.json, for example appsettings.Production.json- User secrets, only in the Development environment
- Environment variables
- Command-line arguments
So you only set the keys you want to change. The Keys table lets you pick them.
What catches people out
- Arrays cannot shrink.
Endpoints__0__Urlreplaces the first element, but elements 1, 2 and so on from appsettings.json stay. There is no way to remove them with an environment variable. - Dots are fine for Docker, not for bash. The default template has
Logging:LogLevel:Microsoft.AspNetCore. Docker, Compose and Kubernetes acceptLogging__LogLevel__Microsoft.AspNetCore;exportrejects it. $gets expanded. Docker Compose interpolates$VARin compose files and .env files, and Kubernetes expands$(VAR). A password with a$silently changes unless it is escaped. The output here escapes it.- YAML turns
trueand123into non-strings. Compose then rejects the file. Every value here is quoted. - A key containing
__cannot be set. The provider would read it as a colon. - Empty sections have no variable.
"Features": {}is a key with a null value; no environment variable can produce that. - Azure App Service connection strings get a prefix. A connection string named Main arrives as
SQLCONNSTR_Main(or MYSQLCONNSTR_, SQLAZURECONNSTR_, CUSTOMCONNSTR_), which the provider maps toConnectionStrings:Main. .NET 8 does not recognise POSTGRESQLCONNSTR_. - A prefix is stripped. With
AddEnvironmentVariables("MyApp_"), only variables starting with MyApp_ are read, andMyApp_Logging__LogLevel__DefaultbecomesLogging:LogLevel:Default.
FAQ
How do I set a nested appsettings.json value with an environment variable?
Join the keys with a double underscore. Logging:LogLevel:Default in appsettings.json becomes the environment variable Logging__LogLevel__Default, and ASP.NET Core's environment variable provider turns every __ back into a colon.
How do I set an array element from an environment variable?
Use the index as a key: Endpoints__0__Url sets Endpoints:0:Url. An environment variable can replace or add an element, but it cannot remove elements that appsettings.json defines.
Can I use a colon instead of a double underscore?
Only where the platform allows a colon in variable names, such as Windows. Bash cannot export such a name, and Azure App Service on Linux requires __. The double underscore works everywhere.
Do environment variables override appsettings.json?
Yes. With the default ASP.NET Core host, later sources win: appsettings.json, then appsettings.{Environment}.json, then user secrets in Development, then environment variables, then command-line arguments.
Why does appsettings.json true show up as True?
Configuration stores every value as a string. The JSON provider writes booleans as True and False and keeps numbers as written, so 1.50 stays 1.50. Binding to a bool or int converts them, and accepts true, True and TRUE alike.
Related tools
- JSON to YAML converter (for Docker Compose and Kubernetes files)
- Connection string builder
- JSON to C# class converter