Isaac.

devops

Environment Configuration

Manage configurations across development, staging, and production.

By Emem IsaacJanuary 31, 20232 min read
#configuration#environments#settings#dotenv
Share:

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.

Share:

Written by Emem Isaac

Expert Software Engineer with 15+ years of experience building scalable enterprise applications. Specialized in ASP.NET Core, Azure, Docker, and modern web development. Passionate about sharing knowledge and helping developers grow.

Ready to Build Something Amazing?

Let's discuss your project and explore how my expertise can help you achieve your goals. Free consultation available.

💼 Trusted by 50+ companies worldwide | ⚡ Average response time: 24 hours