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:
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:
Using User Secrets (Local Development)
Run
dotnet user-secrets init
Store a secret
dotnet user-secrets set "ConnectionStrings:DefaultConnection" "Server=.;Database=DevDB;Encrypt=false"
User secrets are stored outside the project source, so they never enter Git.
Using Azure Key Vault (Production Example)
builder.Configuration.AddAzureKeyVault(new Uri(keyVaultUrl), new DefaultAzureCredential());
This replaces sensitive content securely during runtime.
Workflow Diagram
┌───────────────────────┐
│ appsettings.json │
└───────────────┬───────┘
│
┌───────────────▼─────────────────┐
│ appsettings.Environment.json │
└───────────────┬─────────────────┘
│
┌───────────────▼─────────────────┐
│ User Secrets (Dev Only) │
└───────────────┬─────────────────┘
│
┌───────────────▼─────────────────┐
│ Environment Variables │
└───────────────┬─────────────────┘
│
┌───────────────▼─────────────────┐
│ Cloud Secret Providers │
└──────────────────────────────────┘
Flowchart: Configuration Resolution Logic
┌──────────────────────┐
│Application Starts │
└───────────┬─────────┘
│
▼
┌──────────────────────────────────┐
│ Load Base appsettings.json │
└───────────────────┬──────────────┘
│
▼
┌────────────────────────────────────┐
│ Load Environment-Specific File │
└─────────────────────┬──────────────┘
│
▼
┌────────────────────────────────────┐
│ Apply Secrets and Environment Vars │
└─────────────────────┬──────────────┘
│
▼
┌────────────────────────────────────┐
│ Inject Final Configuration in DI │
└────────────────────────────────────┘
Feature Flags Example
{"FeatureFlags": {
"EnableDiscountService": true}}
Use in code:
if (_config.EnableDiscountService)
{
ApplyDiscount();
}
Future improvement: Connect with LaunchDarkly or Azure App Configuration.
Updating Values Without Recompiling
ASP.NET Core can reload configuration dynamically:
builder.Configuration.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
Useful for:
Best Practices
Keep appsettings.json free of sensitive data.
Use typed configuration via IOptions<T> or IOptionsSnapshot<T>.
Use environment-specific configuration files.
Enable runtime reload where appropriate.
Use cloud secret services for production.
Structure configuration cleanly using hierarchy.
Version configuration changes and track environment drift.
Common Mistakes
| Mistake | Result |
|---|
| Storing secrets directly | Security breach risk |
| Hardcoding values in code | No environment flexibility |
| Using same config for dev and prod | Risk of accidental live API use |
| Not using typed configuration | Typo-based silent failures |
Final Recommendations
Treat configuration as code but protect secrets separately.
Keep configuration modular, versioned, and environment-specific.
Implement automated validation for critical configuration keys.
Combine configuration with logging, monitoring, and feature flags for full observability.
Conclusion
Configuration management is a core engineering capability in ASP.NET Core applications. When used properly, appsettings.json enables maintainability, security, flexibility, and predictable deployment cycles. By layering configuration, protecting secrets, and using strongly typed access, teams can build reliable systems ready for enterprise-scale operations.