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:
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.
<!-- 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>
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 (__).
// 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"];
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.
// 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!