Environment Configuration
Manage configurations across development, staging, and production.
By EMEPublished: February 20, 2025
configurationenvironmentssettingsdotenv
A Simple Analogy
Environment configuration is like custom car settings. What works for the city doesn't work on the highway - you adjust settings per environment.
Why Configuration?
- Flexibility: Different settings per environment
- Security: Keep secrets separate
- Scalability: Same code, different configs
- Deployment: Smooth transitions
- Compliance: Meet environment requirements
Configuration Strategies
// appsettings.json (shared defaults)
{
"Logging": {
"LogLevel": {
"Default": "Information"
}
},
"Database": {
"ConnectionString": "Server=localhost;Database=mydb"
}
}
// appsettings.Development.json (overrides)
{
"Logging": {
"LogLevel": {
"Default": "Debug"
}
},
"Database": {
"ConnectionString": "Server=localhost;Database=mydb_dev"
}
}
// appsettings.Production.json
{
"Logging": {
"LogLevel": {
"Default": "Warning"
}
}
}
Environment Variables
// Program.cs
var env = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
builder.Configuration
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{env}.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.AddUserSecrets<Program>(optional: true);
// Access configuration
var dbConnection = builder.Configuration.GetConnectionString("DefaultConnection");
var logLevel = builder.Configuration["Logging:LogLevel:Default"];
// Type-safe options
builder.Services.Configure<DatabaseOptions>(
builder.Configuration.GetSection("Database"));
Options Pattern
public class DatabaseOptions
{
public string ConnectionString { get; set; }
public int CommandTimeout { get; set; }
public int MaxPoolSize { get; set; }
}
public class MyService
{
private readonly DatabaseOptions _options;
public MyService(IOptions<DatabaseOptions> options)
{
_options = options.Value;
}
public void Connect()
{
var connection = new SqlConnection(_options.ConnectionString)
{
ConnectionTimeout = _options.CommandTimeout
};
}
}
Environment-Specific Logic
if (builder.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseDeveloperExceptionPage();
}
if (builder.Environment.IsProduction())
{
app.UseHsts();
app.UseHttpsRedirection();
}
if (builder.Environment.IsEnvironment("Staging"))
{
// Staging-specific behavior
}
Docker Configuration
FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /app
COPY . .
# Set environment
ENV ASPNETCORE_ENVIRONMENT=Production
ENV ASPNETCORE_URLS=http://+:80
EXPOSE 80
ENTRYPOINT ["dotnet", "MyApp.dll"]
Best Practices
- Keep defaults sensible: Should work for dev
- Use environment variables: Override per environment
- Never commit secrets: Use user secrets locally
- Validate configuration: Fail fast on startup
- Document settings: Explain each configuration
Related Concepts
- Secret management
- Configuration as Code
- Feature flags
- Infrastructure as Code
Summary
Use the Options pattern with JSON files and environment variables for flexible configuration. Keep secrets in separate systems and validate on startup.