.NET Configuration
The .NET configuration system provides a flexible, extensible mechanism for managing application settings from multiple sources. It uses a layered approach where configuration values from different sources are merged, with later sources overriding earlier ones.
Configuration Sources
.NET applications can pull configuration from various sources, each serving different purposes and scenarios.
appsettings.json
The default JSON configuration file included in every ASP.NET Core project.
{
"Logging": {
"LogLevel": {
"Default": "Information"
}
},
"ConnectionStrings": {
"DefaultConnection": "Server=localhost;Database=CommandDB;"
}
}
When to use:
- Default application settings
- Settings that are common across all environments
- Non-sensitive configuration values
- Settings that might be modified by operations teams
Characteristics:
- Checked into source control
- Visible to anyone with repository access
- Easy to edit and maintain
- Can be overridden by other sources
appsettings.{Environment}.json
Environment-specific configuration files that override base settings.
// appsettings.Development.json
{
"Logging": {
"LogLevel": {
"Default": "Debug"
}
},
"ConnectionStrings": {
"DefaultConnection": "Server=localhost;Database=CommandDB_Dev;"
}
}
When to use:
- Environment-specific values (Development, Staging, Production)
- Overriding default appsettings for specific environments
- Different logging levels per environment
- Environment-specific connection strings
Characteristics:
- Automatically loaded based on
ASPNETCORE_ENVIRONMENTvariable - Overrides values in base
appsettings.json - Also checked into source control
- Keeps environment differences visible and maintainable
User Secrets
Stored locally on the developer's machine, outside the project directory.
dotnet user-secrets set "ApiKeys:OpenAI" "sk-proj-abc123..."
// Stored in: ~/.microsoft/usersecrets/{user-secrets-id}/secrets.json
{
"ApiKeys": {
"OpenAI": "sk-proj-abc123..."
}
}
When to use:
- Local development secrets (API keys, passwords)
- Connection strings with credentials during development
- Any sensitive data needed for local testing
- Avoiding accidental commits of secrets to source control
Characteristics:
- Development only - not available in production
- Stored per user, per machine
- Not checked into source control
- Accessed via Secret Manager tool
- Requires
UserSecretsIdin.csprojfile
Environment Variables
System-level or process-level variables set on the host machine.
# Linux/macOS
export ConnectionStrings__DefaultConnection="Server=prod-db;Database=CommandDB;"
# Windows
set ConnectionStrings__DefaultConnection=Server=prod-db;Database=CommandDB;
When to use:
- Production secrets and sensitive configuration
- Container and cloud deployments (Docker, Kubernetes)
- CI/CD pipeline configuration
- Server-specific settings
- Configuration that varies per deployment instance
Characteristics:
- Highly secure - not stored in code or files
- Supported across all platforms and hosting environments
- Use double underscore
__for hierarchical keys (e.g.,ConnectionStrings__DefaultConnection) - Essential for cloud-native applications
Command-Line Arguments
Configuration passed when starting the application.
dotnet run --ConnectionStrings:DefaultConnection="Server=localhost;Database=TestDB;"
When to use:
- Temporary overrides during testing
- Script-based deployments
- Debugging specific scenarios
- One-off configuration changes
Characteristics:
- Highest precedence - overrides all other sources
- Useful for quick testing without modifying files
- Use colon
:for hierarchical keys in command line - Not persistent
Azure Key Vault / Cloud Secrets Managers
Cloud-based secret management services (Azure Key Vault, AWS Secrets Manager, etc.).
When to use:
- Production secrets requiring enterprise-grade security
- Centralized secret management across applications
- Secrets with rotation requirements
- Compliance and audit requirements
Characteristics:
- Requires additional NuGet packages
- Network dependency for configuration loading
- Cached after initial load
- Can integrate with managed identities
Order of Precedence
Configuration sources are applied in a specific order, with later sources overriding earlier ones:
1. appsettings.json (lowest priority)
↓
2. appsettings.{Environment}.json
↓
3. User Secrets (Development only)
↓
4. Environment Variables
↓
5. Command-Line Arguments (highest priority)
Example: If the same setting exists in multiple sources:
appsettings.json:"LogLevel": "Information"appsettings.Development.json:"LogLevel": "Debug"- Environment Variable:
Logging__LogLevel__Default=Warning - Command-Line:
--Logging:LogLevel:Default=Error
Result: LogLevel = Error (command-line wins)
Accessing Configuration
Configuration is accessed through the IConfiguration interface, which is registered in the dependency injection container by default.
In Program.cs
var builder = WebApplication.CreateBuilder(args);
// Access configuration directly from builder
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
var apiKey = builder.Configuration["ApiKeys:OpenAI"];
var logLevel = builder.Configuration["Logging:LogLevel:Default"];
// Use configuration to setup services
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection"))
);
var app = builder.Build();
In Controllers or Services
Inject IConfiguration through constructor dependency injection:
public class PlatformsController : ControllerBase
{
private readonly IConfiguration _configuration;
public PlatformsController(IConfiguration configuration)
{
_configuration = configuration;
}
[HttpGet("info")]
public ActionResult GetInfo()
{
var appName = _configuration["ApplicationName"];
var environment = _configuration["ASPNETCORE_ENVIRONMENT"];
return Ok(new { appName, environment });
}
}
Strongly-Typed Configuration
Bind configuration sections to strongly-typed classes using the Options pattern:
// Configuration class
public class DatabaseOptions
{
public string ConnectionString { get; set; } = string.Empty;
public int MaxRetries { get; set; }
public int CommandTimeout { get; set; }
}
// In Program.cs
builder.Services.Configure<DatabaseOptions>(
builder.Configuration.GetSection("Database")
);
// In a service
public class DataService
{
private readonly DatabaseOptions _dbOptions;
public DataService(IOptions<DatabaseOptions> dbOptions)
{
_dbOptions = dbOptions.Value;
}
}
Configuration Keys
Access nested configuration using colon : notation:
{
"Database": {
"ConnectionString": "...",
"Pool": {
"MaxSize": 100
}
}
}
var maxSize = _configuration["Database:Pool:MaxSize"];
// or
var maxSize = _configuration.GetValue<int>("Database:Pool:MaxSize");
Best Practices
Do:
- Store non-sensitive defaults in
appsettings.json - Use User Secrets for local development secrets
- Use environment variables for production secrets
- Use strong typing with the Options pattern
- Document required configuration keys
Don't:
- Commit secrets to source control
- Hardcode sensitive values in code
- Use
appsettings.jsonfor production secrets - Access configuration everywhere - centralize in services
Common Pitfalls
- Case sensitivity: Configuration keys are case-insensitive, but it's best to be consistent
- Hierarchical separators: Use
:in code,__in environment variables - User Secrets in Production: They only work in Development environment
- Missing configuration: Always provide defaults or validate required settings at startup
The .NET configuration system's flexibility and layered approach makes it suitable for everything from local development to enterprise production deployments, while maintaining security and ease of use.
Further Reading: