Simplify your .NET configuration. FriendlyEnvars maps environment variables directly to strongly-typed options classes - no JSON files, no complex setup. Just define your settings class and you're done. Fully compatible with IOptions, naturally integrates with existing codebase.
$ dotnet add package FriendlyEnvarsSimple, type-safe environment variable configuration for .NET
Do you need to configure your .NET app purely via environment variables?
FriendlyEnvars lets you bind them directly to strongly typed configuration classes.
[Envar] attribute.IOptions<T> pattern.Ideal for: cloud-native apps, containerized deployments, microservices, or anywhere configuration comes from the environment.
[Required], [Range], etc. automatically.IOptions: Smooth experience for modern .NET dependency injection patterns.Annotate properties you want loaded from environment variables. Properties must have a setter (set or init):
public record DatabaseSettings
{
[Envar("DB_HOST")]
public string Host { get; init; } = string.Empty;
[Envar("DB_PORT")]
public int Port { get; init; }
[Envar("DB_SSL_ENABLED")]
public bool SslEnabled { get; init; } = true;
[Envar("DB_CONNECTION_TIMEOUT")]
public TimeSpan ConnectionTimeout { get; init; } = TimeSpan.FromSeconds(30);
}
Hook up configuration binding in your Startup.cs or DI setup:
services
.AddOptions<DatabaseSettings>()
.BindEnvars();
Validate environment variables using standard data annotation attributes:
services
.AddOptions<DatabaseSettings>()
.BindEnvars()
.ValidateDataAnnotations()
.ValidateOnStart();
public class MyService
{
public MyService(IOptions<DatabaseSettings> settings)
{
var dbSettings = settings.Value;
}
}
Supported Types:
string, char, boolbyte, sbyte, short, ushort, int, uint, long, ulong, float, double, decimalGuid, Uri, TimeSpan, DateTime, DateTimeOffset, DateOnly, TimeOnlyEnum (case-insensitive, including [Flags] enums)TypeConverterAdditional Features:
IEnvarPropertyBinder interface.DataAnnotations attributes.By default, conversions use CultureInfo.InvariantCulture for predictable parsing. To handle locale-specific formats:
using System.Globalization;
services.AddOptions<DatabaseSettings>()
.BindEnvars(settings => {
settings.UseCulture(CultureInfo.GetCultureInfo("en-US"));
});
For complex types, implement IEnvarPropertyBinder to control parsing:
using System;
using System.Collections.Generic;
using System.Globalization;
public record ConnectionString
{
public string Host { get; init; } = string.Empty;
public int Port { get; init; }
public string User { get; init; } = string.Empty;
public string Password { get; init; } = string.Empty;
}
public class CustomEnvarPropertyBinder : IEnvarPropertyBinder
{
private readonly DefaultEnvarPropertyBinder _defaultBinder = new();
public object? Convert(string value, Type targetType, CultureInfo culture)
{
if (targetType == typeof(ConnectionString))
{
return ParseConnectionString(value);
}
return _defaultBinder.Convert(value, targetType, culture);
}
private static ConnectionString ParseConnectionString(string connectionString)
{
var pairs = connectionString.Split(';', StringSplitOptions.RemoveEmptyEntries);
var values = new Dictionary<string, string>();
foreach (var pair in pairs)
{
var parts = pair.Split('=', 2);
if (parts.Length == 2)
{
values[parts[0].Trim()] = parts[1].Trim();
}
}
return new ConnectionString
{
Host = values.GetValueOrDefault("Host", "localhost"),
Port = int.Parse(values.GetValueOrDefault("Port", "5432")),
User = values.GetValueOrDefault("User", ""),
Password = values.GetValueOrDefault("Password", "")
};
}
}
Usage with environment variable CONNECTION_STRING=Host=localhost;Port=5432;User=Joe;Password=Joe12:
public record DatabaseSettings
{
[Envar("CONNECTION_STRING")]
public ConnectionString Connection { get; init; } = new();
}
Then, configure the binder:
services.AddOptions<DatabaseSettings>()
.BindEnvars(settings =>
{
settings.UseCustomEnvarPropertyBinder(new CustomEnvarPropertyBinder());
});
IOptionsSnapshot and IOptionsMonitorBy default, IOptionsSnapshot<T> and IOptionsMonitor<T> are enabled and will always reflect the values from app startup.
Environment variable configs do not refresh at runtime.
You can disable support for IOptionsSnapshot<T> and IOptionsMonitor<T> if you want to ensure only IOptions<T> is used:
services.AddOptions<DatabaseSettings>()
.BindEnvars(settings =>
{
settings
.BlockOptionsSnapshot()
.BlockOptionsMonitor();
});
Previously, environment variables set to an empty string ("") were treated the same as unset variables — the property would retain its default value. Now, empty strings are passed to the binder. This means:
string properties will be set to "" instead of keeping their default.int, bool) will throw a FormatException wrapped in EnvarsException if the environment variable is empty.If you relied on the old behavior of ignoring empty values, either unset the variable entirely or use a custom IEnvarPropertyBinder to handle empty strings.
IOptionsSnapshot and IOptionsMonitor are enabled by default as read-only views, but can be disabled if needed.With FriendlyEnvars, configuration is easy, explicit, and safe.