configuration
codewithmukesh/dotnet-claude-kit
Options pattern, secrets management, and environment-based configuration for .NET 10 applications.
What is configuration?
Covers .NET configuration best practices including the Options pattern, IOptions vs IOptionsSnapshot, secrets management, and environment layering. Load this when setting up application configuration, managing secrets, or binding configuration sections.
- Bind configuration sections to strongly-typed classes with validation attributes
- Use IOptions, IOptionsSnapshot, and IOptionsMonitor for different lifetime scenarios
- Validate configuration at startup to fail fast on misconfiguration
- Manage secrets safely with user-secrets in development and Azure Key Vault or environment variables in production
- Support configuration layering across appsettings.json, environment-specific files, and environment variables
- Configure multiple named instances of the same options class for different contexts
How to install configuration
npx skills add https://github.com/codewithmukesh/dotnet-claude-kit --skill configurationHow to use configuration
- 1.Define a strongly-typed options class with validation attributes (Required, Range, etc.)
- 2.Register the options in Program.cs using AddOptions, BindConfiguration, and ValidateOnStart
- 3.Inject IOptions<T>, IOptionsSnapshot<T>, or IOptionsMonitor<T> into services based on your lifetime needs
- 4.Provide configuration values via appsettings.json structure (without secrets), user-secrets in development, or environment variables in production
- 5.For production, configure Azure Key Vault as an additional configuration source using AddAzureKeyVault
Use cases
- Setting up database connection strings and retry policies with validation
- Managing JWT secrets and token expiration settings securely
- Configuring SMTP options for internal and customer email services separately
- Loading production secrets from Azure Key Vault while using user-secrets in development
- Validating complex configuration rules at application startup to prevent runtime failures
- Backend developers building .NET 10 applications
- DevOps engineers managing secrets and environment-based configuration
- Teams implementing secure configuration patterns and secrets management
configuration FAQ
Use IOptions for singleton, immutable configuration read once at startup. Use IOptionsSnapshot for scoped access that re-reads per request (useful for reloadable config). Use IOptionsMonitor for singleton services that need to actively watch for configuration changes.
Never put secrets in appsettings.json. Use dotnet user-secrets for development and Azure Key Vault or environment variables for production. The appsettings.json should contain only the structure and non-sensitive defaults.
With ValidateOnStart(), the application fails immediately at startup with a clear error message, preventing misconfiguration from reaching production. Without it, errors are discovered at runtime when the configuration is first accessed.
Yes, use named options with AddOptions<T>(name).BindConfiguration(...) and retrieve them with IOptionsSnapshot<T>.Get(name) in your services.
Configuration sources are applied in order: appsettings.json → appsettings.{Environment}.json → environment variables → user secrets. Later sources override earlier ones, allowing environment-specific overrides.
Full instructions (SKILL.md)
Source of truth, from codewithmukesh/dotnet-claude-kit.
name: configuration description: > Configuration patterns for .NET 10 applications. Covers the Options pattern, IOptionsSnapshot vs IOptions, secrets management, and environment-based configuration. Load this skill when setting up application configuration, managing secrets, binding configuration sections, or when the user mentions "configuration", "appsettings", "Options pattern", "IOptions", "IOptionsSnapshot", "secrets", "user secrets", "environment variables", "connection string", or "config binding".
Configuration
Core Principles
- Options pattern always — Never read
IConfigurationdirectly in services. Bind configuration sections to strongly-typed classes with validation. - Validate on startup — Use
ValidateDataAnnotations()andValidateOnStart()to catch misconfiguration before the first request. - Secrets never in source — Use user secrets in development, Azure Key Vault or environment variables in production. Never commit secrets to git.
- Configuration layering —
appsettings.json→appsettings.{Environment}.json→ environment variables → user secrets. Later sources override earlier ones.
Patterns
Options Pattern
// Options class with validation attributes
public class DatabaseOptions
{
public const string SectionName = "Database";
[Required]
public required string ConnectionString { get; init; }
[Range(1, 100)]
public int MaxRetryCount { get; init; } = 3;
[Range(1, 60)]
public int CommandTimeoutSeconds { get; init; } = 30;
}
// Registration with validation
builder.Services.AddOptions<DatabaseOptions>()
.BindConfiguration(DatabaseOptions.SectionName)
.ValidateDataAnnotations()
.ValidateOnStart(); // Fails at startup if configuration is invalid
// appsettings.json
{
"Database": {
"ConnectionString": "",
"MaxRetryCount": 3,
"CommandTimeoutSeconds": 30
}
}
Injecting Options
// IOptions<T> — singleton, read once at startup, doesn't change
public class OrderService(IOptions<DatabaseOptions> options)
{
private readonly DatabaseOptions _db = options.Value;
}
// IOptionsSnapshot<T> — scoped, re-reads per request (for reloadable config)
public class OrderService(IOptionsSnapshot<DatabaseOptions> options)
{
private readonly DatabaseOptions _db = options.Value;
}
// IOptionsMonitor<T> — singleton, actively watches for changes
public class BackgroundWorker(IOptionsMonitor<WorkerOptions> options)
{
public void DoWork()
{
var current = options.CurrentValue; // Always latest
}
}
Custom Validation (Complex Rules)
builder.Services.AddOptions<JwtOptions>()
.BindConfiguration("Jwt")
.Validate(options =>
{
if (string.IsNullOrEmpty(options.Key) || options.Key.Length < 32)
return false;
if (options.ExpirationMinutes <= 0)
return false;
return true;
}, "JWT key must be at least 32 characters and expiration must be positive")
.ValidateOnStart();
Azure Key Vault (Production)
// Program.cs — add Key Vault as a configuration source
if (builder.Environment.IsProduction())
{
var keyVaultUri = new Uri(builder.Configuration["KeyVault:Uri"]!);
builder.Configuration.AddAzureKeyVault(keyVaultUri, new DefaultAzureCredential());
}
Configuration for Multiple Environments
// Named options — different config per named instance
builder.Services.AddOptions<SmtpOptions>("internal")
.BindConfiguration("Smtp:Internal");
builder.Services.AddOptions<SmtpOptions>("customer")
.BindConfiguration("Smtp:Customer");
// Usage
public class EmailService(IOptionsSnapshot<SmtpOptions> options)
{
public async Task SendInternalEmail(string to, string body)
{
var smtp = options.Get("internal");
// ...
}
}
Anti-patterns
Don't Read IConfiguration Directly
// BAD — stringly-typed, no validation, hard to test
public class OrderService(IConfiguration config)
{
public void Process()
{
var timeout = int.Parse(config["Database:CommandTimeout"]!);
}
}
// GOOD — strongly-typed options
public class OrderService(IOptions<DatabaseOptions> options)
{
public void Process()
{
var timeout = options.Value.CommandTimeoutSeconds;
}
}
Don't Put Secrets in appsettings.json
// BAD — committed to source control
{
"Jwt": { "Key": "super-secret-key" },
"Database": { "ConnectionString": "Server=prod;Password=secret" }
}
// GOOD — appsettings.json has defaults/structure only
{
"Jwt": { "Key": "", "Issuer": "myapp", "Audience": "myapp" },
"Database": { "ConnectionString": "" }
}
// Secrets provided via user-secrets (dev) or env vars / Key Vault (prod)
Don't Skip Startup Validation
// BAD — misconfiguration discovered at runtime
builder.Services.Configure<JwtOptions>(builder.Configuration.GetSection("Jwt"));
// GOOD — fail fast at startup
builder.Services.AddOptions<JwtOptions>()
.BindConfiguration("Jwt")
.ValidateDataAnnotations()
.ValidateOnStart();
Decision Guide
| Scenario | Recommendation |
|---|---|
| Binding config to class | Options pattern with BindConfiguration |
| Simple, immutable config | IOptions<T> |
| Config that changes per request | IOptionsSnapshot<T> |
| Background service watching config | IOptionsMonitor<T> |
| Development secrets | dotnet user-secrets |
| Production secrets | Azure Key Vault or environment variables |
| Validating config | ValidateDataAnnotations() + ValidateOnStart() |
| Multiple configs of same type | Named options with IOptionsSnapshot<T>.Get(name) |
Related skills
More from codewithmukesh/dotnet-claude-kit and the wider catalog.

container-publish
Containerize .NET 10 apps without writing a Dockerfile using SDK-native publishing.

convention-learner
Detect and enforce project-specific coding conventions by analyzing existing codebase patterns.

ddd
Domain-Driven Design tactical patterns for .NET: aggregates, value objects, domain events, and repositories.

de-sloppify
Systematic 7-step .NET code cleanup pipeline with verification between each phase.

dependency-injection
Dependency injection patterns for .NET 10: service lifetimes, keyed services, decorators, and common pitfalls.

docker
Multi-stage Docker builds and containerization patterns for .NET 10 applications.