appsettings.json contains common settings. appsettings.{Environment}.json contains settings that override them for the active environment.
ASP.NET Core selects the environment-specific file automatically when you use:
var builder = WebApplication.CreateBuilder(args);
Use these exact filename conventions:
appsettings.json
appsettings.Development.json
appsettings.UAT.json
appsettings.Production.json
Use consistent casing. File paths can be case-sensitive on Linux, so appSettings.production.json may not match the expected filename.
1. What is each file used for?
| File | Purpose | When loaded |
|---|---|---|
appsettings.json |
Common settings and defaults | Every environment |
appsettings.Development.json |
Local development overrides | Environment is Development |
appsettings.UAT.json |
User Acceptance Testing overrides | Environment is UAT |
appsettings.Production.json |
Live application overrides | Environment is Production |
Development, Staging, and Production are conventional environment names. UAT is a valid custom environment name. Microsoft Learn
2. How does ASP.NET Core select the environment?
You normally set an environment variable on the machine, hosting platform, or application process:
ASPNETCORE_ENVIRONMENT=UAT
ASP.NET Core then loads:
appsettings.json
appsettings.UAT.json
For:
ASPNETCORE_ENVIRONMENT=Production
It loads:
appsettings.json
appsettings.Production.json
It does not load Development, UAT, and Production files together.
If no environment is configured, the default is Production.
For modern applications using WebApplication.CreateBuilder, DOTNET_ENVIRONMENT can also select the environment and takes precedence over ASPNETCORE_ENVIRONMENT if both are set. Avoid configuring conflicting values. Microsoft Learn
3. Simple configuration example
appsettings.json — common settings
{
"ApplicationSettings": {
"ApplicationName": "Product API",
"SupportEmail": "support@example.com",
"PageSize": 20
},
"ExternalApi": {
"BaseUrl": "https://sandbox.example.com"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
These settings provide the baseline for all environments.
appsettings.Development.json
{
"ApplicationSettings": {
"PageSize": 5
},
"ExternalApi": {
"BaseUrl": "https://localhost:7002"
},
"Logging": {
"LogLevel": {
"Default": "Debug"
}
}
}
appsettings.UAT.json
{
"ApplicationSettings": {
"PageSize": 10
},
"ExternalApi": {
"BaseUrl": "https://uat-api.example.com"
}
}
appsettings.Production.json
{
"ApplicationSettings": {
"PageSize": 50
},
"ExternalApi": {
"BaseUrl": "https://api.example.com"
}
}
4. What values will the application actually use?
Assuming no other configuration source overrides these values:
| Setting | Development | UAT | Production |
|---|---|---|---|
| Application name | Product API | Product API | Product API |
| Support email | support@example.com | support@example.com | support@example.com |
| Page size | 5 | 10 | 50 |
| External API URL | Local API | UAT API | Production API |
| Default log level | Debug | Information | Information |
Settings are overridden by key, not by replacing the entire file or section.
For example, Production only overrides:
"ApplicationSettings": {
"PageSize": 50
}
The effective section still contains:
{
"ApplicationName": "Product API",
"SupportEmail": "support@example.com",
"PageSize": 50
}
The name and email remain available from the common file. This follows the configuration rule that later providers override earlier values for matching keys. Microsoft Learn
5. Read the settings in your program
A simple Program.cs:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
var app = builder.Build();
var applicationName =
app.Configuration["ApplicationSettings:ApplicationName"];
var pageSize =
app.Configuration.GetValue<int>("ApplicationSettings:PageSize");
var externalApiUrl =
app.Configuration["ExternalApi:BaseUrl"];
app.Logger.LogInformation(
"Environment: {Environment}, Application: {Application}, PageSize: {PageSize}",
app.Environment.EnvironmentName,
applicationName,
pageSize);
app.MapControllers();
app.Run();
For UAT, the log includes:
Environment: UAT, Application: Product API, PageSize: 10
You do not need to manually read appsettings.UAT.json. Configuration exposes the combined effective values.
To read a connection string:
var connectionString =
builder.Configuration.GetConnectionString("DefaultConnection");
This reads the effective value of:
ConnectionStrings:DefaultConnection
Avoid logging connection strings, passwords, or tokens.
6. How do we set the environment locally?
Option A: Visual Studio — launchSettings.json
This file is under the Properties folder.
You can define separate launch profiles:
{
"profiles": {
"Development": {
"commandName": "Project",
"launchBrowser": false,
"applicationUrl": "http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"UAT": {
"commandName": "Project",
"launchBrowser": false,
"applicationUrl": "http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "UAT"
}
}
}
}
Select the required profile beside Visual Studio’s Run button.
Or run:
dotnet run --launch-profile UAT
This runs your local application using the UAT environment name. It does not deploy anything to a UAT server.
launchSettings.json is for local launch tooling; deployed applications do not use it to choose their environment. Microsoft Learn
Option B: PowerShell
$env:ASPNETCORE_ENVIRONMENT = "UAT"
dotnet run --no-launch-profile
For Production:
$env:ASPNETCORE_ENVIRONMENT = "Production"
dotnet run --no-launch-profile
--no-launch-profile prevents a local launch profile from supplying different settings.
7. How does this work after deployment?
The hosting environment supplies the environment name.
For example, configure these values in your Azure App Service application settings/environment variables:
| Hosting application | Setting | Value |
|---|---|---|
| UAT application | ASPNETCORE_ENVIRONMENT |
UAT |
| Production application | ASPNETCORE_ENVIRONMENT |
Production |
Each application loads its corresponding file when it starts.
You do not need code such as:
// Not required for normal environment selection.
if (serverName == "ProductionServer")
{
// Load production configuration.
}
Publishing with Release does not select Production.
| Concept | Controls |
|---|---|
Debug / Release |
Build configuration |
Development / UAT / Production |
Runtime environment |
For example:
dotnet publish -c Release
Creates a Release build that can run in either UAT or Production, depending on the runtime environment.
8. Which setting takes priority?
With the default WebApplication.CreateBuilder(args) configuration, these application sources have increasing priority:
| Priority | Source |
|---|---|
| 1 — Lowest | appsettings.json |
| 2 | appsettings.{Environment}.json |
| 3 | User Secrets in Development, when configured |
| 4 | Environment variables |
| 5 — Highest | Command-line arguments |
A higher-priority source overrides the same key from a lower-priority source. Custom providers can change the final order. Microsoft Learn
For example:
appsettings.json:
{
"ApplicationSettings": {
"PageSize": 20
}
}
appsettings.Production.json:
{
"ApplicationSettings": {
"PageSize": 50
}
}
Hosting environment variable:
ApplicationSettings__PageSize=100
Final value: 100.
Use double underscores in environment variable names to represent nested configuration keys:
| Environment variable | Configuration key |
|---|---|
ApplicationSettings__PageSize |
ApplicationSettings:PageSize |
ConnectionStrings__DefaultConnection |
ConnectionStrings:DefaultConnection |
JwtSettings__Secret |
JwtSettings:Secret |
The double-underscore format works across platforms. Microsoft Learn
9. What is the common industry approach?
There is no single mandatory standard, but a widely used approach is:
A. Keep common, non-secret defaults in appsettings.json
Examples:
- Default page size.
- Application name.
- General logging levels.
- Non-sensitive timeout settings.
B. Keep environment files small
Only include values that differ between environments.
For example:
{
"ExternalApi": {
"BaseUrl": "https://uat-api.example.com"
}
}
Avoid copying the entire base file into every environment file. Duplication makes configuration harder to maintain.
C. Keep secrets outside committed JSON files
| Setting | Typical storage |
|---|---|
| Local database password | User Secrets |
| Production database password | Secret manager or protected hosting configuration |
| JWT signing secret | Secret manager |
| External service API key | Secret manager |
| Non-secret service URL | Environment JSON or hosting configuration |
For Azure applications, Azure Key Vault with managed identity is a common approach.
An environment-specific filename does not make its contents secure.
D. Build once and promote the same artifact
A typical deployment process is:
- Build and test the application.
- Produce a Release artifact.
- Deploy that artifact to UAT.
- Supply UAT configuration and secrets.
- After approval, deploy the same artifact to Production.
- Supply Production configuration and secrets.
This reduces differences between the code tested in UAT and the code released to Production.
E. Keep environments isolated
UAT should have its own databases, credentials, storage, and external service configuration.
For example:
| Environment | Database | External payment service |
|---|---|---|
| Development | Local development database | Sandbox |
| UAT | UAT database | Sandbox/test account |
| Production | Production database | Live account |
Use separate identities and permissions so an incorrect setting cannot easily give UAT access to production resources.
F. Validate required settings at startup
Fail early if a required setting is missing:
var connectionString =
builder.Configuration.GetConnectionString("DefaultConnection");
if (string.IsNullOrWhiteSpace(connectionString))
{
throw new InvalidOperationException(
"DefaultConnection is not configured.");
}
For larger applications, strongly typed options with startup validation make this more maintainable.
10. Common mistakes
| Mistake | Correct understanding |
|---|---|
Creating appsettings.UAT.json automatically activates UAT |
The runtime environment must be set to UAT |
Putting the environment name inside appsettings.json selects the file |
Environment selection happens during host initialization |
| Publishing in Release selects Production | Build configuration and runtime environment are separate |
launchSettings.json controls the deployed application |
It controls local launch profiles |
| Production settings replace the entire common file | Matching keys override; other values remain |
UAT automatically loads appsettings.Staging.json |
Only appsettings.UAT.json is selected |
| A missing environment file always stops startup | Default environment JSON files are optional |
| Environment variables always override every provider | A custom provider added later can override them |
The optional-file behavior is especially important: a misspelled environment or missing file can leave your application using base defaults.
11. Key points
- Use
appsettings.jsonfor common defaults. - Use
appsettings.UAT.jsonandappsettings.Production.jsonfor environment overrides. WebApplication.CreateBuilder(args)loads them automatically.- Set the environment in the hosting platform or application process.
- Use exact, consistent filename casing.
- The default environment is Production when none is configured.
- Restart the application when changing its runtime environment.
- Keep secrets out of source-controlled settings files.
- Prefer the same build artifact across UAT and Production.
- Log the environment name at startup, but never log secrets.
- Custom environments require explicit checks:
if (builder.Environment.IsEnvironment("UAT"))
{
// UAT-specific behavior, only when necessary.
}
IsProduction()returns false in UAT. Any configuration code guarded only byIsProduction()—including loading a secret provider—will not run in UAT.
The last registered configuration provider wins when the same connection-string key exists in multiple sources.
For this common setup:
var builder = WebApplication.CreateBuilder(args);
// Added AFTER the default configuration providers.
builder.Configuration.AddAzureKeyVault(
new Uri(builder.Configuration["KeyVault:VaultUri"]!),
new DefaultAzureCredential());
Azure Key Vault wins, because you added it after the default providers, including environment variables.
| Source | Example value | Priority in this setup |
|---|---|---|
appsettings.json |
JSON database connection | Lowest |
| Environment variable | Environment database connection | Higher |
| Azure Key Vault | Key Vault database connection | Highest — selected |
ASP.NET Core uses provider registration order to resolve duplicate keys. Environment variables override JSON by default; a provider added afterward can override both. learn.microsoft.com
The names must represent the same configuration key:
| Source | Name |
|---|---|
appsettings.json |
"ConnectionStrings": { "DefaultConnection": "..." } |
| Environment variable | ConnectionStrings__DefaultConnection |
| Azure Key Vault secret | ConnectionStrings--DefaultConnection |
| Key used by .NET | ConnectionStrings:DefaultConnection |
Your application reads the final value using:
var connectionString =
builder.Configuration.GetConnectionString("DefaultConnection");
What happens in different situations?
| Situation | Selected value |
|---|---|
| Key Vault added last and contains the key | Key Vault |
| Key Vault loads successfully but does not contain that key | Environment variable |
| Neither Key Vault nor environment variables contains it | JSON configuration |
| Key Vault cannot be accessed during loading | Normally a startup error, not automatic fallback |
If your Key Vault registration is inside:
if (builder.Environment.IsProduction())
{
builder.Configuration.AddAzureKeyVault(
new Uri(builder.Configuration["KeyVault:VaultUri"]!),
new DefaultAzureCredential());
}
Then Key Vault participates only in Production. In UAT or Development, the environment variable overrides appsettings.json.
Key point: Key Vault has no permanent built-in “highest priority.” It wins here because you registered it last.