Isaac.

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

  1. Keep defaults sensible: Should work for dev
  2. Use environment variables: Override per environment
  3. Never commit secrets: Use user secrets locally
  4. Validate configuration: Fail fast on startup
  5. 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.