Organize ASP.NET Core configuration into separate JSON files by concern (database, logging, services, etc.) and load them conditionally based on environment variables. Pass file names (without extensions) as parameters, and the library automatically loads base files plus environment-specific overrides (e.g., database.json + database.Production.json). Designed for containerized environments (Docker, Kubernetes) with mount paths. Optional encryption support for sensitive values. Follows SOLID principles with separate extension classes by responsibility.
$ dotnet add package Voyager.Configuration.MountPathAn ASP.NET Core extension for organizing JSON configuration files with support for environment-specific settings, encrypted values, and Docker/Kubernetes mount paths.
This library provides a simple way to organize JSON configuration files by file name (without extensions) and load them conditionally based on environment variables. Designed for containerized environments like Docker and Kubernetes, it allows you to:
database.json + database.Production.json)⚠️ DEPRECATION NOTICE: Built-in Encryption The built-in encryption feature is deprecated and will be removed in version 3.0. We recommend migrating to external secret management tools like Mozilla SOPS, Kubernetes Secrets, or cloud providers (Azure Key Vault, AWS Secrets Manager). See ADR-003: Encryption Delegation for migration guidance and rationale.
dotnet add package Voyager.Configuration.MountPath
Pass file names (without .json extension) as parameters:
using Microsoft.Extensions.DependencyInjection;
var builder = Host.CreateDefaultBuilder(args);
builder.ConfigureAppConfiguration((context, config) =>
{
var provider = context.HostingEnvironment.GetSettingsProvider();
// Loads: appsettings.json + appsettings.{Environment}.json
config.AddMountConfiguration(provider, "appsettings");
});
File structure:
YourApp/
├── bin/
│ └── config/
│ ├── appsettings.json # Base configuration
│ └── appsettings.Production.json # Environment-specific overrides
How it works:
appsettings.json (required)appsettings.{ASPNETCORE_ENVIRONMENT}.json (optional by default)Configure custom mount paths or require environment-specific files:
builder.ConfigureAppConfiguration((context, config) =>
{
config.AddMountConfiguration(settings =>
{
settings.FileName = "myconfig"; // File name without extension
settings.ConfigMountPath = "configuration"; // Default: "config"
settings.HostingName = "Production"; // Override environment detection
settings.Optional = false; // Require environment file
});
});
Require environment-specific file:
builder.ConfigureAppConfiguration((context, config) =>
{
// Throws exception if appsettings.Production.json doesn't exist
config.AddMountConfiguration(context.HostingEnvironment.GetSettingsProviderForce());
});
The key feature: organize configuration into separate files by concern, each loaded conditionally based on environment:
builder.ConfigureAppConfiguration((context, config) =>
{
var provider = context.HostingEnvironment.GetSettingsProvider();
// Each file name is loaded as: {name}.json + {name}.{Environment}.json
config.AddMountConfiguration(provider, "appsettings"); // App settings
config.AddMountConfiguration(provider, "database"); // Database config
config.AddMountConfiguration(provider, "logging"); // Logging config
config.AddMountConfiguration(provider, "services"); // External services
});
File structure:
config/
├── appsettings.json
├── appsettings.Production.json
├── database.json
├── database.Production.json
├── logging.json
├── logging.Production.json
├── services.json
└── services.Production.json
Benefits:
Mount configuration files at runtime to separate concerns:
Dockerfile:
FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /app
COPY bin/Release/net8.0/publish .
# Configuration will be mounted at runtime
ENTRYPOINT ["dotnet", "YourApp.dll"]
docker-compose.yml:
services:
myapp:
image: myapp:latest
volumes:
- ./config:/app/config:ro # Mount config folder
environment:
- ASPNETCORE_ENVIRONMENT=Production
config/ directory:
config/
├── database.json # Base database config
├── database.Production.json # Production overrides
├── logging.json # Base logging config
└── logging.Production.json # Production logging settings
Use ConfigMaps for different configuration concerns:
database-config.yaml:
apiVersion: v1
kind: ConfigMap
metadata:
name: database-config
data:
database.json: |
{
"ConnectionStrings": {
"Default": "Server=db;Database=myapp;..."
}
}
logging-config.yaml:
apiVersion: v1
kind: ConfigMap
metadata:
name: logging-config
data:
logging.json: |
{
"Logging": {
"LogLevel": {
"Default": "Information"
}
}
}
deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
template:
spec:
containers:
- name: myapp
image: myapp:latest
volumeMounts:
- name: database-config
mountPath: /app/config/database.json
subPath: database.json
- name: logging-config
mountPath: /app/config/logging.json
subPath: logging.json
volumes:
- name: database-config
configMap:
name: database-config
- name: logging-config
configMap:
name: logging-config
Register configuration services:
// Register settings provider
builder.Services.AddVoyagerConfiguration();
// Register custom settings provider
builder.Services.AddVoyagerConfiguration<MyCustomSettingsProvider>();
// Use in your services
public class MyService
{
private readonly ISettingsProvider _settingsProvider;
public MyService(ISettingsProvider settingsProvider)
{
_settingsProvider = settingsProvider;
}
}
Implement ISettingsProvider for custom behavior:
public class MySettingsProvider : ISettingsProvider
{
public Settings GetSettings(string filename = "appsettings")
{
return new Settings
{
FileName = filename,
ConfigMountPath = "/custom/path",
HostingName = "Production"
};
}
}
// Register in DI
builder.Services.AddVoyagerConfiguration<MySettingsProvider>();
This feature is deprecated and will be removed in version 3.0. Please migrate to external secret management tools.
Built-in encryption (deprecated):
builder.ConfigureAppConfiguration((context, config) =>
{
var provider = context.HostingEnvironment.GetSettingsProvider();
var encryptionKey = Environment.GetEnvironmentVariable("ENCRYPTION_KEY");
// Regular files (plain text)
config.AddMountConfiguration(provider, "logging");
config.AddMountConfiguration(provider, "appsettings");
// ⚠️ DEPRECATED - Use SOPS instead
config.AddEncryptedMountConfiguration(encryptionKey, provider, "secrets");
config.AddEncryptedMountConfiguration(encryptionKey, provider, "connectionstrings");
});
Recommended alternative: Mozilla SOPS
Encrypt configuration files using SOPS before loading:
# Encrypt files with SOPS (one-time setup)
sops -e config/secrets.json > config/secrets.json
# Decrypt in your deployment script or init container
sops -d /config-encrypted/secrets.json > /config/secrets.json
// Load decrypted files normally - no code changes needed!
builder.ConfigureAppConfiguration((context, config) =>
{
var provider = context.HostingEnvironment.GetSettingsProvider();
config.AddMountConfiguration(provider, "logging");
config.AddMountConfiguration(provider, "secrets"); // Already decrypted by SOPS
config.AddMountConfiguration(provider, "appsettings");
});
Why migrate to SOPS?
Built-in encryption is deprecated and will be removed in version 3.0.
Instead of built-in encryption, use industry-standard tools:
Mozilla SOPS - File encryption for GitOps workflows
Kubernetes Secrets + Sealed Secrets
Cloud Secret Managers
dotnet user-secrets
See ADR-003 for detailed migration guide.
:ro)This library follows SOLID principles and modern .NET design patterns:
IEncryptor, ISettingsProvider, ICipherProvider, IEncryptorFactoryThe library provides extension methods organized by their specific concerns:
ConfigurationExtension: Non-encrypted mount configurationEncryptedMountConfigurationExtensions: Encrypted mount configuration (high-level API)EncryptedJsonFileExtensions: Encrypted JSON file operations (low-level API)ServiceCollectionExtensions: Dependency injection registrationAll extension methods are placed in the Microsoft.Extensions.DependencyInjection namespace following .NET conventions.
Note: The
ConfigurationEncryptedExtensionclass is deprecated and will be removed in version 3.0. UseEncryptedMountConfigurationExtensionsandEncryptedJsonFileExtensionsinstead.
For migration from version 1.x to 2.x, see Migration Guide.
Contributions are welcome! Please feel free to submit a Pull Request.
git checkout -b feature/amazing-feature)git commit -m 'Add some amazing feature')git push origin feature/amazing-feature)This project is licensed under the MIT License - see the LICENSE file for details.
See also the list of contributors who participated in this project.
If you encounter any issues or have suggestions: