Every way to configure an app
appsettings.json, appsettings.{Environment}.json, user secrets, environment variables, the command line and the options pattern. Where each setting belongs, and which one wins.
Each source is a layer, and the last layer to set a key wins.
The standard host stacks them like this, lowest first:
appsettings.json, then appsettings.{Environment}.json, then user
secrets (Development only), then environment variables, then
command-line arguments.
So put each setting in the lowest layer that's right for it:
- A default everyone shares (page size, currency) →
appsettings.json, committed to Git. - Different in Development or Production (log level, a local
database file) →
appsettings.Development.jsonorappsettings.Production.json. - A secret on your own machine (an API key for testing) →
dotnet user-secrets. Never a file Git tracks. - A secret or a per-server value in production → an environment
variable (
Shop__PageSizeforShop:PageSize) or a secret store. - A one-off override for this run → the command line,
--Shop:PageSize=50.
In code, don't scatter config["Shop:PageSize"] everywhere. Bind a
section to a class with the options pattern, validate it at
startup, and inject IOptions<ShopOptions>.
Sources and layering
Formal. Host.CreateApplicationBuilder and
WebApplication.CreateBuilder add configuration sources in order:
appsettings.json, appsettings.{Environment}.json, user secrets (in
Development only), environment variables, then command-line arguments.
A key set by a later source overrides the same key from an earlier one;
keys nobody overrides keep their earlier value. Hierarchical keys use
: (Shop:PageSize); in environment variables, where : isn't allowed
everywhere, __ stands for it (Shop__PageSize).
In plain English. Settings come from several places stacked on top of each other: the main settings file, then a file for the current environment, then private developer secrets, then environment variables, then whatever you type on the command line. Each layer can replace values from the layers under it, and the top one wins. That lets one build run everywhere with different settings.
Example. With PageSize 10 in appsettings.json and 25 in
appsettings.Development.json, the app reads 10 in Production and 25 in
Development; Shop__PageSize=50 makes it 50, and --Shop:PageSize=99
on the command line makes it 99. Currency, set only in the base file,
stays USD throughout.
Why this rule exists. Defaults live with the code; each environment overrides only what differs, and secrets never need to be in a file in the repository.
Providers are asked from last to first, so a later source overrides an earlier one key by key.
JSON, environment variables and the command line as layers, and binding a section to a typed class.
Keeping secrets out of the repository, and how DOTNET_ENVIRONMENT picks the appsettings file.
In a real app
Host.CreateApplicationBuilder wires configuration, logging and DI with the standard layers.
The same layers in a web app, and ASPNETCORE_ENVIRONMENT.
A committed secret is leaked. Rotate it; deleting the line isn't enough.
In a test
Section
Replacing settings in a test
Override configuration for the app under test without touching its files.