Home About Lessons Blog On The Mic Contact Projects
Lessons DEPLOY · C#

ASP.NET Core Configuration Reading Order

8 min read · Emre Ulutabak
1
Layered reading logic

When you leave home in the morning, you layer up. An undershirt, a sweater, a jacket. What does someone see from outside? The jacket. The undershirt stays hidden underneath.

ASP.NET Core's configuration system works exactly like this. Multiple sources are read on top of each other, and the last one read wins — it overwrites the previous value.

The reading order is:

  • appsettings.json — always read, base values
  • appsettings.{Environment}.json — read based on environment (Production, Development...)
  • Environment Variables — read last, overwrites previous
  • Command Line Arguments — last of all, only in special cases
💡
The last one read wins. Environment Variables always override the same key in appsettings.json.
2
appsettings and environment files

The ASPNETCORE_ENVIRONMENT variable determines which environment file is read. If this variable is Production, then appsettings.Production.json is read; if it is Development, then appsettings.Development.json is read.

On shared hosting, this variable is usually set inside web.config or defined through the hosting panel.

xml
<!-- web.config — ASPNETCORE_ENVIRONMENT ayarı -->
<aspNetCore processPath="dotnet" arguments=".\MyApp.dll"
            hostingModel="outofprocess">
  <environmentVariables>
    <!-- Bu değer appsettings.Production.json'ın okunmasını sağlar -->
    <environmentVariable name="ASPNETCORE_ENVIRONMENT" value="Production" />
  </environmentVariables>
</aspNetCore>
3
__ (double underscore) rule

In JSON files, nested structures are represented with dots: ConnectionStrings.DefaultConnection. But environment variable names cannot contain dots — the operating system does not allow it.

That is why ASP.NET Core represents JSON hierarchy in env vars using a double underscore (__).

csharp
// appsettings.json'daki JSON yapısı:
// {
//   "ConnectionStrings": { "DefaultConnection": "..." },
//   "EmailSettings": { "SmtpServer": "smtp.brevo.com" },
//   "Jwt": { "PrivateKeyPath": "private.key" }
// }

// Karşılık gelen environment variable key'leri:
// ConnectionStrings__DefaultConnection
// EmailSettings__SmtpServer
// Jwt__PrivateKeyPath

// Program.cs'te okuma:
var connStr = builder.Configuration.GetConnectionString("DefaultConnection");
var smtp    = builder.Configuration.GetSection("EmailSettings")["SmtpServer"];
var keyPath = builder.Configuration["Jwt:PrivateKeyPath"];
💡
Rule: nested structure in JSON → __ (double underscore) in env var. Not a dot, a double underscore.
4
GetConnectionString rule

One of the most common mistakes is writing GetConnectionString("X") but putting the wrong key in the env var.

GetConnectionString("DefaultConnection") is actually a shortcut for Configuration["ConnectionStrings:DefaultConnection"]. So the env var key must be ConnectionStrings__DefaultConnection — whatever X is, the end of the key must match it.

csharp
// Program.cs
var connStr = builder.Configuration.GetConnectionString("DefaultConnection");
// Doğru env var key: ConnectionStrings__DefaultConnection

var connStr2 = builder.Configuration.GetConnectionString("database");
// Doğru env var key: ConnectionStrings__database
// YANLIS: ConnectionStrings__DefaultConnection — eşleşmez, null döner!
💡
GetConnectionString("X") → env var key = ConnectionStrings__X. Must match exactly.
5
Golden rules
💡
Don't put passwords in appsettings.json. This file goes to Git and everyone on the team can see it.
💡
Put secrets in environment variables. They are read last, they win — and they don't go to Git.
💡
If the key doesn't match, it returns null without throwing an error. When you see connection errors, check here first.
6
Mini quiz
MINI QUIZ
If `EmailSettings:SmtpServer` exists in appsettings.json, which environment variable key is used to override it?
EmailSettings.SmtpServer
EmailSettings-SmtpServer
EmailSettings__SmtpServer
SmtpServer__EmailSettings