Introduction
Every production-grade ASP.NET Core application needs configuration values, such as database connection strings, API URLs, environment switches, feature flags, and external service credentials. These configurations evolve across environments: local development, staging, UAT, and production.
ASP.NET Core provides a flexible configuration system built around appsettings.json, environment overrides (like appsettings.Development.json), and secure secret storage mechanisms. However, many applications still misuse configuration — storing passwords in plain text, mixing environment values in a single file, hardcoding values, or not supporting runtime changes.
This article provides a real-world implementation approach to using appsettings.json effectively and securely, ensuring maintainability, scalability, and compliance.
Real-World Problem Scenario
A fintech product deployed across multiple regions faced these issues:
Developers tested payment API keys locally using production values.
Production configuration leaked accidentally into version control.
Scaling environments required different connection strings.
The application needed feature toggles but was built with hardcoded flags.
After implementing a structured configuration strategy using appsettings.json, secure storage (Secrets Manager and Azure Key Vault), and strongly-typed options patterns, the system improved in stability and governance.
This case demonstrates why configuration must be treated as a core architecture concern, not an afterthought.
What appsettings.json Is and Why It Exists
appsettings.json is a JSON-based configuration file automatically read by ASP.NET Core via its built-in configuration provider.
It supports:
Key-value configuration
Hierarchical configuration
Typed mapping to .NET objects
Environment-specific overrides
Secure replacement via external providers
Example Default File
{"ApplicationName": "PaymentAPI","ConnectionStrings": {
"DefaultConnection": "Server=.;Database=PaymentDB;Trusted_Connection=True;"},"ApiSettings": {
"PaymentGatewayUrl": "https://dev-payments.example.com",
"RetryCount": 3},"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning"
}}}Binding Configuration to Strongly Typed Classes (Recommended Pattern)
Instead of manually reading configuration, bind it to a class:
public class ApiSettings
{
public string PaymentGatewayUrl { get; set; }
public int RetryCount { get; set; }
}
Register it in Program.cs:
builder.Services.Configure<ApiSettings>(
builder.Configuration.GetSection("ApiSettings"));
Use it via DI:
public class PaymentService
{
private readonly ApiSettings _config;
public PaymentService(IOptions<ApiSettings> config)
{
_config = config.Value;
}
}
This increases type safety and avoids runtime errors due to missing keys.
Environment-Specific Configurations
ASP.NET Core automatically loads environment files if named:
appsettings.Development.jsonappsettings.Staging.jsonappsettings.Production.json
Example environment file override
{"ConnectionStrings": {
"DefaultConnection": "Server=prod-db;Database=PaymentDB;User Id=app;Password=secureProdPassword;"}}This allows different values per environment without changing code.
Loading Order Priority
ASP.NET Core loads configuration in this order (later overwrites earlier):
appsettings.json
appsettings.{Environment}.json
User secrets (Development only)
Environment variables
Command line arguments
Cloud secret providers (Azure Key Vault, AWS Secrets Manager, etc.)
This layered structure enables override without modifying base files.
Never Store Secrets in appsettings.json
Hardcoded credentials pose a security risk.
Example of what NOT to do
"Password": "MyProductionDatabasePassword"Instead, use:
Development →
dotnet user-secretsProduction → Key Vault / AWS Secrets Manager / Kubernetes Secrets
Using User Secrets (Local Development)
Run
dotnet user-secrets init
Join the conversation! Your thoughts help the community grow.