Skip to content
Free public beta until December 4, 2026: every course is open, no card needed. After that, the first chapter of each course stays free and full access is a one-time $39 ($19 for up to 100 founding members chosen for helpful feedback). Your progress is kept. How the beta works

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.json or appsettings.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__PageSize for Shop: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

Override configuration for the app under test without touching its files.